Commit 01799a55c0794bc2c127a29747e09cda10a915e1

Authored by Yarik
0 parents

first commit

behaviors/ArtBoxAccessBehavior.php 0 → 100755
  1 +++ a/behaviors/ArtBoxAccessBehavior.php
  1 +<?php
  2 +
  3 + namespace common\behaviors;
  4 +
  5 + use Yii;
  6 + use yii\base\Action;
  7 + use yii\base\Event;
  8 + use yii\behaviors\AttributeBehavior;
  9 + use yii\di\Instance;
  10 + use yii\base\Module;
  11 + use yii\filters\AccessRule;
  12 + use yii\web\Request;
  13 + use yii\web\User;
  14 + use yii\web\ForbiddenHttpException;
  15 +
  16 + class ArtBoxAccessBehavior extends AttributeBehavior
  17 + {
  18 +
  19 + public $rules = [];
  20 +
  21 + /**
  22 + * @var AccessRule[] $ruleList
  23 + */
  24 + private $ruleList = [];
  25 +
  26 + public function events()
  27 + {
  28 + return [
  29 + Module::EVENT_BEFORE_ACTION => 'interception',
  30 + ];
  31 + }
  32 +
  33 + /**
  34 + * Check whether current user have access to current action.
  35 + *
  36 + * @param Event $event
  37 + *
  38 + * @return void
  39 + * @throws \yii\web\ForbiddenHttpException
  40 + */
  41 + public function interception($event)
  42 + {
  43 + if (!isset( Yii::$app->i18n->translations[ 'db_rbac' ] )) {
  44 + Yii::$app->i18n->translations[ 'db_rbac' ] = [
  45 + 'class' => 'yii\i18n\PhpMessageSource',
  46 + 'sourceLanguage' => 'ru-Ru',
  47 + 'basePath' => '@developeruz/db_rbac/messages',
  48 + ];
  49 + }
  50 +
  51 + $route = Yii::$app->getRequest()
  52 + ->resolve();
  53 + //Проверяем права по конфигу
  54 + $this->createRule();
  55 + $user = Instance::ensure(Yii::$app->user, User::className());
  56 + $request = Yii::$app->getRequest();
  57 + $action = $event->action;
  58 +
  59 + if (!$this->cheсkByRule($action, $user, $request)) {
  60 + //И по AuthManager
  61 + if (!$this->checkPermission($route)) {
  62 + if ($user->getIsGuest()) {
  63 + $user->loginRequired();
  64 + } else {
  65 + throw new ForbiddenHttpException(Yii::t('db_rbac', 'Недостаточно прав'));
  66 + }
  67 + }
  68 +
  69 + }
  70 + }
  71 +
  72 + /**
  73 + * Fill $ruleList with AccessRules
  74 + *
  75 + * @return void
  76 + */
  77 + protected function createRule()
  78 + {
  79 + foreach ($this->rules as $controller => $rule) {
  80 + foreach ($rule as $singleRule) {
  81 + if (is_array($singleRule)) {
  82 + $option = [
  83 + 'controllers' => [ $controller ],
  84 + 'class' => 'yii\filters\AccessRule',
  85 + ];
  86 + $this->ruleList[] = Yii::createObject(array_merge($option, $singleRule));
  87 + }
  88 + }
  89 + }
  90 + }
  91 +
  92 + /**
  93 + * Check whether the User allowed to perform action
  94 + *
  95 + * @param Action $action
  96 + * @param User $user
  97 + * @param Request $request
  98 + *
  99 + * @return bool
  100 + */
  101 + protected function cheсkByRule($action, $user, $request)
  102 + {
  103 +
  104 + foreach ($this->ruleList as $rule) {
  105 +
  106 + if ($rule->allows($action, $user, $request)) {
  107 + return true;
  108 + }
  109 + }
  110 + return false;
  111 + }
  112 +
  113 + /**
  114 + * Check whether the User have permission for current operation
  115 + *
  116 + * @param array $route
  117 + *
  118 + * @return bool
  119 + */
  120 + protected function checkPermission($route)
  121 + {
  122 + //$route[0] - is the route, $route[1] - is the associated parameters
  123 + $routePathTmp = explode('/', $route[ 0 ]);
  124 + $routeVariant = array_shift($routePathTmp);
  125 + if (Yii::$app->user->can($routeVariant, $route[ 1 ])) {
  126 + return true;
  127 + }
  128 + /**
  129 + * @var string $routePart
  130 + */
  131 + foreach ($routePathTmp as $routePart) {
  132 + $routeVariant .= '/' . $routePart;
  133 + if (Yii::$app->user->can($routeVariant, $route[ 1 ])) {
  134 + return true;
  135 + }
  136 + }
  137 + return false;
  138 + }
  139 +
  140 + }
  141 +
0 142 \ No newline at end of file
... ...
behaviors/ImageBehavior.php 0 → 100755
  1 +++ a/behaviors/ImageBehavior.php
  1 +<?php
  2 +
  3 + namespace common\behaviors;
  4 +
  5 + use yii\base\Behavior;
  6 + use yii\base\Event;
  7 + use yii\db\ActiveRecord;
  8 +
  9 + /**
  10 + * Class ImageBehavior
  11 + * @package common\behaviors
  12 + */
  13 + class ImageBehavior extends Behavior
  14 + {
  15 +
  16 + /**
  17 + * @var string column where file name is stored
  18 + */
  19 + public $link;
  20 +
  21 + /**
  22 + * @var string directory name
  23 + */
  24 + public $directory;
  25 +
  26 + /**
  27 + * @inheritdoc
  28 + */
  29 + public function events()
  30 + {
  31 + return [
  32 + ActiveRecord::EVENT_BEFORE_DELETE => 'beforeDelete',
  33 + ];
  34 + }
  35 +
  36 + /**
  37 + * @param Event $event
  38 + */
  39 + public function beforeDelete($event)
  40 + {
  41 + $file = $this->getImageFile();
  42 + if(file_exists($file)) {
  43 + unlink($file);
  44 + }
  45 + }
  46 +
  47 + /**
  48 + * Get image file path
  49 + *
  50 + * @return null|string
  51 + */
  52 + public function getImageFile()
  53 + {
  54 + $link = $this->link;
  55 + return empty( $this->owner->$link ) ? NULL : \Yii::getAlias('@storage/' . $this->directory . '/' . $this->owner->$link);
  56 + }
  57 +
  58 + /**
  59 + * Get image file url
  60 + *
  61 + * @return null|string
  62 + */
  63 + public function getImageUrl()
  64 + {
  65 + $link = $this->link;
  66 + return empty( $this->owner->$link ) ? NULL : '/storage/' . $this->directory . '/' . $this->owner->$link;
  67 + }
  68 + }
0 69 \ No newline at end of file
... ...
behaviors/MultipleImgBehavior.php 0 → 100755
  1 +++ a/behaviors/MultipleImgBehavior.php
  1 +<?php
  2 +
  3 + namespace common\behaviors;
  4 +
  5 + use common\components\artboximage\ArtboxImageHelper;
  6 + use yii\base\Behavior;
  7 + use yii\db\ActiveRecord;
  8 + use yii\helpers\Url;
  9 +
  10 + /**
  11 + * Class MultipleImgBehavior
  12 + * @todo Write validation
  13 + * @property ActiveRecord $owner
  14 + * @property ActiveRecord $image
  15 + * @property ActiveRecord[] $images
  16 + * @package common\behaviors
  17 + */
  18 + class MultipleImgBehavior extends Behavior
  19 + {
  20 +
  21 + /**
  22 + * key - $model foreign key, value - $owner link key (usual ID)
  23 + * @var array
  24 + */
  25 + public $links = [];
  26 +
  27 + /**
  28 + * Will be passed to get image and get images queries
  29 + *
  30 + * @var array
  31 + */
  32 + public $conditions = [];
  33 +
  34 + /**
  35 + * Full namespaced image model
  36 + * @var string
  37 + */
  38 + public $model;
  39 +
  40 + /**
  41 + * Image config array:
  42 + * 'caption' - $model caption attribute
  43 + * 'delete_action' - url to image delete action, will be passed as first argument to
  44 + * Url::to();
  45 + * 'id' - $model primary key
  46 + * @var array
  47 + */
  48 + public $config = [];
  49 +
  50 + /**
  51 + * One image query
  52 + *
  53 + * @return \yii\db\ActiveQuery
  54 + */
  55 + public function getImage()
  56 + {
  57 + /**
  58 + * @var ActiveRecord $owner
  59 + */
  60 + $owner = $this->owner;
  61 + $query = $owner->hasOne($this->model, $this->links);
  62 + $conditions = $this->conditions;
  63 + foreach($conditions as $condition) {
  64 + $query->andWhere($condition);
  65 + }
  66 + return $query;
  67 + }
  68 +
  69 + /**
  70 + * All images query
  71 + *
  72 + * @return \yii\db\ActiveQuery
  73 + */
  74 + public function getImages()
  75 + {
  76 + /**
  77 + * @var ActiveRecord $owner
  78 + */
  79 + $owner = $this->owner;
  80 + $query = $owner->hasMany($this->model, $this->links);
  81 + $conditions = $this->conditions;
  82 + foreach($conditions as $left => $right) {
  83 + $query->andWhere([$left => $right]);
  84 + }
  85 + return $query;
  86 + }
  87 +
  88 + /**
  89 + * Get images config array for FileInput
  90 + *
  91 + * @return array
  92 + */
  93 + public function getImagesConfig()
  94 + {
  95 + $op = [];
  96 + $images = $this->getImages()->all();
  97 + $config = $this->config;
  98 + if(!isset( $config[ 'id' ] )) {
  99 + return $op;
  100 + }
  101 + foreach($images as $image) {
  102 + $op[] = [
  103 + 'caption' => ( isset( $config[ 'caption' ] ) ) ? $image->{$config[ 'caption' ]} : '',
  104 + 'url' => ( isset( $config[ 'delete_action' ] ) ) ? Url::to([
  105 + $config[ 'delete_action' ],
  106 + 'id' => $image->{$config[ 'id' ]},
  107 + ]) : '',
  108 + 'key' => $image->{$config[ 'id' ]},
  109 + 'extra' => [
  110 + 'id' => $image->{$config[ 'id' ]},
  111 + ],
  112 + ];
  113 + }
  114 + return $op;
  115 + }
  116 +
  117 + /**
  118 + * Get images HTML
  119 + *
  120 + * @param string $preset
  121 + *
  122 + * @return array
  123 + */
  124 + public function getImagesHTML($preset = 'admin_thumb')
  125 + {
  126 + $op = [];
  127 + $images = $this->getImages()->all();
  128 + foreach($images as $image) {
  129 + $op[] = ArtboxImageHelper::getImage($image->imageUrl, $preset);
  130 + }
  131 + return $op;
  132 + }
  133 +
  134 + public function getImageUrl()
  135 + {
  136 + $image = $this->getImage()->one();
  137 + if(!empty($image)) {
  138 + return $image->getImageUrl();
  139 + } else {
  140 + return NULL;
  141 + }
  142 + }
  143 + }
0 144 \ No newline at end of file
... ...
behaviors/SaveImgBehavior.php 0 → 100755
  1 +++ a/behaviors/SaveImgBehavior.php
  1 +<?php
  2 +
  3 + namespace common\behaviors;
  4 +
  5 + use yii\base\Behavior;
  6 + use yii\base\ModelEvent;
  7 + use yii\db\ActiveRecord;
  8 + use yii\web\UploadedFile;
  9 +
  10 + /**
  11 + * Class Save Image Behavior
  12 + * @property ActiveRecord $owner
  13 + * @package common\behaviors
  14 + */
  15 + class SaveImgBehavior extends Behavior
  16 + {
  17 +
  18 + public $fields;
  19 +
  20 + public $isLanguage = false;
  21 +
  22 + public function events()
  23 + {
  24 + return [
  25 + ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeSave',
  26 + ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave',
  27 + ];
  28 + }
  29 +
  30 + /**
  31 + * @param ModelEvent $event
  32 + */
  33 + public function beforeSave($event)
  34 + {
  35 + foreach($this->fields as $field) {
  36 + $field_name = $field[ 'name' ];
  37 + $name = $field_name;
  38 + if($this->isLanguage) {
  39 + $name = '[' . $this->owner->language_id . ']' . $name;
  40 + }
  41 +
  42 + $image = UploadedFile::getInstance($this->owner, $name);
  43 +
  44 + if(empty( $image ) && $event->name == ActiveRecord::EVENT_BEFORE_UPDATE) {
  45 + $this->owner->$field_name = $this->owner->getOldAttribute($field_name);
  46 + } elseif(!empty( $image )) {
  47 + $imgDir = \Yii::getAlias('@storage/' . $field[ 'directory' ] . '/');
  48 +
  49 + if(!is_dir($imgDir)) {
  50 + mkdir($imgDir, 0755, true);
  51 + }
  52 +
  53 + $baseName = $image->baseName;
  54 +
  55 + $iteration = 0;
  56 + $file_name = $imgDir . $baseName . '.' . $image->extension;
  57 + while(file_exists($file_name)) {
  58 + $baseName = $image->baseName . '_' . ++$iteration;
  59 + $file_name = $imgDir . $baseName . '.' . $image->extension;
  60 + }
  61 + unset( $iteration );
  62 +
  63 + $this->owner->$field_name = $baseName . '.' . $image->extension;
  64 +
  65 + $image->saveAs($file_name);
  66 + }
  67 + }
  68 + }
  69 +
  70 + /**
  71 + * @param int $field
  72 + *
  73 + * @return null|string
  74 + */
  75 + public function getImageFile($field = 0)
  76 + {
  77 + $fieldset = $this->fields[ $field ];
  78 + $name = $fieldset[ 'name' ];
  79 + $directory = $fieldset[ 'directory' ];
  80 + return empty( $this->owner->$name ) ? NULL : '/storage/' . $directory . '/' . $this->owner->$name;
  81 + }
  82 +
  83 + /**
  84 + * @param int $field
  85 + *
  86 + * @return null|string
  87 + */
  88 + public function getImageUrl($field = 0)
  89 + {
  90 + $fieldset = $this->fields[ $field ];
  91 + $name = $fieldset[ 'name' ];
  92 + $directory = $fieldset[ 'directory' ];
  93 + return empty( $this->owner->$name ) ? NULL : '/storage/' . $directory . '/' . $this->owner->$name;
  94 + }
  95 + }
0 96 \ No newline at end of file
... ...
behaviors/SaveMultipleFileBehavior.php 0 → 100755
  1 +++ a/behaviors/SaveMultipleFileBehavior.php
  1 +<?php
  2 +
  3 + namespace common\behaviors;
  4 +
  5 + use yii\base\Behavior;
  6 + use yii\base\Event;
  7 + use yii\db\ActiveRecord;
  8 + use yii\web\UploadedFile;
  9 +
  10 + /**
  11 + * Class SaveMultipleFileBehavior
  12 + *
  13 + * @todo Write validators
  14 + *
  15 + * @package common\behaviors
  16 + */
  17 + class SaveMultipleFileBehavior extends Behavior
  18 + {
  19 +
  20 + /**
  21 + * $owner attribute to write files
  22 + * @var string
  23 + */
  24 + public $name;
  25 +
  26 + /**
  27 + * Directory to store files
  28 + * @var string
  29 + */
  30 + public $directory;
  31 +
  32 + /**
  33 + * Column in $model where to store filename
  34 + * @var string
  35 + */
  36 + public $column;
  37 +
  38 + /**
  39 + * key - owner link attribute (usual ID), value - $model link attribute
  40 + * @var array
  41 + */
  42 + public $links = [];
  43 +
  44 + /**
  45 + * Full namespaced classname of file table
  46 + * @var string
  47 + */
  48 + public $model;
  49 +
  50 + public function events()
  51 + {
  52 + return [
  53 + ActiveRecord::EVENT_AFTER_UPDATE => 'downloadFiles',
  54 + ActiveRecord::EVENT_AFTER_INSERT => 'downloadFiles',
  55 + ];
  56 + }
  57 +
  58 + /**
  59 + * Save files to file table
  60 + *
  61 + * @param Event $event
  62 + */
  63 + public function downloadFiles($event)
  64 + {
  65 + /**
  66 + * @var ActiveRecord $owner
  67 + */
  68 + $owner = $this->owner;
  69 + $name = $this->name;
  70 + $owner->$name = UploadedFile::getInstances($owner, $name);
  71 + if(!empty( $files = $this->filesUpload() )) {
  72 + $model = $this->model;
  73 + $links = $this->links;
  74 + $column = $this->column;
  75 + foreach($files as $file) {
  76 + /**
  77 + * @var ActiveRecord $fileModel
  78 + */
  79 + $fileModel = new $model();
  80 + foreach($links as $link_owner => $link) {
  81 + $fileModel->$link = $owner->$link_owner;
  82 + }
  83 + $fileModel->$column = $file;
  84 + $fileModel->save();
  85 + }
  86 + }
  87 + $this->detach();
  88 + }
  89 +
  90 + /**
  91 + * Save files to file system
  92 + *
  93 + * @return array
  94 + */
  95 + private function filesUpload()
  96 + {
  97 + $owner = $this->owner;
  98 + $name = $this->name;
  99 + $directory = $this->directory;
  100 + $fileDir = \Yii::getAlias('@storage/' . $directory . '/');
  101 + if(!is_dir($fileDir)) {
  102 + mkdir($fileDir, 0755, true);
  103 + }
  104 + $files = [];
  105 + /**
  106 + * @var UploadedFile $file
  107 + */
  108 + foreach($owner->$name as $file) {
  109 + $fileName = $file->baseName . '.' . $file->extension;
  110 + $i = 0;
  111 + while(file_exists(\Yii::getAlias($fileDir . $fileName))) {
  112 + $fileName = $file->baseName . '_' . ++$i . '.' . $file->extension;
  113 + }
  114 + $file->saveAs($fileDir . $fileName);
  115 + $files[] = $fileName;
  116 + }
  117 + return $files;
  118 + }
  119 + }
0 120 \ No newline at end of file
... ...
behaviors/Slug.php 0 → 100755
  1 +++ a/behaviors/Slug.php
  1 +<?php
  2 +
  3 + namespace common\behaviors;
  4 +
  5 + use yii;
  6 + use yii\base\Behavior;
  7 + use yii\db\ActiveRecord;
  8 + use dosamigos\transliterator\TransliteratorHelper;
  9 +
  10 + class Slug extends Behavior
  11 + {
  12 +
  13 + /**
  14 + * @var string
  15 + */
  16 + public $inAttribute = 'title';
  17 +
  18 + /**
  19 + * @var string
  20 + */
  21 + public $outAttribute = 'alias';
  22 +
  23 + /**
  24 + * @var bool
  25 + */
  26 + public $translit = true;
  27 +
  28 + /**
  29 + * @inheritdoc
  30 + */
  31 + public function events()
  32 + {
  33 + return [
  34 + ActiveRecord::EVENT_BEFORE_INSERT => 'getSlug',
  35 + ActiveRecord::EVENT_BEFORE_UPDATE => 'getSlug',
  36 + ];
  37 + }
  38 +
  39 + /**
  40 + * Generate slug
  41 + *
  42 + * @param yii\base\Event $event
  43 + *
  44 + * @return void
  45 + */
  46 + public function getSlug($event)
  47 + {
  48 + if(!empty( $this->owner->{$this->inAttribute} )) {
  49 + if(empty( $this->owner->{$this->outAttribute} )) {
  50 + $this->owner->{$this->outAttribute} = $this->generateSlug($this->owner->{$this->inAttribute});
  51 + } else {
  52 + $this->owner->{$this->outAttribute} = $this->generateSlug($this->owner->{$this->outAttribute});
  53 + }
  54 + }
  55 + }
  56 +
  57 + /**
  58 + * @param string $slug
  59 + *
  60 + * @return string
  61 + */
  62 + private function generateSlug($slug)
  63 + {
  64 + $slug = $this->slugify($slug);
  65 + if($this->checkUniqueSlug($slug)) {
  66 + return $slug;
  67 + } else {
  68 + for($suffix = 2; !$this->checkUniqueSlug($new_slug = $slug . '-' . $suffix); $suffix++) {
  69 + }
  70 + return $new_slug;
  71 + }
  72 + }
  73 +
  74 + /**
  75 + * @param string $slug
  76 + *
  77 + * @return string
  78 + */
  79 + private function slugify($slug)
  80 + {
  81 + if($this->translit) {
  82 + return yii\helpers\Inflector::slug( $this->translit( $slug ), '-', true );
  83 + } else {
  84 + return $this->slug($slug, '-', true);
  85 + }
  86 + }
  87 +
  88 + /**
  89 + * @param string $string
  90 + * @param string $replacement
  91 + * @param bool $lowercase
  92 + *
  93 + * @return string
  94 + */
  95 + private function slug($string, $replacement = '-', $lowercase = true)
  96 + {
  97 + $string = preg_replace('/[^\p{L}\p{Nd}]+/u', $replacement, $string);
  98 + $string = trim($string, $replacement);
  99 + return $lowercase ? strtolower($string) : $string;
  100 + }
  101 +
  102 + /**
  103 + * Check whether current slug is unique
  104 + *
  105 + * @param string $slug
  106 + *
  107 + * @return bool
  108 + */
  109 + private function checkUniqueSlug($slug)
  110 + {
  111 + /**
  112 + * @var ActiveRecord $owner
  113 + */
  114 + $owner = $this->owner;
  115 + $query = $owner->find()
  116 + ->where([
  117 + $this->outAttribute => $slug,
  118 + ]);
  119 + if(!$owner->isNewRecord) {
  120 + $pks = $owner->primaryKey();
  121 + if(!empty( $pks )) {
  122 + $pk_rules = [ 'and' ];
  123 + foreach($pks as $pk) {
  124 + $pk_rules[] = [ $pk => $owner->$pk ];
  125 + }
  126 + $query->andWhere([
  127 + 'not',
  128 + $pk_rules,
  129 + ]);
  130 + }
  131 + }
  132 + return !$query->exists();
  133 + }
  134 +
  135 + static function translit ($string, $setting = 'all')
  136 + {
  137 + $letter = array (
  138 +
  139 + 'а' => 'a', 'б' => 'b', 'в' => 'v',
  140 + 'г' => 'g', 'д' => 'd', 'е' => 'e',
  141 + 'ё' => 'e', 'ж' => 'zh', 'з' => 'z',
  142 + 'и' => 'i', 'й' => 'y', 'к' => 'k',
  143 + 'л' => 'l', 'м' => 'm', 'н' => 'n',
  144 + 'о' => 'o', 'п' => 'p', 'р' => 'r',
  145 + 'с' => 's', 'т' => 't', 'у' => 'u',
  146 + 'ф' => 'f', 'х' => 'h', 'ц' => 'c',
  147 + 'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sch',
  148 + 'ь' => "", 'ы' => 'y', 'ъ' => "",
  149 + 'э' => 'e', 'ю' => 'yu', 'я' => 'ya',
  150 + 'ї' => 'yi', 'є' => 'ye', 'і' => 'ee',
  151 +
  152 + 'А' => 'A', 'Б' => 'B', 'В' => 'V',
  153 + 'Г' => 'G', 'Д' => 'D', 'Е' => 'E',
  154 + 'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z',
  155 + 'И' => 'I', 'Й' => 'Y', 'К' => 'K',
  156 + 'Л' => 'L', 'М' => 'M', 'Н' => 'N',
  157 + 'О' => 'O', 'П' => 'P', 'Р' => 'R',
  158 + 'С' => 'S', 'Т' => 'T', 'У' => 'U',
  159 + 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C',
  160 + 'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch',
  161 + 'Ь' => "", 'Ы' => 'Y', 'Ъ' => "",
  162 + 'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya',
  163 + 'Ї' => 'Yi', 'Є' => 'Ye', 'І' => 'Ee'
  164 + );
  165 +
  166 + $symbol = array (
  167 + ' ' => '-', "'" => '', '"' => '',
  168 + '!' => '', "@" => '', '#' => '',
  169 + '$' => '', "%" => '', '^' => '',
  170 + ';' => '', "*" => '', '(' => '',
  171 + ')' => '', "+" => '', '~' => '',
  172 + '.' => '', ',' => '-', '?' => '',
  173 + '…' => '', '№' => 'N', '°' => '',
  174 + '`' => '', '|' => '', '&' => '-and-',
  175 + '<' => '', '>' => ''
  176 + );
  177 +
  178 + if ($setting == 'all')
  179 + {
  180 + $converter = $letter + $symbol;
  181 + }
  182 + else if ($setting == 'letter')
  183 + {
  184 + $converter = $letter;
  185 + }
  186 + else if ($setting == 'symbol')
  187 + {
  188 + $converter = $symbol;
  189 + }
  190 +
  191 + $url = strtr ($string, $converter);
  192 +
  193 + $url = str_replace ("---", '-', $url);
  194 + $url = str_replace ("--", '-', $url);
  195 +
  196 + return $url;
  197 + }
  198 +
  199 + }
  200 +
0 201 \ No newline at end of file
... ...
components/SmsSender.php 0 → 100755
  1 +++ a/components/SmsSender.php
  1 +<?php
  2 +namespace common\components;
  3 +
  4 +use yii\base\Component;
  5 +
  6 +class SmsSender extends Component
  7 +{
  8 +
  9 + public $tel;
  10 + public $text;
  11 +
  12 + public function send($tel, $text)
  13 + {
  14 +
  15 + // $text = iconv('windows-1251', 'utf-8', htmlspecialchars($text));
  16 + $description = iconv('windows-1251', 'utf-8', htmlspecialchars($tel . ' description'));
  17 + $start_time = 'AUTO'; // отправить немедленно или ставим дату и время в формате YYYY-MM-DD HH:MM:SS
  18 + $end_time = 'AUTO'; // автоматически рассчитать системой или ставим дату и время в формате YYYY-MM-DD HH:MM:SS
  19 + $rate = 1; // скорость отправки сообщений (1 = 1 смс минута). Одиночные СМС сообщения отправляются всегда с максимальной скоростью.
  20 + $lifetime = 4; // срок жизни сообщения 4 часа
  21 +
  22 + $source = 'rukzachok'; // Alfaname
  23 + $recipient = $tel;
  24 + $user = '380674064008';
  25 + $password = 'smsartweb2012';
  26 +
  27 + $myXML = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
  28 + $myXML .= "<request>";
  29 + $myXML .= "<operation>SENDSMS</operation>";
  30 + $myXML .= ' <message start_time="' . $start_time . '" end_time="' . $end_time . '" lifetime="' . $lifetime . '" rate="' . $rate . '" desc="' . $description . '" source="' . $source . '">' . "\n";
  31 + $myXML .= " <body>" . $text . "</body>";
  32 + $myXML .= " <recipient>" . $recipient . "</recipient>";
  33 + $myXML .= "</message>";
  34 + $myXML .= "</request>";
  35 +
  36 + $ch = curl_init();
  37 + curl_setopt($ch, CURLOPT_USERPWD, $user . ':' . $password);
  38 + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
  39 + curl_setopt($ch, CURLOPT_POST, 1);
  40 + curl_setopt($ch, CURLOPT_URL, 'http://sms-fly.com/api/api.php');
  41 + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: text/xml", "Accept: text/xml"));
  42 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  43 + curl_setopt($ch, CURLOPT_POSTFIELDS, $myXML);
  44 + $response = curl_exec($ch);
  45 + curl_close($ch);
  46 +
  47 + return $response;
  48 + }
  49 +}
... ...
components/artboximage/ArtboxImage.php 0 → 100755
  1 +++ a/components/artboximage/ArtboxImage.php
  1 +<?php
  2 +
  3 + namespace common\components\artboximage;
  4 +
  5 + use yii\base\Component;
  6 + use yii\base\ErrorException;
  7 + use yii\image\drivers\Image;
  8 +
  9 + class ArtboxImage extends Component
  10 + {
  11 + /**
  12 + * @var string $driver GD, Imagick ...
  13 + */
  14 + public $driver;
  15 +
  16 + public $presets = [];
  17 +
  18 + /**
  19 + * File path to image locations
  20 + *
  21 + * @var string $rootPath
  22 + */
  23 + public $rootPath;
  24 +
  25 + /**
  26 + * Web path to image locations
  27 + *
  28 + * @var string $rootUrl
  29 + */
  30 + public $rootUrl;
  31 +
  32 + public $extensions = [
  33 + 'jpg' => 'jpeg',
  34 + 'jpeg' => 'jpeg',
  35 + 'png' => 'png',
  36 + 'gif' => 'gif',
  37 + 'bmp' => 'bmp',
  38 + ];
  39 +
  40 + /**
  41 + * Try to load image and prepare it to manipulation.
  42 + *
  43 + * @param null|string $file
  44 + * @param null|string $driver
  45 + *
  46 + * @return \yii\image\drivers\Image
  47 + * @throws \yii\base\ErrorException
  48 + */
  49 + public function load($file = null, $driver = null)
  50 + {
  51 + if (empty( $file ) || !realpath($file)) {
  52 + throw new ErrorException('File name can not be empty and exists');
  53 + }
  54 + return Image::factory($file, $driver ? $driver : $this->driver);
  55 + }
  56 + }
0 57 \ No newline at end of file
... ...
components/artboximage/ArtboxImageHelper.php 0 → 100755
  1 +++ a/components/artboximage/ArtboxImageHelper.php
  1 +<?php
  2 +
  3 + namespace common\components\artboximage;
  4 +
  5 + use Yii;
  6 + use yii\base\Object;
  7 + use yii\helpers\Html;
  8 +
  9 + class ArtboxImageHelper extends Object
  10 + {
  11 +
  12 + /**
  13 + * @var ArtboxImage $imageDriver
  14 + */
  15 + private static $imageDriver;
  16 +
  17 + /**
  18 + * @var array $presets
  19 + */
  20 + private static $presets;
  21 +
  22 + /**
  23 + * Get image manipulation driver
  24 + *
  25 + * @return \common\components\artboximage\ArtboxImage
  26 + */
  27 + public static function getDriver()
  28 + {
  29 + if (empty( self::$imageDriver )) {
  30 + self::$imageDriver = Yii::$app->get('artboximage');
  31 + }
  32 + return self::$imageDriver;
  33 + }
  34 +
  35 + /**
  36 + * Get named preset from driver preset list.
  37 + *
  38 + * @param string $preset
  39 + *
  40 + * @return array|null
  41 + */
  42 + public static function getPreset($preset)
  43 + {
  44 + if (empty( self::$presets )) {
  45 + self::$presets = self::getDriver()->presets;
  46 + }
  47 + return empty( self::$presets[ $preset ] ) ? null : self::$presets[ $preset ];
  48 + }
  49 +
  50 + /**
  51 + * Get image HTML for image
  52 + *
  53 + * @param string $file
  54 + * @param array|string $preset
  55 + * @param array $imgOptions
  56 + *
  57 + * @see Html::img()
  58 + * @return string
  59 + */
  60 + public static function getImage($file, $preset, $imgOptions = [])
  61 + {
  62 + $preset_alias = is_array($preset) ? array_keys($preset)[ 0 ] : null;
  63 + return Html::img(self::getImageSrc($file, $preset, $preset_alias), $imgOptions);
  64 + }
  65 +
  66 + /**
  67 + * Get src for image
  68 + *
  69 + * @param string $file
  70 + * @param string $preset
  71 + * @param null|string $preset_alias
  72 + *
  73 + * @return bool|string
  74 + */
  75 + public static function getImageSrc($file, $preset, $preset_alias = null)
  76 + {
  77 + if (is_string($preset)) {
  78 + $preset_alias = $preset;
  79 + $preset = self::getPreset($preset);
  80 + }
  81 + if (empty( $preset ) || empty( $preset_alias )) {
  82 + return $file;
  83 + }
  84 +
  85 + $filePath = self::getPathFromUrl($file);
  86 + if (!file_exists($filePath) || !preg_match(
  87 + '#^(.*)\.(' . self::getExtensionsRegexp() . ')$#',
  88 + $file,
  89 + $matches
  90 + )
  91 + ) {
  92 + return $file;
  93 + }
  94 + return self::getPresetUrl($filePath, $preset, $preset_alias);
  95 + }
  96 +
  97 + /**
  98 + * Replace web path with file path
  99 + *
  100 + * @param string $url
  101 + *
  102 + * @return string
  103 + */
  104 + private static function getPathFromUrl($url)
  105 + {
  106 + return substr_replace($url, self::getDriver()->rootPath, 0, strlen(self::getDriver()->rootUrl));
  107 + }
  108 +
  109 + /**
  110 + * Replace file path with web path
  111 + *
  112 + * @param string $path
  113 + *
  114 + * @return string
  115 + */
  116 + private static function getUrlFromPath($path)
  117 + {
  118 + return substr_replace($path, self::getDriver()->rootUrl, 0, strlen(self::getDriver()->rootPath));
  119 + }
  120 +
  121 + /**
  122 + * Get formatted file url or create it if not exist
  123 + *
  124 + * @param string $filePath
  125 + * @param array $preset
  126 + * @param string $preset_alias
  127 + *
  128 + * @return bool|string
  129 + */
  130 + private static function getPresetUrl($filePath, $preset, $preset_alias)
  131 + {
  132 + $pathinfo = pathinfo($filePath);
  133 + $presetPath = $pathinfo[ 'dirname' ] . '/styles/' . strtolower($preset_alias);
  134 + $presetFilePath = $presetPath . '/' . $pathinfo[ 'basename' ];
  135 + $presetUrl = self::getUrlFromPath($presetFilePath);
  136 + if (file_exists($presetFilePath)) {
  137 + return $presetUrl;
  138 + }
  139 + if (!file_exists($presetPath)) {
  140 + @mkdir($presetPath, 0777, true);
  141 + }
  142 + $output = self::createPresetImage($filePath, $preset, $preset_alias);
  143 + if (!empty( $output )) {
  144 + $f = fopen($presetFilePath, 'w');
  145 + fwrite($f, $output);
  146 + fclose($f);
  147 + return $presetUrl;
  148 + }
  149 + return false;
  150 + }
  151 +
  152 + /**
  153 + * Create formatted image.
  154 + * Available manipulations:
  155 + * * resize
  156 + * * flip
  157 + *
  158 + * @param string $filePath
  159 + * @param array $preset
  160 + * @param string $preset_alias
  161 + *
  162 + * @return string
  163 + */
  164 + private static function createPresetImage($filePath, $preset, $preset_alias)
  165 + {
  166 + $image = self::getDriver()
  167 + ->load($filePath);
  168 + foreach ($preset as $action => $data) {
  169 + switch ($action) {
  170 + case 'resize':
  171 + $width = empty( $data[ 'width' ] ) ? null : $data[ 'width' ];
  172 + $height = empty( $data[ 'height' ] ) ? null : $data[ 'height' ];
  173 + $master = empty( $data[ 'master' ] ) ? null : $data[ 'master' ];
  174 + $image->resize($width, $height, $master);
  175 + break;
  176 + case 'flip':
  177 + $image->flip(@$data[ 'direction' ]);
  178 + break;
  179 + default:
  180 + break;
  181 + }
  182 + }
  183 + return $image->render();
  184 + }
  185 +
  186 + /**
  187 + * Get extensions regexp
  188 + *
  189 + * @return string regexp
  190 + */
  191 + private static function getExtensionsRegexp()
  192 + {
  193 + $keys = array_keys(self::getDriver()->extensions);
  194 + return '(?i)' . join('|', $keys);
  195 + }
  196 + }
0 197 \ No newline at end of file
... ...
components/artboxtree/ArtboxTreeBehavior.php 0 → 100755
  1 +++ a/components/artboxtree/ArtboxTreeBehavior.php
  1 +<?php
  2 +
  3 +namespace common\components\artboxtree;
  4 +
  5 +use yii\base\Behavior;
  6 +use yii\base\Exception;
  7 +use yii\base\NotSupportedException;
  8 +use yii\db\ActiveRecord;
  9 +use yii\db\Expression;
  10 +
  11 +class ArtboxTreeBehavior extends Behavior {
  12 +
  13 + /** @var ActiveRecord $owner */
  14 + public $owner;
  15 +
  16 + public $keyNameId;
  17 + public $keyNameParentId = 'parent_id';
  18 + public $keyNameGroup = 'group';
  19 + public $keyNamePath = 'path_int';
  20 + public $keyNameDepth = 'depth'; // @todo -> $keyNameDepth;
  21 + public $primaryKeyMode = true;
  22 +
  23 + /**
  24 + * @var string
  25 + */
  26 + public $delimiter = '|';
  27 +
  28 + /**
  29 + * @var ActiveRecord|self|null
  30 + */
  31 + protected $entity;
  32 +
  33 + /**
  34 + * @param ActiveRecord $owner
  35 + * @throws Exception
  36 + */
  37 + public function attach($owner)
  38 + {
  39 + parent::attach($owner);
  40 + if ($this->keyNameId === null) {
  41 + $primaryKey = $owner->primaryKey();
  42 + if (!isset($primaryKey[0])) {
  43 + throw new Exception('"' . $owner->className() . '" must have a primary key.');
  44 + }
  45 + $this->keyNameId = $primaryKey[0];
  46 + }
  47 + }
  48 +
  49 + public function events()
  50 + {
  51 + return [
  52 + // @todo Use beforeSave for automatic set MP-params
  53 + ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate',
  54 + ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert',
  55 + ];
  56 + }
  57 +
  58 + /*
  59 + * Main methods
  60 + */
  61 +
  62 + /*
  63 + * get one parent
  64 + * use AL-method
  65 + */
  66 + public function getParent() {
  67 + return $this->getParentAL();
  68 + }
  69 +
  70 + /*
  71 + * get all parents
  72 + * use MP-method
  73 + */
  74 + public function getParents() {
  75 + return $this->getParentsMP();
  76 + }
  77 +
  78 + /*
  79 + * get one-level children items
  80 + * use AL-method
  81 + */
  82 + public function getChildren() {
  83 + return $this->getChildrenAL();
  84 + }
  85 +
  86 + /*
  87 + * get all-level children items
  88 + * use MP-method
  89 + */
  90 + public function getAllChildren($depth = null, $where = [], $with = null) {
  91 + return $this->getAllChildrenMP($depth, $where, $with);
  92 + }
  93 +
  94 + /*
  95 + * get all-level children items
  96 + * use MP-method
  97 + */
  98 + public function getAllChildrenTree($depth = null, $where = [], $with = null) {
  99 + $query = $this->getAllChildrenMP($depth, $where, $with);
  100 + return $this->buildTree($query->all(), $this->owner->getAttribute($this->keyNameId));
  101 + }
  102 +
  103 + // @todo Check algorytm
  104 + public function buildTree(array $data, $parentId = 0) {
  105 + $result = [];
  106 + foreach ($data as $key => $element) {
  107 + if ($element->getAttribute($this->keyNameParentId) == $parentId) {
  108 + unset($data[$key]);
  109 + $children = $this->buildTree($data, $element->getAttribute($this->keyNameId));
  110 + $result[] = [
  111 + 'item' => $element,
  112 + 'children' => $children
  113 + ];
  114 + }
  115 + }
  116 + return $result;
  117 + }
  118 +
  119 +
  120 + /*
  121 + * ================================
  122 + * MP-methods
  123 + * ================================
  124 + */
  125 +
  126 + /*
  127 + * Full-path (use MP-method)
  128 + */
  129 + public function getParentsMP($depth = null) {
  130 + $tableName = $this->owner->tableName();
  131 + $path = $this->owner->getAttribute($this->keyNamePath);
  132 + $query = $this->owner->find()
  133 + ->andWhere(['<@', "{$tableName}.[[{$this->keyNamePath}]]", $path]);
  134 + if ($depth > 0) {
  135 + $query->andWhere(['>=', "{$tableName}.[[{$this->keyNameDepth}]]", $this->owner->getAttribute($this->keyNameDepth) - $depth]);
  136 + }
  137 + $query->andWhere(['<', "{$tableName}.[[{$this->keyNameDepth}]]", $this->owner->getAttribute($this->keyNameDepth)]);
  138 +
  139 + $orderBy = [];
  140 + $orderBy["{$tableName}.[[{$this->keyNameDepth}]]"] = SORT_ASC;
  141 + $orderBy["{$tableName}.[[{$this->keyNameId}]]"] = SORT_ASC;
  142 +
  143 + $query
  144 + ->andWhere($this->groupWhere())
  145 + ->addOrderBy($orderBy);
  146 + $query->multiple = true;
  147 +
  148 + return $query;
  149 + }
  150 + /*public function getParentsMP($depth = null) {
  151 + $path = $this->getParentPath();
  152 + if ($path !== null) {
  153 + $paths = explode(',', trim($path, '{}'));
  154 + if (!$this->primaryKeyMode) {
  155 + $path = null;
  156 + $paths = array_map(
  157 + function ($value) use (&$path) {
  158 + return $path = ($path !== null ? $path . ',' : '') . $value;
  159 + },
  160 + $paths
  161 + );
  162 + }
  163 + if ($depth !== null) {
  164 + $paths = array_slice($paths, -$depth);
  165 + }
  166 + } else {
  167 + $paths = [];
  168 + }
  169 +
  170 + $tableName = $this->owner->tableName();
  171 + if ($this->primaryKeyMode) {
  172 + $condition[] = ["{$tableName}.[[{$this->keyNameId}]]" => $paths];
  173 + } else {
  174 + $condition[] = ["{$tableName}.[[{$this->keyNamePath}]]" => $paths];
  175 + }
  176 +
  177 + $query = $this->owner->find()
  178 + ->andWhere($condition)
  179 + ->andWhere($this->groupWhere())
  180 + ->addOrderBy(["{$tableName}.[[{$this->keyNamePath}]]" => SORT_ASC]);
  181 + $query->multiple = true;
  182 +
  183 + return $query;
  184 + }*/
  185 +
  186 + /**
  187 + * @param bool $asArray = false
  188 + * @return null|string|array
  189 + */
  190 + public function getParentPath($asArray = false)
  191 + {
  192 + return static::getParentPathInternal($this->owner->getAttribute($this->keyNamePath), $asArray);
  193 + }
  194 + /**
  195 + * @return array
  196 + */
  197 + protected function groupWhere()
  198 + {
  199 + $tableName = $this->owner->tableName();
  200 + if ($this->keyNameGroup === null) {
  201 + return [];
  202 + } else {
  203 + return ["{$tableName}.[[{$this->keyNameGroup}]]" => $this->owner->getAttribute($this->keyNameGroup)];
  204 + }
  205 + }
  206 +
  207 +
  208 + public function getAllChildrenMP($depth = null, $where = [], $with = null)
  209 + {
  210 + $tableName = $this->owner->tableName();
  211 + $path = $this->owner->getAttribute($this->keyNamePath);
  212 + $query = $this->owner->find()
  213 + ->andWhere(['@>', "{$tableName}.[[{$this->keyNamePath}]]", $path]);
  214 +
  215 + if ($depth > 0) {
  216 + $query->andWhere(['<=', "{$tableName}.[[{$this->keyNameDepth}]]", $this->owner->getAttribute($this->keyNameDepth) + $depth]);
  217 + }
  218 +
  219 + $orderBy = [];
  220 + $orderBy["{$tableName}.[[{$this->keyNameDepth}]]"] = SORT_ASC;
  221 + $orderBy["{$tableName}.[[{$this->keyNameId}]]"] = SORT_ASC;
  222 +
  223 + if ($where) {
  224 + $query->andWhere($where);
  225 + }
  226 + if ($with) {
  227 + $query->with($with);
  228 + }
  229 +
  230 + $query
  231 + ->andWhere($this->groupWhere())
  232 + ->addOrderBy($orderBy);
  233 + $query->multiple = true;
  234 +
  235 + return $query;
  236 + }
  237 +
  238 + /*
  239 + * ================================
  240 + * AL methods
  241 + * ================================
  242 + */
  243 +
  244 + /*
  245 + * Parent entity (use AL-method)
  246 + * @return \yii\db\ActiveRecord
  247 + */
  248 + public function getParentAL() {
  249 + $parent_id = $this->owner->getAttribute($this->keyNameParentId);
  250 + if (empty($parent_id))
  251 + return null;
  252 +
  253 + $where = [$this->keyNameId => $parent_id];
  254 + if ($this->keyNameGroup) {
  255 + $where[$this->keyNameGroup] = $this->owner->getAttribute($this->keyNameGroup);
  256 + }
  257 +
  258 + return $this->owner->find()->where($where)->one();
  259 + }
  260 +
  261 + /*
  262 + * Get parents by AL-method
  263 + * @return array
  264 + */
  265 + public function getParentsAL() {
  266 + $parent_id = $this->owner->getAttribute($this->keyNameParentId);
  267 + if ($parent_id == 0) {
  268 + return [];
  269 + }
  270 +
  271 + $parent = $this->owner;
  272 + $parents = [];
  273 + while(true) {
  274 + $parent = $parent->getParentAL();
  275 + if (is_null($parent))
  276 + break;
  277 + $parents[] = $parent;
  278 + }
  279 +
  280 + return array_reverse($parents);
  281 + }
  282 +
  283 + /*
  284 + * Children entities (one-step) (use AL-method)
  285 + * @return ActiveQuery
  286 + */
  287 + public function getChildrenAL() {
  288 + $where = [$this->keyNameParentId => $this->owner->getAttribute($this->keyNameId)];
  289 + if ($this->keyNameGroup) {
  290 + $where[$this->keyNameGroup] = $this->owner->getAttribute($this->keyNameGroup);
  291 + }
  292 + return $this->owner->find()->where($where);
  293 + }
  294 +
  295 + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  296 +
  297 + /**
  298 + * @param array $changedAttributes
  299 + * @throws Exception
  300 + */
  301 + protected function rebuildChildren($changedAttributes)
  302 + {
  303 + $path = isset($changedAttributes[$this->keyNamePath]) ? $changedAttributes[$this->keyNamePath] : $this->owner->getAttribute($this->keyNamePath);
  304 + $update = [];
  305 + $condition = [
  306 + 'and',
  307 + ['@>', "[[{$this->keyNamePath}]]", $path, false],
  308 + ];
  309 + if ($this->keyNameGroup !== null) {
  310 + $group = isset($changedAttributes[$this->keyNameGroup]) ? $changedAttributes[$this->keyNameGroup] : $this->owner->getAttribute($this->keyNameGroup);
  311 + $condition[] = [$this->keyNameGroup => $group];
  312 + }
  313 + $params = [];
  314 +
  315 + if (isset($changedAttributes[$this->keyNamePath])) {
  316 + $substringExpr = $this->substringExpression(
  317 + "[[{$this->keyNamePath}]]",
  318 + 'array_length(:pathOld) + 1',
  319 + "array_length([[{$this->keyNamePath}]]) - array_length(:pathOld)"
  320 + );
  321 + $update[$this->keyNamePath] = new Expression($this->concatExpression([':pathNew', $substringExpr]));
  322 + $params[':pathOld'] = $path;
  323 + $params[':pathNew'] = $this->owner->getAttribute($this->keyNamePath);
  324 + }
  325 +
  326 + if ($this->keyNameGroup !== null && isset($changedAttributes[$this->keyNameGroup])) {
  327 + $update[$this->keyNameGroup] = $this->owner->getAttribute($this->keyNameGroup);
  328 + }
  329 +
  330 + if ($this->keyNameDepth !== null && isset($changedAttributes[$this->keyNameDepth])) {
  331 + $delta = $this->owner->getAttribute($this->keyNameDepth) - $changedAttributes[$this->keyNameDepth];
  332 + $update[$this->keyNameDepth] = new Expression("[[{$this->keyNameDepth}]]" . sprintf('%+d', $delta));
  333 + }
  334 + if (!empty($update)) {
  335 + $this->owner->updateAll($update, $condition, $params);
  336 + }
  337 + }
  338 +
  339 + /**
  340 + * @param string $path
  341 + * @param string $delimiter
  342 + * @param bool $asArray = false
  343 + * @return null|string|array
  344 + */
  345 + protected static function getParentPathInternal($path, $asArray = false)
  346 + {
  347 + $path = explode(',', trim($path, '{}'));
  348 + array_pop($path);
  349 + if ($asArray) {
  350 + return $path;
  351 + }
  352 + return count($path) > 0 ? implode(',', $path) : null;
  353 + }
  354 +
  355 + protected function toLike($path) {
  356 + return strtr($path . ',', ['%' => '\%', '_' => '\_', '\\' => '\\\\']) . '%';
  357 + }
  358 +
  359 + protected function concatExpression($items)
  360 + {
  361 + if ($this->owner->getDb()->driverName === 'sqlite' || $this->owner->getDb()->driverName === 'pgsql') {
  362 + return implode(' || ', $items);
  363 + }
  364 + return 'CONCAT(' . implode(',', $items) . ')';
  365 + }
  366 +
  367 + protected function substringExpression($string, $from, $length)
  368 + {
  369 + if ($this->owner->getDb()->driverName === 'sqlite') {
  370 + return "SUBSTR({$string}, {$from}, {$length})";
  371 + }
  372 + return "SUBSTRING({$string}, {$from}, {$length})";
  373 + }
  374 +
  375 + // =======================================================
  376 + public function afterInsert() {
  377 + $this->withSave();
  378 + $this->owner->updateAttributes([$this->keyNamePath => $this->owner->getAttribute($this->keyNamePath), $this->keyNameDepth => $this->owner->getAttribute($this->keyNameDepth)]);
  379 + }
  380 +
  381 + public function beforeUpdate()
  382 + {
  383 + if ($this->owner->getIsNewRecord()) {
  384 + throw new NotSupportedException('Method "' . $this->owner->className() . '::insert" is not supported for inserting new entitys.');
  385 + }
  386 + $this->withSave();
  387 + }
  388 +
  389 + protected function withSave() {
  390 + $id = $this->owner->getAttribute($this->keyNameId);
  391 + $parent_id = $this->owner->getAttribute($this->keyNameParentId);
  392 +
  393 + if (is_null($parent_id)) {
  394 + $parent_id = 0;
  395 + }
  396 +
  397 + // check parent_id value is changed!
  398 + /*if ($this->owner->getOldAttribute($this->keyNameParentId) == $parent_id) {
  399 + return;
  400 + }*/
  401 +
  402 + // rebuild parents entities
  403 + if ($parent_id == 0) {
  404 + $depth = 0;
  405 + $path = [intval($id)];
  406 + } else {
  407 + $parents = $this->getParentsAL();
  408 + $path = [];
  409 + $depth = 0;
  410 + foreach ($parents as $entity) {
  411 + $path[] = $entity->getAttribute($this->keyNameId);
  412 + $depth++;
  413 + }
  414 + $path[] = intval($id);
  415 + }
  416 +
  417 + $path = '{'. implode(',', $path) .'}';
  418 +
  419 + // rebuild children entities (recurcive)
  420 +// $this->rebuildChildren([
  421 +// $this->keyNamePath => $path
  422 +// ]);
  423 +
  424 + $this->owner->setAttribute($this->keyNamePath, $path);
  425 +// $this->owner->setAttribute($this->keyNamePath, $path);
  426 + $this->owner->setAttribute($this->keyNameDepth, $depth);
  427 + }
  428 +
  429 + public function recursiveRebuildChildren() {
  430 + $children = $this->getChildrenAL()->all();
  431 + $root_path = explode(',', $this->owner->getAttribute($this->keyNamePath));
  432 + $root_depth = $this->owner->getAttribute($this->keyNameDepth);
  433 +
  434 + /** @var $child ActiveRecord */
  435 + foreach ($children as $child) {
  436 + $path = $root_path;
  437 + $path[] = $child->getAttribute($this->keyNameId);
  438 + $depth = $root_depth + 1;
  439 +
  440 + $child->recursiveRebuildChildren();
  441 + }
  442 + }
  443 +}
0 444 \ No newline at end of file
... ...
components/artboxtree/ArtboxTreeHelper.php 0 → 100755
  1 +++ a/components/artboxtree/ArtboxTreeHelper.php
  1 +<?php
  2 +
  3 +namespace common\components\artboxtree;
  4 +
  5 +use yii\base\Object;
  6 +use yii\helpers\ArrayHelper;
  7 +
  8 +class ArtboxTreeHelper extends Object {
  9 + public static function treeMap($tree, $from, $to, $symbol = '.')
  10 + {
  11 + $result = [];
  12 +
  13 + self::recursiveTreeMap($result, $tree, $from, $to, $symbol);
  14 +
  15 + return $result;
  16 + }
  17 +
  18 + public static function setArrayField($path, $as_string = false) {
  19 + if (is_array($path)) {
  20 + if ($as_string) {
  21 + foreach ($path as &$item) {
  22 + $item = "'$item'";
  23 + }
  24 + }
  25 + $path = implode(',', $path);
  26 + }
  27 + return '{'. $path .'}';
  28 + }
  29 +
  30 + public static function getArrayField($path) {
  31 + $path = trim($path, '{}');
  32 + return empty($path) ? [] : explode(',', $path);
  33 + }
  34 +
  35 + protected static function recursiveTreeMap(&$result, $tree, $from, $to, $symbol = '&ndash;') {
  36 + foreach ($tree as $item) {
  37 + $element = $item['item'];
  38 + $key = ArrayHelper::getValue($element, $from);
  39 + $value = ArrayHelper::getValue($element, $to);
  40 + $row = str_repeat($symbol, $element->depth+1) . $value;
  41 + $result[$key] = $row;
  42 + if (!empty($item['children'])) {
  43 + self::recursiveTreeMap($result, $item['children'], $from, $to, $symbol);
  44 + }
  45 + }
  46 + }
  47 +}
0 48 \ No newline at end of file
... ...
components/artboxtree/ArtboxTreeQueryTrait.php 0 → 100755
  1 +++ a/components/artboxtree/ArtboxTreeQueryTrait.php
  1 +<?php
  2 +
  3 + namespace common\components\artboxtree;
  4 +
  5 + trait ArtboxTreeQueryTrait
  6 + {
  7 +
  8 + public static $cacheTree = [];
  9 +
  10 + /** @var \yii\db\ActiveQuery $this */
  11 + static $model;
  12 +
  13 + /*
  14 + * @return \yii\db\ActiveQuery
  15 + */
  16 + private function getModel()
  17 + {
  18 + if(empty( self::$model )) {
  19 + $class = $this->modelClass;
  20 + self::$model = new $class;
  21 + }
  22 + return self::$model;
  23 + }
  24 +
  25 + public function getTree($group = NULL, $with = NULL)
  26 + {
  27 + $model = $this->getModel();
  28 + if($group !== NULL) {
  29 + $this->andWhere([ $model->keyNameGroup => $group ]);
  30 + }
  31 + if($with) {
  32 + $this->with($with);
  33 + }
  34 + $data = $this->all();
  35 + if(empty( $data )) {
  36 + return [];
  37 + }
  38 +
  39 + return $this->buildTree($data);
  40 + }
  41 +
  42 + private function recursiveRebuild($tree, $parentPath = NULL, $depth = 0)
  43 + {
  44 + $model = $this->getModel();
  45 +
  46 + foreach($tree as $row) {
  47 + $path = ( is_null($parentPath) ? '' : $parentPath . $model->delimiter ) . $row[ 'item' ]->getAttribute($model->keyNameId);
  48 + $row[ 'item' ]->setAttribute($model->keyNamePath, $path);
  49 + $row[ 'item' ]->setAttribute($model->keyNameDepth, $depth);
  50 + $row[ 'item' ]->save();
  51 + if(!empty( $row[ 'children' ] )) {
  52 + $this->recursiveRebuild($row[ 'children' ], $path, $depth + 1);
  53 + }
  54 + }
  55 + }
  56 +
  57 + /**
  58 + * @param int $group
  59 + */
  60 + public function rebuildMP($group, $with = NULL)
  61 + {
  62 + $tree = $this->getTree($group, $with);
  63 +
  64 + $this->recursiveRebuild($tree);
  65 + }
  66 +
  67 + protected function buildTree(array $data, $parentId = 0)
  68 + {
  69 + $model = $this->getModel();
  70 +
  71 + $result = [];
  72 + foreach($data as $element) {
  73 + if($element[ $model->keyNameParentId ] == $parentId) {
  74 + $children = $this->buildTree($data, $element[ $model->keyNameId ]);
  75 + $result[] = [
  76 + 'item' => $element,
  77 + 'children' => $children,
  78 + ];
  79 + }
  80 + }
  81 + return $result;
  82 + }
  83 +
  84 + public function normalizeTreeData(array $data, $parentId = NULL)
  85 + {
  86 + $model = $this->getModel();
  87 +
  88 + $result = [];
  89 + foreach($data as $element) {
  90 + if($element[ $model->keyNameParentId ] == $parentId) {
  91 + $result[] = $element;
  92 + $children = $this->normalizeTreeData($data, $element[ $model->keyNameId ]);
  93 + if($children) {
  94 + $result = array_merge($result, $children);
  95 + }
  96 + }
  97 + }
  98 + return $result;
  99 + }
  100 + }
0 101 \ No newline at end of file
... ...
components/artboxtree/ArtboxTreeWidget.php 0 → 100755
  1 +++ a/components/artboxtree/ArtboxTreeWidget.php
  1 +<?php
  2 +
  3 +namespace common\components\artboxtree;
  4 +
  5 +use Yii;
  6 +use yii\base\Widget;
  7 +use yii\i18n\Formatter;
  8 +use yii\base\InvalidConfigException;
  9 +
  10 +class ArtboxTreeWidget extends Widget
  11 +{
  12 +
  13 + /**
  14 + * @var \yii\data\DataProviderInterface the data provider for the view. This property is required.
  15 + */
  16 + public $dataProvider;
  17 +
  18 + /**
  19 + * @var string
  20 + */
  21 + public $keyNameId = 'id';
  22 +
  23 + /**
  24 + * @var string
  25 + */
  26 + public $keyNameParentId = 'parent_id';
  27 +
  28 + /**
  29 + * @var integer or null
  30 + */
  31 + public $maxLevel = null;
  32 +
  33 + /**
  34 + * @var integer
  35 + */
  36 + public $rootParentId = 0;
  37 +
  38 + /**
  39 + * @var string
  40 + */
  41 + public $emptyResult;
  42 +
  43 + /**
  44 + * @var boolean include the CSS and JS files. Default is true.
  45 + * If this is set false, you are responsible to explicitly include the necessary CSS and JS files in your page.
  46 + */
  47 + public $assetBundle;
  48 +
  49 + /**
  50 + * @var array|Formatter the formatter used to format model attribute values into displayable texts.
  51 + * This can be either an instance of [[Formatter]] or an configuration array for creating the [[Formatter]]
  52 + * instance. If this property is not set, the "formatter" application component will be used.
  53 + */
  54 + public $formatter;
  55 +
  56 + /**
  57 + * Init the widget object.
  58 + */
  59 + public function init()
  60 + {
  61 + parent::init();
  62 + if ($this->dataProvider === null) {
  63 + throw new InvalidConfigException('The "dataProvider" property must be set.');
  64 + }
  65 + if ($this->keyNameId === null) {
  66 + throw new InvalidConfigException('The "keyNameId" property must be set.');
  67 + }
  68 + if ($this->formatter == null) {
  69 + $this->formatter = Yii::$app->getFormatter();
  70 + } elseif (is_array($this->formatter)) {
  71 + $this->formatter = Yii::createObject($this->formatter);
  72 + }
  73 + if (!$this->formatter instanceof Formatter) {
  74 + throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.');
  75 + }
  76 + }
  77 +
  78 + /**
  79 + * Runs the widget.
  80 + */
  81 + public function run()
  82 + {
  83 + if (!empty($this->assetBundle) && class_exists($this->assetBundle)) {
  84 + $view = $this->getView();
  85 + $assetBundle = $this->assetBundle;
  86 + $assetBundle::register($view);
  87 + }
  88 + if ($this->dataProvider->getCount() == 0) {
  89 + return $this->renderEmptyResult();
  90 + }
  91 +
  92 + parent::run();
  93 + }
  94 +
  95 + protected function renderEmptyResult() {
  96 + return empty($this->emptyResult) ? Yii::t('artbox', 'TreeViewEmptyResult') : Yii::t('artbox', $this->emptyResult);
  97 + }
  98 +
  99 + /**
  100 + * Normalize tree data
  101 + * @param array $data
  102 + * @param string $parentId
  103 + * @return array
  104 + */
  105 + protected function normalizeTreeData(array $data, $parentId = null) {
  106 + $result = [];
  107 + foreach ($data as $element) {
  108 + if ($element[$this->keyNameParentId] == $parentId) {
  109 + $result[] = $element;
  110 + $children = $this->normalizeTreeData($data, $element[$this->keyNameId]);
  111 + if ($children) {
  112 + $result = array_merge($result, $children);
  113 + }
  114 + }
  115 + }
  116 + return $result;
  117 + }
  118 +
  119 + /**
  120 + * Hierarchy tree data
  121 + * @param array $data
  122 + * @param string $parentId
  123 + * @return array
  124 + */
  125 + protected function hierarchyTreeData(array $data, $parentId = null) {
  126 + $result = [];
  127 + foreach ($data as $element) {
  128 + if ($element[$this->keyNameParentId] == $parentId) {
  129 + $children = $this->hierarchyTreeData($data, $element[$this->keyNameId]);
  130 + $result[] = [
  131 + 'item' => $element,
  132 + 'children' => $children
  133 + ];
  134 + }
  135 + }
  136 + return $result;
  137 + }
  138 +}
0 139 \ No newline at end of file
... ...
components/artboxtree/treegrid/TreeGridColumn.php 0 → 100755
  1 +++ a/components/artboxtree/treegrid/TreeGridColumn.php
  1 +<?php
  2 +
  3 +namespace common\components\artboxtree\treegrid;
  4 +
  5 +use Closure;
  6 +use Yii;
  7 +use yii\base\Model;
  8 +use yii\base\Object;
  9 +use yii\data\ActiveDataProvider;
  10 +use yii\db\ActiveQueryInterface;
  11 +use yii\helpers\ArrayHelper;
  12 +use yii\helpers\Html;
  13 +use yii\helpers\Inflector;
  14 +
  15 +/**
  16 + * Column is the base class of all [[TreeGrid]] column classes.
  17 + * The code was based in: https://github.com/yiisoft/yii2/blob/master/framework/grid/DataColumn.php
  18 + *
  19 + * @author Leandro Gehlen <leandrogehlen@gmail.com>
  20 + */
  21 +class TreeGridColumn extends Object {
  22 +
  23 + /**
  24 + * @var TreeGrid the grid view object that owns this column.
  25 + */
  26 + public $grid;
  27 +
  28 + /**
  29 + * @var string the header cell content. Note that it will not be HTML-encoded.
  30 + */
  31 + public $header;
  32 +
  33 + /**
  34 + * @var string the footer cell content. Note that it will not be HTML-encoded.
  35 + */
  36 + public $footer;
  37 +
  38 + /**
  39 + * @var callable This is a callable that will be used to generate the content of each cell.
  40 + * The signature of the function should be the following: `function ($model, $key, $index, $column)`.
  41 + * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered
  42 + * and `$column` is a reference to the [[TreeColumn]] object.
  43 + */
  44 + public $content;
  45 +
  46 + /**
  47 + * @var boolean whether this column is visible. Defaults to true.
  48 + */
  49 + public $visible = true;
  50 +
  51 + /**
  52 + * @var array the HTML attributes for the column group tag.
  53 + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
  54 + */
  55 + public $options = [];
  56 +
  57 + /**
  58 + * @var array the HTML attributes for the header cell tag.
  59 + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
  60 + */
  61 + public $headerOptions = [];
  62 +
  63 + /**
  64 + * @var array|\Closure the HTML attributes for the data cell tag. This can either be an array of
  65 + * attributes or an anonymous function ([[Closure]]) that returns such an array.
  66 + * The signature of the function should be the following: `function ($model, $key, $index, $column)`.
  67 + * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered
  68 + * and `$column` is a reference to the [[Column]] object.
  69 + * A function may be used to assign different attributes to different rows based on the data in that row.
  70 + *
  71 + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
  72 + */
  73 + public $contentOptions = [];
  74 +
  75 + /**
  76 + * @var array the HTML attributes for the footer cell tag.
  77 + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
  78 + */
  79 + public $footerOptions = [];
  80 +
  81 + /**
  82 + * @var string the attribute name associated with this column. When neither [[content]] nor [[value]]
  83 + * is specified, the value of the specified attribute will be retrieved from each data model and displayed.
  84 + *
  85 + * Also, if [[label]] is not specified, the label associated with the attribute will be displayed.
  86 + */
  87 + public $attribute;
  88 +
  89 + /**
  90 + * @var string label to be displayed in the [[header|header cell]] and also to be used as the sorting
  91 + * link label when sorting is enabled for this column.
  92 + * If it is not set and the models provided by the GridViews data provider are instances
  93 + * of [[\yii\db\ActiveRecord]], the label will be determined using [[\yii\db\ActiveRecord::getAttributeLabel()]].
  94 + * Otherwise [[\yii\helpers\Inflector::camel2words()]] will be used to get a label.
  95 + */
  96 + public $label;
  97 +
  98 + /**
  99 + * @var boolean whether the header label should be HTML-encoded.
  100 + * @see label
  101 + */
  102 + public $encodeLabel = true;
  103 +
  104 + /**
  105 + * @var string|\Closure an anonymous function or a string that is used to determine the value to display in the current column.
  106 + *
  107 + * If this is an anonymous function, it will be called for each row and the return value will be used as the value to
  108 + * display for every data model. The signature of this function should be: `function ($model, $key, $index, $column)`.
  109 + * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered
  110 + * and `$column` is a reference to the [[DataColumn]] object.
  111 + *
  112 + * You may also set this property to a string representing the attribute name to be displayed in this column.
  113 + * This can be used when the attribute to be displayed is different from the [[attribute]] that is used for
  114 + * sorting and filtering.
  115 + *
  116 + * If this is not set, `$model[$attribute]` will be used to obtain the value, where `$attribute` is the value of [[attribute]].
  117 + */
  118 + public $value;
  119 +
  120 + /**
  121 + * @var string|array in which format should the value of each data model be displayed as (e.g. `"raw"`, `"text"`, `"html"`,
  122 + * `['date', 'php:Y-m-d']`). Supported formats are determined by the [[GridView::formatter|formatter]] used by
  123 + * the [[GridView]]. Default format is "text" which will format the value as an HTML-encoded plain text when
  124 + * [[\yii\i18n\Formatter]] is used as the [[GridView::$formatter|formatter]] of the GridView.
  125 + */
  126 + public $format = 'text';
  127 +
  128 + /**
  129 + * Renders the header cell.
  130 + */
  131 + public function renderHeaderCell()
  132 + {
  133 + return Html::tag('th', $this->renderHeaderCellContent(), $this->headerOptions);
  134 + }
  135 +
  136 + /**
  137 + * Renders the footer cell.
  138 + */
  139 + public function renderFooterCell()
  140 + {
  141 + return Html::tag('td', $this->renderFooterCellContent(), $this->footerOptions);
  142 + }
  143 +
  144 + /**
  145 + * Renders a data cell.
  146 + * @param mixed $model the data model being rendered
  147 + * @param mixed $key the key associated with the data model
  148 + * @param integer $index the zero-based index of the data item among the item array returned by [[GridView::dataProvider]].
  149 + * @return string the rendering result
  150 + */
  151 + public function renderDataCell($model, $key, $index, $is_first = false, $symbol = '&ndash;')
  152 + {
  153 + if ($this->contentOptions instanceof Closure) {
  154 + $options = call_user_func($this->contentOptions, $model, $key, $index, $this);
  155 + } else {
  156 + $options = $this->contentOptions;
  157 + }
  158 + return Html::tag('td', ($is_first ? str_repeat($symbol, $model->depth) : '') . $this->renderDataCellContent($model, $key, $index), $options);
  159 + }
  160 +
  161 + /**
  162 + * Renders the header cell content.
  163 + * The default implementation simply renders [[header]].
  164 + * This method may be overridden to customize the rendering of the header cell.
  165 + * @return string the rendering result
  166 + */
  167 + protected function renderHeaderCellContent()
  168 + {
  169 + if ($this->header !== null || $this->label === null && $this->attribute === null) {
  170 + return trim($this->header) !== '' ? $this->header : $this->grid->emptyCell;
  171 + }
  172 +
  173 + $provider = $this->grid->dataProvider;
  174 +
  175 + if ($this->label === null) {
  176 + if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQueryInterface) {
  177 + /* @var $model Model */
  178 + $model = new $provider->query->modelClass;
  179 + $label = $model->getAttributeLabel($this->attribute);
  180 + } else {
  181 + $models = $provider->getModels();
  182 + if (($model = reset($models)) instanceof Model) {
  183 + /* @var $model Model */
  184 + $label = $model->getAttributeLabel($this->attribute);
  185 + } else {
  186 + $label = Inflector::camel2words($this->attribute);
  187 + }
  188 + }
  189 + } else {
  190 + $label = $this->label;
  191 + }
  192 +
  193 + return $this->encodeLabel ? Html::encode($label) : $label;
  194 + }
  195 +
  196 + /**
  197 + * Renders the footer cell content.
  198 + * The default implementation simply renders [[footer]].
  199 + * This method may be overridden to customize the rendering of the footer cell.
  200 + * @return string the rendering result
  201 + */
  202 + protected function renderFooterCellContent()
  203 + {
  204 + return trim($this->footer) !== '' ? $this->footer : $this->grid->emptyCell;
  205 + }
  206 +
  207 + /**
  208 + * Renders the data cell content.
  209 + * @param mixed $model the data model
  210 + * @param mixed $key the key associated with the data model
  211 + * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]].
  212 + * @return string the rendering result
  213 + */
  214 + protected function renderDataCellContent($model, $key, $index)
  215 + {
  216 + if ($this->content === null) {
  217 + return $this->grid->formatter->format($this->getDataCellValue($model, $key, $index), $this->format);
  218 + } else {
  219 + if ($this->content !== null) {
  220 + return call_user_func($this->content, $model, $key, $index, $this);
  221 + } else {
  222 + return $this->grid->emptyCell;
  223 + }
  224 + }
  225 +
  226 +
  227 + }
  228 +
  229 + /**
  230 + * Returns the data cell value.
  231 + * @param mixed $model the data model
  232 + * @param mixed $key the key associated with the data model
  233 + * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]].
  234 + * @return string the data cell value
  235 + */
  236 + public function getDataCellValue($model, $key, $index)
  237 + {
  238 + if ($this->value !== null) {
  239 + if (is_string($this->value)) {
  240 + return ArrayHelper::getValue($model, $this->value);
  241 + } else {
  242 + return call_user_func($this->value, $model, $key, $index, $this);
  243 + }
  244 + } elseif ($this->attribute !== null) {
  245 + return ArrayHelper::getValue($model, $this->attribute);
  246 + }
  247 + return null;
  248 + }
  249 +
  250 +}
0 251 \ No newline at end of file
... ...
components/artboxtree/treegrid/TreeGridWidget.php 0 → 100755
  1 +++ a/components/artboxtree/treegrid/TreeGridWidget.php
  1 +<?php
  2 +
  3 +namespace common\components\artboxtree\treegrid;
  4 +
  5 +use common\modules\rubrication\models\TaxOption;
  6 +use Yii;
  7 +use yii\helpers\Html;
  8 +use yii\helpers\ArrayHelper;
  9 +
  10 +class TreeGridWidget extends \common\components\artboxtree\ArtboxTreeWidget {
  11 +
  12 + /**
  13 + * @var array grid column configuration. Each array element represents the configuration
  14 + * for one particular grid column.
  15 + * @see \yii\grid::$columns for details.
  16 + */
  17 + public $columns = [];
  18 +
  19 + /**
  20 + * @var string the default data column class if the class name is not explicitly specified when configuring a data column.
  21 + * Defaults to 'leandrogehlen\treegrid\TreeGridColumn'.
  22 + */
  23 + public $dataColumnClass;
  24 +
  25 + /**
  26 + * @var array the HTML attributes for the container tag of the grid view.
  27 + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
  28 + */
  29 + public $options = ['class' => 'table table-striped table-bordered'];
  30 +
  31 + /**
  32 + * @var array The plugin options
  33 + */
  34 + public $pluginOptions = [];
  35 +
  36 + /**
  37 + * @var boolean whether to show the grid view if [[dataProvider]] returns no data.
  38 + */
  39 + public $showOnEmpty = true;
  40 +
  41 + public $rowOptions = [];
  42 +
  43 + /**
  44 + * @var Closure an anonymous function that is called once BEFORE rendering each data model.
  45 + * It should have the similar signature as [[rowOptions]]. The return result of the function
  46 + * will be rendered directly.
  47 + */
  48 + public $beforeRow;
  49 +
  50 + /**
  51 + * @var Closure an anonymous function that is called once AFTER rendering each data model.
  52 + * It should have the similar signature as [[rowOptions]]. The return result of the function
  53 + * will be rendered directly.
  54 + */
  55 + public $afterRow;
  56 +
  57 + /**
  58 + * @var boolean whether to show the header section of the grid table.
  59 + */
  60 + public $showHeader = true;
  61 +
  62 + /**
  63 + * @var array the HTML attributes for the table header row.
  64 + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
  65 + */
  66 + public $headerRowOptions = [];
  67 +
  68 + /**
  69 + * @var boolean whether to show the footer section of the grid table.
  70 + */
  71 + public $showFooter = false;
  72 +
  73 + /**
  74 + * @var string the HTML display when the content of a cell is empty
  75 + */
  76 + public $emptyCell = '&nbsp;';
  77 +
  78 + public $levelSymbol = '&ndash;';
  79 +
  80 + /**
  81 + * Init the widget object.
  82 + */
  83 + public function init() {
  84 + parent::init();
  85 +
  86 + $this->initColumns();
  87 + }
  88 +
  89 + /**
  90 + * Runs the widget.
  91 + */
  92 + public function run() {
  93 + $run = parent::run();
  94 + if (!is_null($run))
  95 + return $run;
  96 +
  97 + if ($this->showOnEmpty || $this->dataProvider->getCount() > 0) {
  98 + $pagination = $this->dataProvider->getPagination();
  99 + $pagination->setPageSize($this->dataProvider->getTotalCount());
  100 +
  101 + $header = $this->showHeader ? $this->renderTableHeader() : false;
  102 + $body = $this->renderItems();
  103 + $footer = $this->showFooter ? $this->renderTableFooter() : false;
  104 +
  105 + $content = array_filter([
  106 + $header,
  107 + $body,
  108 + $footer
  109 + ]);
  110 +
  111 + return Html::tag('table', implode("\n", $content), $this->options);
  112 + } else {
  113 + return $this->renderEmptyResult();
  114 + }
  115 + }
  116 +
  117 + /**
  118 + * Renders the table header.
  119 + * @return string the rendering result.
  120 + */
  121 + public function renderTableHeader()
  122 + {
  123 + $cells = [];
  124 + foreach ($this->columns as $column) {
  125 + /* @var $column TreeGridColumn */
  126 + $cells[] = $column->renderHeaderCell();
  127 + }
  128 + $content = Html::tag('tr', implode('', $cells), $this->headerRowOptions);
  129 + return "<thead>\n" . $content . "\n</thead>";
  130 + }
  131 +
  132 + /**
  133 + * Renders the table footer.
  134 + * @return string the rendering result.
  135 + */
  136 + public function renderTableFooter()
  137 + {
  138 + $cells = [];
  139 + foreach ($this->columns as $column) {
  140 + /* @var $column TreeGridColumn */
  141 + $cells[] = $column->renderFooterCell();
  142 + }
  143 + $content = Html::tag('tr', implode('', $cells), $this->footerRowOptions);
  144 + return "<tfoot>\n" . $content . "\n</tfoot>";
  145 + }
  146 +
  147 + /**
  148 + * Renders the data models for the grid view.
  149 + */
  150 + public function renderItems()
  151 + {
  152 + $rows = [];
  153 + $models = array_values($this->dataProvider->getModels());
  154 + $keys = $this->dataProvider->getKeys();
  155 + $models = TaxOption::find()->normalizeTreeData($models, $this->rootParentId);
  156 + foreach ($models as $index => $model) {
  157 + $key = $keys[$index];
  158 + if ($this->beforeRow !== null) {
  159 + $row = call_user_func($this->beforeRow, $model, $key, $index, $this);
  160 + if (!empty($row)) {
  161 + $rows[] = $row;
  162 + }
  163 + }
  164 +
  165 + $rows[] = $this->renderTableRow($model, $key, $index);
  166 +
  167 + if ($this->afterRow !== null) {
  168 + $row = call_user_func($this->afterRow, $model, $key, $index, $this);
  169 + if (!empty($row)) {
  170 + $rows[] = $row;
  171 + }
  172 + }
  173 + }
  174 +
  175 + if (empty($rows)) {
  176 + $colspan = count($this->columns);
  177 + return "<tr><td colspan=\"$colspan\">" . $this->renderEmpty() . "</td></tr>";
  178 + } else {
  179 + return implode("\n", $rows);
  180 + }
  181 + }
  182 +
  183 + /**
  184 + * Renders a table row with the given data model and key.
  185 + * @param mixed $model the data model to be rendered
  186 + * @param mixed $key the key associated with the data model
  187 + * @param integer $index the zero-based index of the data model among the model array returned by [[dataProvider]].
  188 + * @return string the rendering result
  189 + */
  190 + public function renderTableRow($model, $key, $index)
  191 + {
  192 + $cells = [];
  193 + /* @var $column TreeGridColumn */
  194 + $i = 0;
  195 + foreach ($this->columns as $column) {
  196 + $cells[] = $column->renderDataCell($model, $key, $index, $i == 0, $this->levelSymbol);
  197 + $i++;
  198 + }
  199 + if ($this->rowOptions instanceof Closure) {
  200 + $options = call_user_func($this->rowOptions, $model, $key, $index, $this);
  201 + } else {
  202 + $options = $this->rowOptions;
  203 + }
  204 + $options['data-key'] = is_array($key) ? json_encode($key) : (string) $key;
  205 +
  206 + $id = ArrayHelper::getValue($model, $this->keyNameId);
  207 + Html::addCssClass($options, "treegrid-$id");
  208 +
  209 + $parentId = ArrayHelper::getValue($model, $this->keyNameParentId);
  210 + if ($parentId) {
  211 + Html::addCssClass($options, "treegrid-parent-$parentId");
  212 + }
  213 +
  214 + return Html::tag('tr', implode('', $cells), $options);
  215 + }
  216 +
  217 + /**
  218 + * Creates column objects and initializes them.
  219 + */
  220 + protected function initColumns()
  221 + {
  222 + if (empty($this->columns)) {
  223 + $this->guessColumns();
  224 + }
  225 + foreach ($this->columns as $i => $column) {
  226 + if (is_string($column)) {
  227 + $column = $this->createDataColumn($column);
  228 + } else {
  229 + $column = Yii::createObject(array_merge([
  230 + 'class' => $this->dataColumnClass ? : TreeGridColumn::className(),
  231 + 'grid' => $this,
  232 + ], $column));
  233 + }
  234 + if (!$column->visible) {
  235 + unset($this->columns[$i]);
  236 + continue;
  237 + }
  238 + $this->columns[$i] = $column;
  239 + }
  240 + }
  241 +
  242 + /**
  243 + * Creates a [[DataColumn]] object based on a string in the format of "attribute:format:label".
  244 + * @param string $text the column specification string
  245 + * @return DataColumn the column instance
  246 + * @throws InvalidConfigException if the column specification is invalid
  247 + */
  248 + protected function createDataColumn($text)
  249 + {
  250 + if (!preg_match('/^([^:]+)(:(\w*))?(:(.*))?$/', $text, $matches)) {
  251 + throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"');
  252 + }
  253 +
  254 + return Yii::createObject([
  255 + 'class' => $this->dataColumnClass ? : TreeGridColumn::className(),
  256 + 'grid' => $this,
  257 + 'attribute' => $matches[1],
  258 + 'format' => isset($matches[3]) ? $matches[3] : 'text',
  259 + 'label' => isset($matches[5]) ? $matches[5] : null,
  260 + ]);
  261 + }
  262 +
  263 + /**
  264 + * This function tries to guess the columns to show from the given data
  265 + * if [[columns]] are not explicitly specified.
  266 + */
  267 + protected function guessColumns()
  268 + {
  269 + $models = $this->dataProvider->getModels();
  270 + $model = reset($models);
  271 + if (is_array($model) || is_object($model)) {
  272 + foreach ($model as $name => $value) {
  273 + $this->columns[] = $name;
  274 + }
  275 + }
  276 + }
  277 +}
0 278 \ No newline at end of file
... ...
components/artboxtree/treelist/TreeListWidget.php 0 → 100755
  1 +++ a/components/artboxtree/treelist/TreeListWidget.php
  1 +<?php
  2 +
  3 +namespace common\components\artboxtree\treelist;
  4 +
  5 +use Yii;
  6 +use yii\helpers\Html;
  7 +use yii\helpers\ArrayHelper;
  8 +
  9 +class TreeListWidget extends \common\components\artboxtree\ArtboxTreeWidget {
  10 +
  11 + public $displayField = 'title';
  12 +
  13 + /**
  14 + * Init the widget object.
  15 + */
  16 + public function init() {
  17 + parent::init();
  18 + }
  19 +
  20 + /**
  21 + * Runs the widget.
  22 + */
  23 + public function run() {
  24 + $run = parent::run();
  25 + if (!is_null($run))
  26 + return $run;
  27 +
  28 + $models = $this->hierarchyTreeData(array_values($this->dataProvider->getModels()), $this->rootParentId);
  29 + return $this->renderTreelist($models);
  30 + }
  31 +
  32 + protected function renderTreelist($models) {
  33 + foreach ($models as $index => $model) {
  34 + $row = $this->renderTreelistItem($model['item']);
  35 + $children = empty($model['children']) ? '' : $this->renderTreelist($model['children']);
  36 + $output[] = '<li>'. $row . $children .'</li>';
  37 + }
  38 +
  39 + if (!empty($output))
  40 + return '<ul>'. implode("\n", $output) .'</ul>';
  41 + }
  42 +
  43 + protected function renderTreelistItem($model)
  44 + {
  45 + $options = [];
  46 + $id = ArrayHelper::getValue($model, $this->keyNameId);
  47 + Html::addCssClass($options, "treelistitem-$id");
  48 +
  49 + $parent_id = ArrayHelper::getValue($model, $this->keyNameParentId);
  50 + if ($parent_id) {
  51 + Html::addCssClass($options, "treelistitem-parent-$parent_id");
  52 + }
  53 +
  54 +// if (is_string($this->value)) {
  55 +// return ArrayHelper::getValue($model, $this->value);
  56 +// } else {
  57 +// return call_user_func($this->value, $model, $key, $index, $this);
  58 +// }
  59 +
  60 + return Html::tag('span', ArrayHelper::getValue($model, $this->displayField), $options);
  61 + }
  62 +}
0 63 \ No newline at end of file
... ...
components/artboxtree/treemenu/TreeMenuWidget.php 0 → 100755
  1 +++ a/components/artboxtree/treemenu/TreeMenuWidget.php
  1 +<?php
  2 +
  3 +namespace common\components\artboxtree\treemenu;
  4 +
  5 +use Yii;
  6 +use yii\helpers\Html;
  7 +use yii\helpers\ArrayHelper;
  8 +
  9 +class TreeMenuWidget extends \common\components\artboxtree\ArtboxTreeWidget {
  10 +
  11 + public $displayField = 'title';
  12 +
  13 + /**
  14 + * Init the widget object.
  15 + */
  16 + public function init() {
  17 + parent::init();
  18 + }
  19 +
  20 + /**
  21 + * Runs the widget.
  22 + */
  23 + public function run() {
  24 + $run = parent::run();
  25 + if (!is_null($run))
  26 + return $run;
  27 +
  28 + $models = $this->hierarchyTreeData(array_values($this->dataProvider->getModels()), $this->rootParentId);
  29 + return $this->renderTreelist($models);
  30 + }
  31 +
  32 + protected function renderTreelist($models) {
  33 + foreach ($models as $index => $model) {
  34 + $row = $this->renderTreelistItem($model['item']);
  35 + $children = empty($model['children']) ? '' : $this->renderTreelist($model['children']);
  36 + $output[] = '<li>'. $row . $children .'</li>';
  37 + }
  38 +
  39 + if (!empty($output))
  40 + return '<ul>'. implode("\n", $output) .'</ul>';
  41 + }
  42 +
  43 + protected function renderTreelistItem($model)
  44 + {
  45 + $options = [];
  46 + $id = ArrayHelper::getValue($model, $this->keyNameId);
  47 + Html::addCssClass($options, "treelistitem-$id");
  48 +
  49 + $parent_id = ArrayHelper::getValue($model, $this->keyNameParentId);
  50 + if ($parent_id) {
  51 + Html::addCssClass($options, "treelistitem-parent-$parent_id");
  52 + }
  53 +
  54 +// if (is_string($this->value)) {
  55 +// return ArrayHelper::getValue($model, $this->value);
  56 +// } else {
  57 +// return call_user_func($this->value, $model, $key, $index, $this);
  58 +// }
  59 +
  60 + return Html::tag('span', ArrayHelper::getValue($model, $this->displayField), $options);
  61 + }
  62 +}
0 63 \ No newline at end of file
... ...
composer.json 0 → 100644
  1 +++ a/composer.json
  1 +{
  2 + "name": "artweb/artbox",
  3 + "description": "Yii2 light-weight CMS",
  4 + "license": "BSD-3-Clause",
  5 + "require": {
  6 + "php": ">=7.0",
  7 + "yiisoft/yii2": "*",
  8 + "developeruz/yii2-db-rbac": "*"
  9 + },
  10 + "autoload": {
  11 + "psr-4": {
  12 + "artweb\\artbox\\": ""
  13 + }
  14 + }
  15 +}
0 16 \ No newline at end of file
... ...
console/GenerateController.php 0 → 100644
  1 +++ a/console/GenerateController.php
  1 +<?php
  2 +
  3 + namespace console\controllers;
  4 +
  5 + use common\modules\product\models\Brand;
  6 + use common\modules\product\models\Category;
  7 + use common\modules\product\models\Product;
  8 + use common\modules\product\models\ProductVariant;
  9 + use common\modules\rubrication\models\TaxGroup;
  10 + use common\modules\rubrication\models\TaxOption;
  11 + use Faker\Factory;
  12 + use Faker\Generator;
  13 + use yii\console\Controller;
  14 + use yii\helpers\Console;
  15 +
  16 + /**
  17 + * Class GenerateController to generate ArtBox fake data
  18 + *
  19 + * @package console\controllers
  20 + */
  21 + class GenerateController extends Controller
  22 + {
  23 + /**
  24 + * Faker Generator locales
  25 + *
  26 + * @var array
  27 + */
  28 + private $locales = [
  29 + 2 => 'ru_RU',
  30 + 3 => 'uk_UA',
  31 + ];
  32 +
  33 + /**
  34 + * Faker Generators instances
  35 + *
  36 + * @var Generator[] $fakers
  37 + */
  38 + private $fakers = [];
  39 +
  40 + /**
  41 + * Create faker Generators for all $locales
  42 + *
  43 + * @param \yii\base\Action $action
  44 + *
  45 + * @return bool
  46 + */
  47 + public function beforeAction($action)
  48 + {
  49 + $parent = parent::beforeAction($action);
  50 + $fakers = [];
  51 + foreach ($this->locales as $locale_id => $locale) {
  52 + $fakers[ $locale_id ] = Factory::create($locale);
  53 + }
  54 + $this->fakers = $fakers;
  55 + return $parent;
  56 + }
  57 +
  58 + /**
  59 + * Generate fake Brands
  60 + *
  61 + * @param int $count Brands count
  62 + *
  63 + * @return int
  64 + */
  65 + public function actionBrand(int $count = 10): int
  66 + {
  67 + /**
  68 + * @var Brand[] $models
  69 + */
  70 + $models = [];
  71 + $fakers = $this->fakers;
  72 + for ($i = 0; $i < $count; $i++) {
  73 + $models[ $i ] = \Yii::createObject(Brand::className());
  74 + $models[ $i ]->generateLangs();
  75 + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) {
  76 + $modelLang->title = $fakers[ $language_id ]->company;
  77 + }
  78 + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) {
  79 + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW);
  80 + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW);
  81 + echo "Brand '$title' inserted under $id ID.\n";
  82 + };
  83 + }
  84 + return 0;
  85 + }
  86 +
  87 + /**
  88 + * Generate fake categories
  89 + *
  90 + * @param int $count Category count
  91 + *
  92 + * @return int
  93 + */
  94 + public function actionCategory(int $count = 10):int
  95 + {
  96 + /**
  97 + * @var Category[] $models
  98 + */
  99 + $models = [];
  100 + $fakers = $this->fakers;
  101 + for ($i = 0; $i < $count; $i++) {
  102 + $models[ $i ] = \Yii::createObject(
  103 + [
  104 + 'class' => Category::className(),
  105 + 'depth' => 0,
  106 + 'parent_id' => 0,
  107 + ]
  108 + );
  109 + $models[ $i ]->generateLangs();
  110 + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) {
  111 + $modelLang->title = ucfirst($fakers[ $language_id ]->word);
  112 + }
  113 + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) {
  114 + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW);
  115 + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW);
  116 + echo "Category '$title' inserted under $id ID.\n";
  117 + };
  118 + }
  119 + return 0;
  120 + }
  121 +
  122 + /**
  123 + * Generate fake products with variants, categories and tax options
  124 + *
  125 + * @param int $count Product count
  126 + *
  127 + * @return int
  128 + */
  129 + public function actionProduct(int $count = 10):int
  130 + {
  131 + /**
  132 + * @var Product[] $models
  133 + */
  134 + $models = [];
  135 + $fakers = $this->fakers;
  136 + $brands = Brand::find()
  137 + ->limit(20)
  138 + ->asArray()
  139 + ->column();
  140 + $categories = Category::find()
  141 + ->limit(100)
  142 + ->asArray()
  143 + ->column();
  144 + $product_options = TaxOption::find()
  145 + ->joinWith('taxGroup')
  146 + ->where([ 'tax_group.level' => TaxGroup::GROUP_PRODUCT ])
  147 + ->limit(50)
  148 + ->asArray()
  149 + ->column();
  150 + $variant_options = TaxOption::find()
  151 + ->joinWith('taxGroup')
  152 + ->where([ 'tax_group.level' => TaxGroup::GROUP_VARIANT ])
  153 + ->limit(50)
  154 + ->asArray()
  155 + ->column();
  156 + for ($i = 0; $i < $count; $i++) {
  157 + $models[ $i ] = \Yii::createObject(
  158 + [
  159 + 'class' => Product::className(),
  160 + 'brand_id' => $fakers[ 2 ]->randomElement($brands),
  161 + ]
  162 + );
  163 + $models[ $i ]->setCategories(
  164 + $fakers[ 2 ]->randomElements($categories, $fakers[ 2 ]->randomDigitNotNull)
  165 + );
  166 + $models[ $i ]->setOptions(
  167 + $fakers[ 2 ]->randomElements($product_options, $fakers[ 2 ]->randomDigitNotNull)
  168 + );
  169 + $models[ $i ]->generateLangs();
  170 + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) {
  171 + $modelLang->title = ucfirst($fakers[ $language_id ]->word);
  172 + }
  173 + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) {
  174 + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW);
  175 + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW);
  176 + echo "Product '$title' inserted under $id ID.\n";
  177 + $variant_count = $fakers[ 2 ]->numberBetween(4, 10);
  178 + for ($j = 0; $j < $variant_count; $j++) {
  179 + /**
  180 + * @var ProductVariant $variant
  181 + */
  182 + $variant = \Yii::createObject(
  183 + [
  184 + 'class' => ProductVariant::className(),
  185 + 'product_id' => $models[ $i ]->id,
  186 + 'sku' => $fakers[ 2 ]->uuid,
  187 + 'price' => $fakers[ 2 ]->randomFloat(2, 100, 10000),
  188 + 'stock' => 10,
  189 + 'product_unit_id' => 1,
  190 + ]
  191 + );
  192 + $variant->setOptions(
  193 + $fakers[ 2 ]->randomElements($variant_options, $fakers[ 2 ]->randomDigitNotNull)
  194 + );
  195 + $variant->generateLangs();
  196 + foreach ($variant->modelLangs as $variant_language_id => $variantLang) {
  197 + $variantLang->title = ucfirst($fakers[ $variant_language_id ]->word);
  198 + }
  199 + if ($variant->save() && $variant->transactionStatus) {
  200 + $variant_title = $this->ansiFormat($variant->modelLangs[ 2 ]->title, Console::FG_YELLOW);
  201 + $variant_id = $this->ansiFormat($variant->id, Console::FG_YELLOW);
  202 + echo "Variant '$variant_title' inserted under $variant_id ID for product $title.\n";
  203 + }
  204 + }
  205 + };
  206 + }
  207 + return 0;
  208 + }
  209 +
  210 + /**
  211 + * Generate fake tax groups with tax options.
  212 + *
  213 + * @param int $count_product Tax Groups for Product
  214 + * @param int $count_variant Tax Groups for ProductVariant
  215 + *
  216 + * @return int
  217 + */
  218 + public function actionTax(int $count_product = 10, int $count_variant = 10):int
  219 + {
  220 + $categories = Category::find()
  221 + ->asArray()
  222 + ->column();
  223 + $this->generateTax(TaxGroup::GROUP_PRODUCT, $count_product, $categories);
  224 + $this->generateTax(TaxGroup::GROUP_VARIANT, $count_variant, $categories);
  225 + return 0;
  226 + }
  227 +
  228 + /**
  229 + * Generates tax groups amd tax options for actionTax
  230 + *
  231 + * @param int $level Tax Group for Product or ProductVariant
  232 + * @param int $count Tax Group count
  233 + * @param array $categories Categories for Tax Groups
  234 + *
  235 + * @see GenerateController::actionTax()
  236 + */
  237 + private function generateTax(int $level, int $count, array $categories = [])
  238 + {
  239 + $count_option = 10;
  240 + $fakers = $this->fakers;
  241 + /**
  242 + * @var TaxGroup[] $models
  243 + */
  244 + $models = [];
  245 + for ($i = 0; $i < $count; $i++) {
  246 + $models[ $i ] = \Yii::createObject(
  247 + [
  248 + 'class' => TaxGroup::className(),
  249 + 'is_filter' => true,
  250 + 'display' => true,
  251 + ]
  252 + );
  253 + $models[ $i ]->level = $level;
  254 + $models[ $i ]->setCategories($categories);
  255 + $models[ $i ]->generateLangs();
  256 + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) {
  257 + $modelLang->title = ucfirst($fakers[ $language_id ]->word);
  258 + }
  259 + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) {
  260 + for ($j = 0; $j < $count_option; $j++) {
  261 + /**
  262 + * @var TaxOption $option
  263 + */
  264 + $option = \Yii::createObject(
  265 + TaxOption::className()
  266 + );
  267 + $option->tax_group_id = $models[ $i ]->id;
  268 + $option->generateLangs();
  269 + foreach ($option->modelLangs as $option_language_id => $taxOptionLang) {
  270 + $taxOptionLang->value = ucfirst($fakers[ $option_language_id ]->word);
  271 + }
  272 + $option->save();
  273 + }
  274 + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW);
  275 + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW);
  276 + $element = $this->ansiFormat(
  277 + ( $level === TaxGroup::GROUP_PRODUCT ) ? 'Product' : 'Variant',
  278 + Console::FG_RED
  279 + );
  280 + echo "Category '$title' inserted for $element under $id ID.\n";
  281 + };
  282 + }
  283 + }
  284 + }
  285 +
0 286 \ No newline at end of file
... ...
console/ImportController.php 0 → 100755
  1 +++ a/console/ImportController.php
  1 +<?php
  2 +
  3 +namespace console\controllers;
  4 +
  5 +use common\modules\product\models\Import;
  6 +use Yii;
  7 +use yii\console\Controller;
  8 +
  9 +class ImportController extends Controller {
  10 + public $errors = [];
  11 +
  12 +
  13 + private function getProductsFile($file_type = 'uploadFileProducts') {
  14 + $filename = Yii::getAlias('@uploadDir') .'/'. Yii::getAlias('@'. $file_type);
  15 + if (!is_file($filename)) {
  16 + $this->stderr('Task already executed');
  17 + return Controller::EXIT_CODE_ERROR;
  18 + }
  19 + return fopen ($filename, 'r');
  20 + }
  21 +
  22 + public function actionProducts() {
  23 +// if (file_exists(Yii::getAlias('@uploadDir/goProducts.lock'))) {
  24 +// $this->errors[] = 'Task already executed';
  25 +// return Controller::EXIT_CODE_ERROR;
  26 +// }
  27 +// $ff = fopen(Yii::getAlias('@uploadDir/goProducts.lock'), 'w+');
  28 +// fclose($ff);
  29 + $model = new Import();
  30 + $model->goProducts(0, null);
  31 +// unlink(Yii::getAlias('@uploadDir/goProducts.lock'));
  32 + return Controller::EXIT_CODE_NORMAL;
  33 + }
  34 +
  35 + public function actionPrices() {
  36 + if (file_exists(Yii::getAlias('@uploadDir/goPrices.lock'))) {
  37 + $this->stderr('Task already executed');
  38 + return Controller::EXIT_CODE_ERROR;
  39 + }
  40 + $ff = fopen(Yii::getAlias('@uploadDir/goPrices.lock'), 'w+');
  41 + fclose($ff);
  42 + $model = new Import();
  43 + $data = $model->goPrices(0, null);
  44 + unlink(Yii::getAlias('@uploadDir/goPrices.lock'));
  45 + return Controller::EXIT_CODE_NORMAL;
  46 + }
  47 +
  48 + private function saveNotFoundRecord (array $line, $filename)
  49 + {
  50 + $str = implode (';', $line)."\n";
  51 + $str = iconv ("UTF-8//TRANSLIT//IGNORE", "windows-1251", $str);
  52 +
  53 + $fg = fopen (Yii::getAlias('@uploadDir') .'/'. $filename, 'a+');
  54 + fputs ($fg, $str);
  55 + fclose ($fg);
  56 + }
  57 +}
0 58 \ No newline at end of file
... ...
console/SiteMapController.php 0 → 100755
  1 +++ a/console/SiteMapController.php
  1 +<?php
  2 +
  3 +namespace console\controllers;
  4 +
  5 +use common\models\Article;
  6 +use common\models\Seo;
  7 +use common\modules\product\models\Category;
  8 +use common\modules\product\models\Product;
  9 +use frontend\models\ProductFrontendSearch;
  10 +use Yii;
  11 +use common\models\Page;
  12 +use yii\helpers\ArrayHelper;
  13 +use yii\helpers\Url;
  14 +use yii\console\Controller;
  15 +/**
  16 + * PageController implements the CRUD actions for Page model.
  17 + */
  18 +class SiteMapController extends Controller
  19 +{
  20 +
  21 + private $urlList = ['http://www.rukzachok.com.ua/'];
  22 + private $count = 1;
  23 +
  24 +
  25 +
  26 + public function checkFilter($category, $filter){
  27 + $productModel = new ProductFrontendSearch();
  28 + $productProvider = $productModel->search($category, $filter);
  29 + if(!empty($productProvider->models)){
  30 + return true;
  31 + } else {
  32 + return false;
  33 + }
  34 + }
  35 +
  36 +
  37 +
  38 + public function getAddStatic(){
  39 + return [
  40 + 'http://www.rukzachok.com.ua',
  41 + 'http://www.rukzachok.com.ua/catalog'
  42 + ];
  43 + }
  44 +
  45 +
  46 + public function getProducts() {
  47 + return Product::find()->all();
  48 +
  49 + }
  50 +
  51 +
  52 + public function getSeoLinks() {
  53 + return Seo::find()->where(['meta' => ''])->all();
  54 +
  55 + }
  56 +
  57 + public function getStaticPages(){
  58 + return Page::find()->all();
  59 + }
  60 +
  61 +
  62 + public function getCategories(){
  63 + return Category::find()->all();
  64 + }
  65 +
  66 +
  67 + public function getArticles(){
  68 + return Article::find()->all();
  69 + }
  70 +
  71 + public function getBrands($category){
  72 +
  73 + return $category->brands;
  74 + }
  75 +
  76 + /**
  77 + * @param $category Category;
  78 + * @return mixed
  79 + */
  80 +
  81 + public function getFilters($category){
  82 +
  83 + return $category->getActiveFilters()->all();
  84 +
  85 + }
  86 +
  87 +
  88 + public function checkUrl($url){
  89 + if(!in_array($url, $this->urlList)){
  90 + $this->urlList[] = $url;
  91 + return true;
  92 + } else {
  93 + return false;
  94 + }
  95 + }
  96 +
  97 +
  98 + public function createRow( $url, $priority, &$content ){
  99 + if($this->checkUrl($url)){
  100 + print $this->count++ . "\n";
  101 + $content .= '<url>' .
  102 + '<loc>' . $url . '</loc>' .
  103 + '<lastmod>' . date('Y-m-d') . '</lastmod>' .
  104 + '<changefreq>Daily</changefreq>' .
  105 + '<priority>' . $priority .'</priority>' .
  106 + '</url>';
  107 + }
  108 + }
  109 +
  110 +
  111 + public function actionProcess() {
  112 +
  113 + $config = ArrayHelper::merge(
  114 + require(__DIR__ . '/../../frontend/config/main.php'),
  115 + require(__DIR__ . '/../../common/config/main.php')
  116 +
  117 + );
  118 +
  119 + Yii::$app->urlManager->addRules($config['components']['urlManager']['rules']);
  120 +
  121 +
  122 +
  123 + $dirName = Yii::getAlias('@frontend').'/web';
  124 +
  125 + $filename = 'sitemap.xml';
  126 +
  127 + setlocale(LC_ALL, 'ru_RU.CP1251');
  128 + $handle = fopen($dirName .'/'. $filename, "w");
  129 +
  130 + $content = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
  131 +
  132 + foreach ($this->getAddStatic() as $page) {
  133 + $this->createRow($page , 1,$content);
  134 + }
  135 +
  136 + foreach ($this->getStaticPages() as $page) {
  137 + $url = Url::to(['text/index','translit' => $page->translit]);
  138 + $this->createRow($url , 1,$content);
  139 + }
  140 +
  141 + foreach ($this->getCategories() as $category) {
  142 + $url = Url::to(['catalog/category', 'category' => $category]);
  143 + $this->createRow($url , 1,$content);
  144 + }
  145 +
  146 +
  147 + foreach ($this->getProducts() as $product) {
  148 +
  149 + $url = Url::to(['catalog/product', 'product' => $product]);
  150 + $this->createRow($url , 0.9, $content);
  151 + }
  152 +
  153 +
  154 + foreach ($this->getArticles() as $article) {
  155 +
  156 + $url = Url::to(['articles/show', 'translit' => $article->translit, 'id' => $article->id,]);
  157 + $this->createRow($url , 0.8,$content);
  158 +
  159 + }
  160 +
  161 +
  162 + foreach($this->getCategories() as $category){
  163 + foreach ($this->getBrands($category) as $brand) {
  164 + if($this->checkFilter($category, ['brands' => [$brand->id]])){
  165 + $url = Url::to(['catalog/category', 'category' => $category, 'filters' => ['brands' => [$brand->alias]]]) ;
  166 + $this->createRow($url , 0.8, $content);
  167 + }
  168 + }
  169 + }
  170 +
  171 +
  172 + foreach($this->getCategories() as $category){
  173 + foreach ($this->getFilters($category) as $filter) {
  174 + if($this->checkFilter($category, [$filter['group_alias'] => [$filter['option_alias']]])){
  175 + $url = Url::to(['catalog/category', 'category' => $category, 'filters' => [$filter['group_alias'] => [$filter['option_alias']]] ]);
  176 + $this->createRow($url , 0.8, $content);
  177 + }
  178 +
  179 + }
  180 + }
  181 +
  182 + foreach($this->getSeoLinks() as $link){
  183 + $url = Yii::$app->urlManager->baseUrl.$link->url;
  184 + $this->createRow($url , 0.7, $content);
  185 +
  186 + }
  187 +
  188 +
  189 +
  190 +// foreach($this->getCategories() as $category){
  191 +// foreach ($this->getFilters($category) as $filter1) {
  192 +// foreach ($this->getFilters($category) as $filter2) {
  193 +// if($this->checkFilter($category, [$filter1['group_alias'] => [$filter1['option_alias']],$filter2['group_alias'] => [$filter2['option_alias']]] )){
  194 +// $url = Url::to(['catalog/category', 'category' => $category, 'filters' => [$filter1['group_alias'] => [$filter1['option_alias']],$filter2['group_alias'] => [$filter2['option_alias']]] ]);
  195 +// $this->createRow($url , 0.7, $content);
  196 +// }
  197 +//
  198 +// }
  199 +//
  200 +// foreach ($this->getBrands($category) as $brand) {
  201 +// if($this->checkFilter($category, ['brands' => [$brand->id], $filter1['group_alias'] => [$filter1['option_alias']]] )){
  202 +// $url = Url::to(['catalog/category', 'category' => $category, 'filters' => ['brands' => [$brand->alias],$filter1['group_alias'] => [$filter1['option_alias']]]]);
  203 +// $this->createRow($url , 0.7,$content);
  204 +// }
  205 +//
  206 +// }
  207 +// }
  208 +// }
  209 +
  210 +
  211 +
  212 + $content .= '</urlset>';
  213 +
  214 + fwrite($handle, $content);
  215 + fclose($handle);
  216 +
  217 + print $dirName .'/'. $filename;
  218 + }
  219 +
  220 +}
... ...
models/Customer.php 0 → 100755
  1 +++ a/models/Customer.php
  1 +<?php
  2 +
  3 + namespace common\models;
  4 +
  5 + use Yii;
  6 + use yii\web\IdentityInterface;
  7 +
  8 + /**
  9 + * This is the model class for table "customer".
  10 + *
  11 + * @property integer $id
  12 + * @property string $username
  13 + * @property string $password_hash
  14 + * @property string $name
  15 + * @property string $surname
  16 + * @property string $phone
  17 + * @property string $gender
  18 + * @property integer $birth_day
  19 + * @property integer $birth_month
  20 + * @property integer $birth_year
  21 + * @property string $body
  22 + * @property integer $group_id
  23 + * @property string $email
  24 + * @property string $auth_key
  25 + * @property string $password_reset_token
  26 + * @property integer $status
  27 + * @property integer $created_at
  28 + * @property integer $updated_at
  29 + */
  30 + class Customer extends User implements IdentityInterface
  31 + {
  32 +
  33 + /**
  34 + * @inheritdoc
  35 + */
  36 + public static function tableName()
  37 + {
  38 + return 'customer';
  39 + }
  40 +
  41 + /**
  42 + * @inheritdoc
  43 + */
  44 + public function rules()
  45 + {
  46 + return [
  47 + [
  48 + [
  49 + 'username',
  50 + 'password_hash',
  51 + ],
  52 + 'required',
  53 + ],
  54 + [
  55 + [ 'password' ],
  56 + 'safe',
  57 + ],
  58 + [
  59 + [
  60 + 'birth_day',
  61 + 'birth_month',
  62 + 'birth_year',
  63 + 'group_id',
  64 + 'status',
  65 + 'created_at',
  66 + 'updated_at',
  67 + ],
  68 + 'integer',
  69 + ],
  70 + [
  71 + [ 'body' ],
  72 + 'string',
  73 + ],
  74 + [
  75 + [ 'status' ],
  76 + 'default',
  77 + 'value' => '10',
  78 + ],
  79 + [
  80 + [
  81 + 'username',
  82 + 'name',
  83 + 'surname',
  84 + 'phone',
  85 + 'email',
  86 + 'password_reset_token',
  87 + ],
  88 + 'string',
  89 + 'max' => 255,
  90 + ],
  91 + [
  92 + [
  93 + 'gender',
  94 + 'auth_key',
  95 + ],
  96 + 'string',
  97 + 'max' => 32,
  98 + ],
  99 + ];
  100 + }
  101 +
  102 + /**
  103 + * @inheritdoc
  104 + */
  105 + public function attributeLabels()
  106 + {
  107 + return [
  108 + 'id' => Yii::t('app', 'id'),
  109 + 'username' => Yii::t('app', 'username'),
  110 + 'name' => Yii::t('app', 'cname'),
  111 + 'surname' => Yii::t('app', 'surname'),
  112 + 'phone' => Yii::t('app', 'phone'),
  113 + 'gender' => Yii::t('app', 'gender'),
  114 + 'birth_day' => Yii::t('app', 'birth_day'),
  115 + 'birth_month' => Yii::t('app', 'birth_month'),
  116 + 'birth_year' => Yii::t('app', 'birth_year'),
  117 + 'body' => Yii::t('app', 'body'),
  118 + 'group_id' => Yii::t('app', 'group_id'),
  119 + 'email' => Yii::t('app', 'email'),
  120 + 'auth_key' => Yii::t('app', 'auth_key'),
  121 + 'password_reset_token' => Yii::t('app', 'password_reset_token'),
  122 + 'status' => Yii::t('app', 'status'),
  123 + 'created_at' => Yii::t('app', 'created_at'),
  124 + 'updated_at' => Yii::t('app', 'updated_at'),
  125 + ];
  126 + }
  127 +
  128 + /**
  129 + * Finds user by email
  130 + *
  131 + * @param string $email
  132 + *
  133 + * @return static|null
  134 + */
  135 + public static function findByEmail($email)
  136 + {
  137 + return static::findOne(
  138 + [
  139 + 'email' => $email,
  140 + 'status' => self::STATUS_ACTIVE,
  141 + ]
  142 + );
  143 + }
  144 +
  145 + /**
  146 + * Get full name
  147 + *
  148 + * @return string
  149 + */
  150 + public function getName()
  151 + {
  152 + return $this->username . ' ' . $this->surname;
  153 + }
  154 +
  155 + public function getPassword()
  156 + {
  157 + return false;
  158 + }
  159 +
  160 + }
... ...
models/CustomerSearch.php 0 → 100755
  1 +++ a/models/CustomerSearch.php
  1 +<?php
  2 +
  3 + namespace common\models;
  4 +
  5 + use yii\base\Model;
  6 + use yii\data\ActiveDataProvider;
  7 +
  8 + /**
  9 + * CustomerSearch represents the model behind the search form about `common\models\Customer`.
  10 + */
  11 + class CustomerSearch extends Customer
  12 + {
  13 +
  14 + /**
  15 + * @inheritdoc
  16 + */
  17 + public function rules()
  18 + {
  19 + return [
  20 + [
  21 + [
  22 + 'id',
  23 + 'birth_day',
  24 + 'birth_month',
  25 + 'birth_year',
  26 + 'group_id',
  27 + ],
  28 + 'integer',
  29 + ],
  30 + [
  31 + [
  32 + 'username',
  33 + 'name',
  34 + 'surname',
  35 + 'phone',
  36 + 'body',
  37 + ],
  38 + 'safe',
  39 + ],
  40 + ];
  41 + }
  42 +
  43 + /**
  44 + * @inheritdoc
  45 + */
  46 + public function scenarios()
  47 + {
  48 + // bypass scenarios() implementation in the parent class
  49 + return Model::scenarios();
  50 + }
  51 +
  52 + /**
  53 + * Creates data provider instance with search query applied
  54 + *
  55 + * @param array $params
  56 + *
  57 + * @return ActiveDataProvider
  58 + */
  59 + public function search($params)
  60 + {
  61 + $query = Customer::find();
  62 +
  63 + // add conditions that should always apply here
  64 +
  65 + $dataProvider = new ActiveDataProvider(
  66 + [
  67 + 'query' => $query,
  68 + ]
  69 + );
  70 +
  71 + $this->load($params);
  72 +
  73 + if (!$this->validate()) {
  74 + // uncomment the following line if you do not want to return any records when validation fails
  75 + // $query->where('0=1');
  76 + return $dataProvider;
  77 + }
  78 +
  79 + // grid filtering conditions
  80 + $query->andFilterWhere(
  81 + [
  82 + 'id' => $this->id,
  83 + 'birth_day' => $this->birth_day,
  84 + 'birth_month' => $this->birth_month,
  85 + 'birth_year' => $this->birth_year,
  86 + 'group_id' => $this->group_id,
  87 + ]
  88 + );
  89 +
  90 + $query->andFilterWhere(
  91 + [
  92 + 'like',
  93 + 'username',
  94 + $this->username,
  95 + ]
  96 + )
  97 + ->andFilterWhere(
  98 + [
  99 + 'like',
  100 + 'name',
  101 + $this->name,
  102 + ]
  103 + )
  104 + ->andFilterWhere(
  105 + [
  106 + 'like',
  107 + 'surname',
  108 + $this->surname,
  109 + ]
  110 + )
  111 + ->andFilterWhere(
  112 + [
  113 + 'like',
  114 + 'phone',
  115 + $this->phone,
  116 + ]
  117 + )
  118 + ->andFilterWhere(
  119 + [
  120 + 'like',
  121 + 'body',
  122 + $this->body,
  123 + ]
  124 + );
  125 +
  126 + return $dataProvider;
  127 + }
  128 + }
... ...
models/Feedback.php 0 → 100755
  1 +++ a/models/Feedback.php
  1 +<?php
  2 +
  3 + namespace common\models;
  4 +
  5 + use Yii;
  6 + use yii\behaviors\AttributeBehavior;
  7 + use yii\behaviors\TimestampBehavior;
  8 + use yii\db\ActiveRecord;
  9 +
  10 + /**
  11 + * This is the model class for table "feedback".
  12 + *
  13 + * @property integer $id
  14 + * @property string $name
  15 + * @property string $phone
  16 + * @property integer $created_at
  17 + * @property string $ip
  18 + */
  19 + class Feedback extends ActiveRecord
  20 + {
  21 +
  22 + const SCENARIO_FEEDBACK = 'feedback';
  23 + const SCENARIO_CALLBACK = 'callback';
  24 +
  25 + /**
  26 + * @inheritdoc
  27 + */
  28 + public static function tableName()
  29 + {
  30 + return 'feedback';
  31 + }
  32 +
  33 + /**
  34 + * @inheritdoc
  35 + */
  36 + public function scenarios()
  37 + {
  38 + $scenarios = parent::scenarios();
  39 + $scenarios = array_merge(
  40 + $scenarios,
  41 + [
  42 + self::SCENARIO_FEEDBACK => [
  43 + 'name',
  44 + 'phone',
  45 + ],
  46 + self::SCENARIO_CALLBACK => [ 'phone' ],
  47 + ]
  48 + );
  49 + return $scenarios;
  50 + }
  51 +
  52 + /**
  53 + * @inheritdoc
  54 + */
  55 + public function behaviors()
  56 + {
  57 + return [
  58 + [
  59 + 'class' => TimestampBehavior::className(),
  60 + 'updatedAtAttribute' => false,
  61 + ],
  62 + [
  63 + 'class' => AttributeBehavior::className(),
  64 + 'attributes' => [
  65 + ActiveRecord::EVENT_BEFORE_INSERT => 'ip',
  66 + ],
  67 + 'value' => function ($event) {
  68 + return \Yii::$app->request->userIP;
  69 + },
  70 + ],
  71 + ];
  72 + }
  73 +
  74 + /**
  75 + * @inheritdoc
  76 + */
  77 + public function rules()
  78 + {
  79 + return [
  80 + [
  81 + [
  82 + 'phone',
  83 + 'name',
  84 + ],
  85 + 'required',
  86 + ],
  87 + [
  88 + [ 'phone' ],
  89 + 'match',
  90 + 'pattern' => '/^\+38\(\d{3}\)\d{3}-\d{2}-\d{2}$/',
  91 + ],
  92 + [
  93 + [
  94 + 'name',
  95 + 'phone',
  96 + ],
  97 + 'string',
  98 + 'max' => 255,
  99 + ],
  100 + ];
  101 + }
  102 +
  103 + /**
  104 + * @inheritdoc
  105 + */
  106 + public function attributeLabels()
  107 + {
  108 + return [
  109 + 'id' => Yii::t('app', 'id'),
  110 + 'name' => Yii::t('app', 'name'),
  111 + 'phone' => Yii::t('app', 'phone'),
  112 + 'created_at' => Yii::t('app', 'created_at'),
  113 + 'ip' => Yii::t('app', 'ip'),
  114 + ];
  115 + }
  116 + }
... ...
models/FeedbackSearch.php 0 → 100755
  1 +++ a/models/FeedbackSearch.php
  1 +<?php
  2 +
  3 + namespace common\models;
  4 +
  5 + use yii\data\ActiveDataProvider;
  6 +
  7 + class FeedbackSearch extends Feedback
  8 + {
  9 +
  10 + /**
  11 + * @inheritdoc
  12 + */
  13 + public function rules()
  14 + {
  15 + return [
  16 + [
  17 + [ 'id' ],
  18 + 'integer',
  19 + ],
  20 + [
  21 + [
  22 + 'name',
  23 + 'phone',
  24 + 'ip',
  25 + 'created_at',
  26 + ],
  27 + 'safe',
  28 + ],
  29 + ];
  30 + }
  31 +
  32 + /**
  33 + * @inheritdoc
  34 + */
  35 + public function scenarios()
  36 + {
  37 + return Feedback::scenarios();
  38 + }
  39 +
  40 + /**
  41 + * Creates data provider instance with search query applied
  42 + *
  43 + * @param array $params
  44 + *
  45 + * @return ActiveDataProvider
  46 + */
  47 + public function search($params)
  48 + {
  49 + $query = Feedback::find();
  50 +
  51 + // add conditions that should always apply here
  52 +
  53 + $dataProvider = new ActiveDataProvider(
  54 + [
  55 + 'query' => $query,
  56 + ]
  57 + );
  58 +
  59 + $this->load($params);
  60 +
  61 + if (!$this->validate()) {
  62 + // uncomment the following line if you do not want to return any records when validation fails
  63 + // $query->where('0=1');
  64 + return $dataProvider;
  65 + }
  66 +
  67 + // grid filtering conditions
  68 + $query->andFilterWhere(
  69 + [
  70 + 'id' => $this->id,
  71 + ]
  72 + );
  73 +
  74 + $query->andFilterWhere(
  75 + [
  76 + 'like',
  77 + 'name',
  78 + $this->name,
  79 + ]
  80 + )
  81 + ->andFilterWhere(
  82 + [
  83 + 'like',
  84 + 'phone',
  85 + $this->phone,
  86 + ]
  87 + )
  88 + ->andFilterWhere(
  89 + [
  90 + 'like',
  91 + 'ip',
  92 + $this->ip,
  93 + ]
  94 + );
  95 +
  96 + return $dataProvider;
  97 + }
  98 + }
... ...
models/Page.php 0 → 100755
  1 +++ a/models/Page.php
  1 +<?php
  2 +
  3 + namespace common\models;
  4 +
  5 + use common\modules\language\behaviors\LanguageBehavior;
  6 + use yii\db\ActiveQuery;
  7 + use yii\db\ActiveRecord;
  8 + use yii\web\Request;
  9 + use Yii;
  10 +
  11 + /**
  12 + * This is the model class for table "page".
  13 + *
  14 + * @property integer $id
  15 + * @property bool $in_menu
  16 + * * From language behavior *
  17 + * @property PageLang $lang
  18 + * @property PageLang[] $langs
  19 + * @property PageLang $objectLang
  20 + * @property string $ownerKey
  21 + * @property string $langKey
  22 + * @property PageLang[] $modelLangs
  23 + * @property bool $transactionStatus
  24 + * @method string getOwnerKey()
  25 + * @method void setOwnerKey( string $value )
  26 + * @method string getLangKey()
  27 + * @method void setLangKey( string $value )
  28 + * @method ActiveQuery getLangs()
  29 + * @method ActiveQuery getLang( integer $language_id )
  30 + * @method PageLang[] generateLangs()
  31 + * @method void loadLangs( Request $request )
  32 + * @method bool linkLangs()
  33 + * @method bool saveLangs()
  34 + * @method bool getTransactionStatus()
  35 + * * End language behavior *
  36 + */
  37 + class Page extends ActiveRecord
  38 + {
  39 +
  40 + public $title;
  41 +
  42 + /**
  43 + * @inheritdoc
  44 + */
  45 + public static function tableName()
  46 + {
  47 + return 'page';
  48 + }
  49 +
  50 + /**
  51 + * @inheritdoc
  52 + */
  53 + public function behaviors()
  54 + {
  55 + return [
  56 + 'language' => [
  57 + 'class' => LanguageBehavior::className(),
  58 + ],
  59 + ];
  60 + }
  61 +
  62 + /**
  63 + * @inheritdoc
  64 + */
  65 + public function rules()
  66 + {
  67 + return [
  68 + [
  69 + [ 'title' ],
  70 + 'safe',
  71 + ],
  72 + [
  73 + [
  74 + 'in_menu',
  75 + ],
  76 + 'boolean',
  77 + ],
  78 + ];
  79 + }
  80 +
  81 + /**
  82 + * @inheritdoc
  83 + */
  84 + public function attributeLabels()
  85 + {
  86 + return [
  87 + 'id' => Yii::t('app', 'id'),
  88 + 'in_menu' => Yii::t('app', 'in_menu'),
  89 + ];
  90 + }
  91 + }
... ...
models/PageLang.php 0 → 100755
  1 +++ a/models/PageLang.php
  1 +<?php
  2 +
  3 + namespace common\models;
  4 +
  5 + use common\modules\language\models\Language;
  6 + use Yii;
  7 + use yii\db\ActiveRecord;
  8 +
  9 + /**
  10 + * This is the model class for table "page_lang".
  11 + *
  12 + * @property integer $page_id
  13 + * @property integer $language_id
  14 + * @property string $title
  15 + * @property string $body
  16 + * @property string $meta_title
  17 + * @property string $meta_keywords
  18 + * @property string $meta_description
  19 + * @property string $seo_text
  20 + * @property string $h1
  21 + * @property string $alias
  22 + * @property Language $language
  23 + * @property Page $page
  24 + */
  25 + class PageLang extends ActiveRecord
  26 + {
  27 +
  28 + public static function primaryKey()
  29 + {
  30 + return [
  31 + 'page_id',
  32 + 'language_id',
  33 + ];
  34 + }
  35 +
  36 + /**
  37 + * @inheritdoc
  38 + */
  39 + public static function tableName()
  40 + {
  41 + return 'page_lang';
  42 + }
  43 +
  44 + public function behaviors()
  45 + {
  46 + return [
  47 + 'slug' => [
  48 + 'class' => 'common\behaviors\Slug',
  49 + ],
  50 + ];
  51 + }
  52 +
  53 + /**
  54 + * @inheritdoc
  55 + */
  56 + public function rules()
  57 + {
  58 + return [
  59 + [
  60 + [
  61 + 'title',
  62 + 'body',
  63 + ],
  64 + 'required',
  65 + ],
  66 + [
  67 + [
  68 + 'body',
  69 + 'seo_text',
  70 + ],
  71 + 'string',
  72 + ],
  73 + [
  74 + [
  75 + 'title',
  76 + 'meta_title',
  77 + 'meta_keywords',
  78 + 'meta_description',
  79 + 'h1',
  80 + 'alias',
  81 + ],
  82 + 'string',
  83 + 'max' => 255,
  84 + ],
  85 + [
  86 + [
  87 + 'page_id',
  88 + 'language_id',
  89 + ],
  90 + 'unique',
  91 + 'targetAttribute' => [
  92 + 'page_id',
  93 + 'language_id',
  94 + ],
  95 + 'message' => 'The combination of Page ID and Language ID has already been taken.',
  96 + ],
  97 + [
  98 + [ 'language_id' ],
  99 + 'exist',
  100 + 'skipOnError' => true,
  101 + 'targetClass' => Language::className(),
  102 + 'targetAttribute' => [ 'language_id' => 'id' ],
  103 + ],
  104 + [
  105 + [ 'page_id' ],
  106 + 'exist',
  107 + 'skipOnError' => true,
  108 + 'targetClass' => Page::className(),
  109 + 'targetAttribute' => [ 'page_id' => 'id' ],
  110 + ],
  111 + ];
  112 + }
  113 +
  114 + /**
  115 + * @inheritdoc
  116 + */
  117 + public function attributeLabels()
  118 + {
  119 + return [
  120 + 'page_id' => Yii::t('app', 'page_id'),
  121 + 'language_id' => Yii::t('app', 'language_id'),
  122 + 'title' => Yii::t('app', 'title'),
  123 + 'body' => Yii::t('app', 'body'),
  124 + 'meta_title' => Yii::t('app', 'meta_title'),
  125 + 'meta_keywords' => Yii::t('app', 'meta_keywords'),
  126 + 'meta_description' => Yii::t('app', 'meta_description'),
  127 + 'seo_text' => Yii::t('app', 'seo_text'),
  128 + 'h1' => Yii::t('app', 'h1'),
  129 + 'alias' => Yii::t('app', 'alias'),
  130 + ];
  131 + }
  132 +
  133 + /**
  134 + * @return \yii\db\ActiveQuery
  135 + */
  136 + public function getLanguage()
  137 + {
  138 + return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]);
  139 + }
  140 +
  141 + /**
  142 + * @return \yii\db\ActiveQuery
  143 + */
  144 + public function getPage()
  145 + {
  146 + return $this->hasOne(Page::className(), [ 'id' => 'page_id' ]);
  147 + }
  148 + }
... ...
models/PageSearch.php 0 → 100755
  1 +++ a/models/PageSearch.php
  1 +<?php
  2 +
  3 + namespace common\models;
  4 +
  5 + use yii\base\Model;
  6 + use yii\data\ActiveDataProvider;
  7 +
  8 + /**
  9 + * PageSearch represents the model behind the search form about `common\models\Page`.
  10 + */
  11 + class PageSearch extends Page
  12 + {
  13 +
  14 + public $title;
  15 +
  16 + /**
  17 + * @inheritdoc
  18 + */
  19 + public function rules()
  20 + {
  21 + return [
  22 + [
  23 + [ 'id' ],
  24 + 'integer',
  25 + ],
  26 + [
  27 + [ 'title' ],
  28 + 'safe',
  29 + ],
  30 + [
  31 + [ 'in_menu' ],
  32 + 'boolean',
  33 + ],
  34 + ];
  35 + }
  36 +
  37 + /**
  38 + * @inheritdoc
  39 + */
  40 + public function scenarios()
  41 + {
  42 + // bypass scenarios() implementation in the parent class
  43 + return Model::scenarios();
  44 + }
  45 +
  46 + public function behaviors()
  47 + {
  48 + return [];
  49 + }
  50 +
  51 + /**
  52 + * Creates data provider instance with search query applied
  53 + *
  54 + * @param array $params
  55 + *
  56 + * @return ActiveDataProvider
  57 + */
  58 + public function search($params)
  59 + {
  60 + $query = Page::find()
  61 + ->joinWith('lang');
  62 +
  63 + // add conditions that should always apply here
  64 +
  65 + $dataProvider = new ActiveDataProvider(
  66 + [
  67 + 'query' => $query,
  68 + 'sort' => [
  69 + 'attributes' => [
  70 + 'title' => [
  71 + 'asc' => [ 'page_lang.title' => SORT_ASC ],
  72 + 'desc' => [ 'page_lang.title' => SORT_DESC ],
  73 + ],
  74 + 'id',
  75 + 'in_menu',
  76 + ],
  77 + ],
  78 + ]
  79 + );
  80 +
  81 + $this->load($params);
  82 +
  83 + if (!$this->validate()) {
  84 + // uncomment the following line if you do not want to return any records when validation fails
  85 + // $query->where('0=1');
  86 + return $dataProvider;
  87 + }
  88 +
  89 + // grid filtering conditions
  90 + $query->andFilterWhere(
  91 + [
  92 + 'id' => $this->id,
  93 + 'in_menu' => $this->in_menu,
  94 + ]
  95 + )
  96 + ->andFilterWhere(
  97 + [
  98 + 'like',
  99 + 'page_lang.title',
  100 + $this->title,
  101 + ]
  102 + );
  103 +
  104 + return $dataProvider;
  105 + }
  106 + }
... ...
translation/ru/app.php 0 → 100755
  1 +++ a/translation/ru/app.php
  1 +<?php
  2 + return [
  3 + 'id' => 'ID',
  4 + 'username' => 'Логин',
  5 + 'cname' => 'Имя',
  6 + 'surname' => 'Фамилия',
  7 + 'auth_key' => 'Ключ аутентификации',
  8 + 'password_hash' => 'Хэш пароля',
  9 + 'password_reset_token' => 'Password Reset Token',
  10 + 'email' => 'Логин (e-mail)',
  11 + 'phone' => 'Телефон',
  12 + 'status' => 'Статус',
  13 + 'gender' => 'Пол',
  14 + 'birth_day' => 'Birth Day',
  15 + 'birth_month' => 'Birth Month',
  16 + 'birth_year' => 'Birth Year',
  17 + 'group_id' => 'Group ID',
  18 + 'created_at' => 'Created At',
  19 + 'updated_at' => 'Updated At',
  20 + 'verifyCode' => 'Код проверки',
  21 + 'password' => 'Пароль',
  22 + 'password_repeat' => 'Повторить пароль',
  23 + 'registration' => 'Регистрация',
  24 + 'message' => 'Этот {field} уже занят',
  25 + 'message_match_password' => 'Пароли не совпадают',
  26 + 'exit' => 'Выход',
  27 + 'enter' => 'Войти',
  28 + 'your_personal_area' => 'Вход в личный кабинет',
  29 + 'forgot_password' => 'Забыли пароль?',
  30 + 'rememberMe' => 'Запомнить меня',
  31 + 'articles' => 'Всего товаров',
  32 + 'code' => 'Код: {0}',
  33 + 'checkout' => 'оформить заказ',
  34 + 'sum' => 'Сумма',
  35 + 'continue_shopping' => 'продолжить покупки',
  36 + 'edit_personal_data' => 'Редактировать личные данные',
  37 + 'personal_data' => 'Личные данные',
  38 + 'my_orders' => 'Мои заказы',
  39 + 'bookmarks' => 'Закладки',
  40 + 'basket' => 'Корзина',
  41 + 'banner_id' => 'Banner ID',
  42 + 'image' => 'Изображение',
  43 + 'alt' => 'Описание',
  44 + 'title' => 'Заголовок',
  45 + 'url' => 'Ссылка',
  46 + 'width' => 'Ширина',
  47 + 'height' => 'Высота',
  48 + 'blog_id' => 'Blog ID',
  49 + 'user_id' => 'User ID',
  50 + 'name' => 'Название',
  51 + 'link' => 'Ссылка',
  52 + 'user_add_id' => 'User Add ID',
  53 + 'view_count' => 'Количество просмотров',
  54 + 'description' => 'Описание',
  55 + 'cover' => 'Фото главное',
  56 + 'event_id' => 'Event ID',
  57 + 'alias' => 'Ссылка',
  58 + 'body' => 'Тело',
  59 + 'meta_title' => 'Мета заголовок',
  60 + 'h1' => 'H1',
  61 + 'seo_text' => 'Сео Текст',
  62 + 'end_at' => 'Срок действия по',
  63 + 'order_items_id' => 'Order Items ID',
  64 + 'order_id' => 'Order ID',
  65 + 'item_id' => 'Item ID',
  66 + 'item_count' => 'Количество',
  67 + 'price' => 'Цена',
  68 + 'customer_id' => 'Customer ID',
  69 + 'delivery' => 'Доставка',
  70 + 'payment' => 'Оплата',
  71 + 'seo_id' => 'Seo ID',
  72 + 'controller' => 'Controller',
  73 + 'seo_category_id' => 'Seo Category ID',
  74 + 'seo_dynamic_id' => 'Seo Dynamic ID',
  75 + 'action' => 'Action',
  76 + 'fields' => 'Поля',
  77 + 'param' => 'Параметры',
  78 + 'key' => 'Ключ',
  79 + 'service_id' => 'Service ID',
  80 + 'slider_id' => 'Slider ID',
  81 + 'speed' => 'Скорость',
  82 + 'duration' => 'Продолжительность',
  83 + 'slider_image_id' => 'Slider Image ID',
  84 + 'sort' => 'Сортировка',
  85 + 'order_name' => 'Ф.И.О',
  86 + 'order_phone' => 'Контактный телефон',
  87 + 'order_email' => 'email',
  88 + 'order_comment' => 'Комментарии',
  89 + 'articlesID' => 'ID',
  90 + 'articlesDate' => 'Дата',
  91 + 'articlesImage' => 'Изображение',
  92 + 'lang-Articles ID' => '',
  93 + 'lang-Language ID' => '',
  94 + 'lang-Title' => '',
  95 + 'lang-Body' => '',
  96 + 'lang-Meta Title' => '',
  97 + 'lang-Meta Keywords' => '',
  98 + 'lang-Meta Description' => '',
  99 + 'lang-Seo Text' => '',
  100 + 'lang-H1' => '',
  101 + 'lang-Body Preview' => '',
  102 + 'language_id' => '',
  103 + 'bg_id' => '',
  104 + 'feedback_id' => 'Feedback ID',
  105 + 'ip' => 'IP',
  106 + 'productName' => 'Продукт',
  107 + 'op_name' => 'Вид',
  108 + 'art' => 'Артикул',
  109 + 'cost' => 'Цена за один',
  110 + 'count' => 'Кол.',
  111 + 'sumCost' => 'Сумма',
  112 + 'in_menu' => 'Show in menu',
  113 + 'page_id' => 'Page ID',
  114 + 'meta_keywords' => 'Meta Keywords',
  115 + 'meta_description' => 'Meta Description',
  116 + 'product_spec_id' => 'Product Spec ID',
  117 + 'product_id' => 'Product ID',
  118 + 'tech_spec_link' => 'Tech Spec Link',
  119 + 'tech_char_link' => 'Tech Char Link',
  120 + 'techSpecFile' => 'techSpecFile',
  121 + 'techCharFile' => 'techCharFile',
  122 + 'tech_spec_text' => 'Tech Spec Text',
  123 + 'instruction' => 'Instruction',
  124 + 'product_to_project_id' => 'Product To Project ID',
  125 + 'product_variant_id' => 'Product Variant ID',
  126 + 'project_id' => 'Project ID',
  127 + 'product_to_rating_id' => 'Product To Rating ID',
  128 + 'value' => 'Value',
  129 + 'images' => 'Images',
  130 + 'project_image_id' => 'Project Image ID',
  131 + 'meta' => 'Meta',
  132 + 'date_time' => 'Дата',
  133 + 'template_location_id' => 'Template Location ID',
  134 + 'template_location_name' => 'Template Location Name',
  135 + 'template_location_title' => 'Template Location Title',
  136 + 'is_slider' => 'Is Slider',
  137 + 'is_banner' => 'Is Banner',
  138 + 'order_delivery_id' => 'order Delivery ID',
  139 + 'text' => 'Text',
  140 + 'emailis' => 'Такой email уже есть.',
  141 + 'меню' => 'меню',
  142 + 'контрактный отдел' => 'контрактный отдел',
  143 + 'отдел по работе с дизайнерами и архитекторами' => 'отдел по работе с дизайнерами и архитекторами',
  144 + 'диз_арх_2' => 'отдел по работе<br/>с дизайнерами<br/>и архитекторами',
  145 + 'search' => 'поиск',
  146 + 'copy1' => '2003 - 2016 Все права защищены и охраняются действующим законодательством Украины.',
  147 + 'copy2' => 'Использование материалов с данного сайта возможно только с письменного разрешения компании ООО «Витекс Украина».',
  148 + 'form1' => 'Запрос на просчет',
  149 + 'comment' => 'комментарий',
  150 + 'submit' => 'Отправить',
  151 + 'Сертификаты' => 'Сертификаты',
  152 + 'Монтаж, уборка, уход' => 'Монтаж, уборка, уход',
  153 + 'Галерея объектов' => 'Галерея объектов',
  154 + 'скачать' => 'скачать',
  155 + 'Документ технической документации' => 'Документ технической документации',
  156 + 'Вы также можете скачать таблицу с ' => 'Вы также можете скачать таблицу с ',
  157 + 'техническими характеристиками' => 'техническими характеристиками',
  158 + 'Номер по каталогу' => 'Номер по каталогу',
  159 + 'Заказать образец' => 'Заказать образец',
  160 + 'Технические характеристики' => 'Технические характеристики',
  161 + 'Контрактные продукты' => 'Контрактные продукты',
  162 + 'Статьи' => 'Статьи',
  163 + 'Контакты' => 'Контакты',
  164 + 'Отправить запрос' => 'Отправить запрос',
  165 + 'Создание сайтов' => 'Создание сайтов',
  166 + 'Подробнее' => 'Подробнее',
  167 + 'Запрос на просчет' => 'Запрос на просчет',
  168 + 'comment2' => 'Комментарий',
  169 + 'продолжить выбор' => 'продолжить выбор',
  170 + 'Отправить' => 'Отправить',
  171 + 'success1' => 'Ваша заявка принята.',
  172 + 'success2' => 'Мы свяжемся в Вами в ближайшее время',
  173 + 'Поиск' => 'Поиск',
  174 + 'Результаты поиска для' => 'Результаты поиска для',
  175 + 'certs' => 'Сертификаты',
  176 + ];
0 177 \ No newline at end of file
... ...
translation/ru/blog.php 0 → 100644
  1 +++ a/translation/ru/blog.php
  1 +<?php
  2 + return [
  3 + 'Select category' => 'Выберите категорию ...',
  4 + 'Select tag' => 'Выберите тэг ...',
  5 + 'Has no parent rubric' => 'Без категории',
  6 + 'Waiting for results' => 'Загрузка ...',
  7 + 'Select related products' => 'Выберите сопутствующие товары',
  8 + 'Select related articles' => 'Выберите статьи',
  9 + 'Blog Articles' => 'Статьи блога',
  10 + 'Create Blog Article' => 'Создать статью',
  11 + 'Update Blog Article: ' => 'Обновить статью: ',
  12 + 'Not active' => 'Не активна',
  13 + 'Active' => 'Активна',
  14 + 'Are you sure you want to delete this item?' => 'Вы точно хотите это удалить ?',
  15 + 'Update' => 'Обновить',
  16 + 'Blog Categories' => 'Рубрики',
  17 + 'Create Blog Category' => 'Создать рубрику',
  18 + 'Update Blog Category: ' => 'Обновить рубрику: ',
  19 + 'Blog Tags' => 'Тэги',
  20 + 'Create Blog Tag' => 'Создать тэг',
  21 + 'Update Blog Tag: ' => 'Обновить тэг: ',
  22 + ];
0 23 \ No newline at end of file
... ...
translation/ru/product.php 0 → 100755
  1 +++ a/translation/ru/product.php
  1 +<?php
  2 + return [
  3 + 'Categories' => 'Категории',
  4 + 'Create Category' => 'Создать Категорию',
  5 + 'Name' => 'Наименование',
  6 + 'Remote ID' => 'ID в 1С',
  7 + 'Search for "{keywords}"' => 'Поиск по "{keywords}"',
  8 + 'Search for "{keywords}" in category "{category}"' => 'Поиск по "{keywords}" в категории "{category}"',
  9 + 'Promo products' => 'Акционные товары',
  10 + 'New products' => 'Новинки',
  11 + 'Top products' => 'Популярные',
  12 + 'Similar products' => 'Похожие товары',
  13 + 'Brands' => 'Бренды',
  14 + 'Brand' => 'Бренд',
  15 + 'Category' => 'Категория',
  16 + 'Select brand' => 'Выберите бренд',
  17 + 'Select category' => 'Выберите категорию',
  18 + 'SKU' => 'Артикул',
  19 + 'Stock' => 'Остаток',
  20 + 'Price' => 'Цена',
  21 + 'Price Old' => 'Старая Цена',
  22 + 'Products' => 'Товары',
  23 + 'Product' => 'Товар',
  24 + 'Variants' => 'Модификации',
  25 + 'Variant' => 'Модификация',
  26 + 'Create Product' => 'Создать Товар',
  27 + 'Enable' => 'Доступно',
  28 + 'Disable' => 'Отсутсвует',
  29 + ];
0 30 \ No newline at end of file
... ...
translation/ua/app.php 0 → 100755
  1 +++ a/translation/ua/app.php
  1 +<?php
  2 + return [
  3 + 'id' => 'ID',
  4 + 'username' => "Логін",
  5 + 'cname' => 'Ім\'я',
  6 + 'surname' => 'Фамилия',
  7 + 'auth_key' => 'Ключ аутентифікації',
  8 + 'password_hash' => 'Хеш паролю',
  9 + 'password_reset_token' => 'Password Reset Token',
  10 + 'email' => 'Логін (e-mail)',
  11 + 'phone' => 'Телефон',
  12 + 'status' => 'Статус',
  13 + 'gender' => 'Пол',
  14 + 'birth_day' => 'Birth Day',
  15 + 'birth_month' => 'Birth Month',
  16 + 'birth_year' => 'Birth Year',
  17 + 'group_id' => 'Group ID',
  18 + 'created_at' => 'Створено',
  19 + 'updated_at' => 'Оновлено',
  20 + 'verifyCode' => 'Код перевірки',
  21 + 'password' => 'Пароль',
  22 + 'password_repeat' => 'Повторити пароль',
  23 + 'registration' => 'Реєстрація',
  24 + 'message' => 'Цей {field} вже зайнято',
  25 + 'message_match_password' => 'Пароли не совпадают',
  26 + 'exit' => 'Вихід',
  27 + 'enter' => 'Війти',
  28 + 'your_personal_area' => 'Вхід в особистий кабінет',
  29 + 'forgot_password' => 'Забули пароль?',
  30 + 'rememberMe' => 'Запам\'ятати мене',
  31 + 'articles' => 'Всього товарів',
  32 + 'code' => 'Код: {0}',
  33 + 'checkout' => 'оформити замовлення',
  34 + 'sum' => 'Сума',
  35 + 'continue_shopping' => 'продовжити покупки',
  36 + 'edit_personal_data' => 'Редагувати особисті дані',
  37 + 'personal_data' => 'Особисті дані',
  38 + 'my_orders' => 'Мої замовлення',
  39 + 'bookmarks' => 'Закладки',
  40 + 'basket' => 'Кошик',
  41 + 'banner_id' => 'Banner ID',
  42 + 'image' => 'Зображення',
  43 + 'alt' => 'Опис',
  44 + 'title' => 'Заголовок',
  45 + 'url' => 'Посилання',
  46 + 'width' => 'Ширина',
  47 + 'height' => 'Висота',
  48 + 'blog_id' => 'Blog ID',
  49 + 'user_id' => 'User ID',
  50 + 'name' => 'Назва',
  51 + 'link' => 'Посилання',
  52 + 'user_add_id' => 'User Add ID',
  53 + 'view_count' => 'Кількість переглядів',
  54 + 'description' => 'Опис',
  55 + 'cover' => 'Фото головне',
  56 + 'event_id' => 'Event ID',
  57 + 'alias' => 'Посилання',
  58 + 'body' => 'Тіло',
  59 + 'meta_title' => 'Мета заголовок',
  60 + 'h1' => 'H1',
  61 + 'seo_text' => 'Сео Текст',
  62 + 'end_at' => 'Термін дії до',
  63 + 'order_items_id' => 'Order Items ID',
  64 + 'order_id' => 'Order ID',
  65 + 'item_id' => 'Item ID',
  66 + 'item_count' => 'Кількість',
  67 + 'price' => 'Ціна',
  68 + 'customer_id' => 'Customer ID',
  69 + 'delivery' => 'Доставка',
  70 + 'payment' => 'Оплата',
  71 + 'seo_id' => 'Seo ID',
  72 + 'controller' => 'Controller',
  73 + 'seo_category_id' => 'Seo Category ID',
  74 + 'seo_dynamic_id' => 'Seo Dynamic ID',
  75 + 'action' => 'Action',
  76 + 'fields' => 'Поля',
  77 + 'param' => 'Параметри',
  78 + 'key' => 'Ключ',
  79 + 'service_id' => 'Service ID',
  80 + 'slider_id' => 'Slider ID',
  81 + 'speed' => 'Швидкість',
  82 + 'duration' => 'Тривалість',
  83 + 'slider_image_id' => 'Slider Image ID',
  84 + 'sort' => 'Сортування',
  85 + 'order_name' => 'П.І.Б.',
  86 + 'order_phone' => 'Контактний телефон',
  87 + 'order_email' => 'email',
  88 + 'order_comment' => 'Коментар',
  89 + 'articlesID' => 'ID',
  90 + 'articlesDate' => 'Дата',
  91 + 'articlesImage' => 'Зображення',
  92 + 'lang-Articles ID' => '',
  93 + 'lang-Language ID' => '',
  94 + 'lang-Title' => '',
  95 + 'lang-Body' => '',
  96 + 'lang-Meta Title' => '',
  97 + 'lang-Meta Keywords' => '',
  98 + 'lang-Meta Description' => '',
  99 + 'lang-Seo Text' => '',
  100 + 'lang-H1' => '',
  101 + 'lang-Body Preview' => '',
  102 + 'language_id' => '',
  103 + 'bg_id' => '',
  104 + 'feedback_id' => 'Feedback ID',
  105 + 'ip' => 'IP',
  106 + 'productName' => 'Продукт',
  107 + 'op_name' => 'Вид',
  108 + 'art' => 'Артикул',
  109 + 'cost' => 'Цена за один',
  110 + 'count' => 'Кол.',
  111 + 'sumCost' => 'Сумма',
  112 + 'in_menu' => 'Show in menu',
  113 + 'page_id' => 'Page ID',
  114 + 'meta_keywords' => 'Meta Keywords',
  115 + 'meta_description' => 'Meta Description',
  116 + 'product_spec_id' => 'Product Spec ID',
  117 + 'product_id' => 'Product ID',
  118 + 'tech_spec_link' => 'Tech Spec Link',
  119 + 'tech_char_link' => 'Tech Char Link',
  120 + 'techSpecFile' => 'techSpecFile',
  121 + 'techCharFile' => 'techCharFile',
  122 + 'tech_spec_text' => 'Tech Spec Text',
  123 + 'instruction' => 'Instruction',
  124 + 'product_to_project_id' => 'Product To Project ID',
  125 + 'product_variant_id' => 'Product Variant ID',
  126 + 'project_id' => 'Project ID',
  127 + 'product_to_rating_id' => 'Product To Rating ID',
  128 + 'value' => 'Value',
  129 + 'images' => 'Images',
  130 + 'project_image_id' => 'Project Image ID',
  131 + 'meta' => 'Meta',
  132 + 'date_time' => 'Дата',
  133 + 'template_location_id' => 'Template Location ID',
  134 + 'template_location_name' => 'Template Location Name',
  135 + 'template_location_title' => 'Template Location Title',
  136 + 'is_slider' => 'Is Slider',
  137 + 'is_banner' => 'Is Banner',
  138 + 'order_delivery_id' => 'order Delivery ID',
  139 + 'text' => 'Text',
  140 + 'emailis' => 'Такой email уже есть.',
  141 + 'меню' => 'меню',
  142 + 'контрактный отдел' => 'контрактне відділення',
  143 + 'отдел по работе с дизайнерами и архитекторами' => 'відділення по роботі з дизайнерами та архітекторами',
  144 + 'диз_арх_2' => 'відділення по роботі<br/>з дизайнерами<br/>та архітекторами',
  145 + 'search' => 'пошук',
  146 + 'copy1' => '2003 - 2016 Всі права захищені та охорняються чинним законодавством України.',
  147 + 'copy2' => 'Використання матеріалів з даного сайту можливе лише з письмого дозволу компанії ТОВ «Вітекс Україна».',
  148 + 'form1' => 'Запит на прорахування',
  149 + 'comment' => 'коментар',
  150 + 'submit' => 'Відправити',
  151 + 'Сертификаты' => 'Сертифікати',
  152 + 'Монтаж, уборка, уход' => 'Монтаж, прибирання, догляд',
  153 + 'Галерея объектов' => "Галерея об'єктів",
  154 + 'скачать' => 'завантажити',
  155 + 'Документ технической документации' => 'Документ технічної документації',
  156 + 'Вы также можете скачать таблицу с ' => 'Ви також можете завантажити таблицю з ',
  157 + 'техническими характеристиками' => 'технічними характеристиками',
  158 + 'Номер по каталогу' => 'Номер за каталогом',
  159 + 'Заказать образец' => 'Замовити зразок',
  160 + 'Технические характеристики' => 'Технічні характеристики',
  161 + 'Контрактные продукты' => 'Контрактні продукти',
  162 + 'Статьи' => 'Статті',
  163 + 'Контакты' => 'Контакти',
  164 + 'Отправить запрос' => 'Надіслати запит',
  165 + 'Создание сайтов' => 'Створення сайтів',
  166 + 'Подробнее' => 'Подробнее',
  167 + 'Запрос на просчет' => 'Запит на прорахування',
  168 + 'comment2' => 'Коментар',
  169 + 'продолжить выбор' => 'продовжити вибір',
  170 + 'Отправить' => 'Відправити',
  171 + 'success1' => 'Ваша заявка прийнята.',
  172 + 'success2' => "Ми зв'яжемось з Вами найближчим часом",
  173 + 'Поиск' => 'Пошук',
  174 + 'Результаты поиска для' => 'Результати пошуку для',
  175 + 'certs' => 'Сертифікати',
  176 + ];
0 177 \ No newline at end of file
... ...
translation/ua/product.php 0 → 100755
  1 +++ a/translation/ua/product.php
  1 +<?php
  2 + return [
  3 + 'Categories' => 'Категории',
  4 + 'Create Category' => 'Создать Категорию',
  5 + 'Name' => 'Наименование',
  6 + 'Remote ID' => 'ID в 1С',
  7 + 'Search for "{keywords}"' => 'Поиск по "{keywords}"',
  8 + 'Search for "{keywords}" in category "{category}"' => 'Поиск по "{keywords}" в категории "{category}"',
  9 + 'Promo products' => 'Акционные товары',
  10 + 'New products' => 'Новинки',
  11 + 'Top products' => 'Популярные',
  12 + 'Similar products' => 'Похожие товары',
  13 + 'Brands' => 'Бренды',
  14 + 'Brand' => 'Бренд',
  15 + 'Category' => 'Категория',
  16 + 'Select brand' => 'Выберите бренд',
  17 + 'Select category' => 'Выберите категорию',
  18 + 'SKU' => 'Артикул',
  19 + 'Stock' => 'Остаток',
  20 + 'Price' => 'Цена',
  21 + 'Price Old' => 'Старая Цена',
  22 + 'Products' => 'Товары',
  23 + 'Product' => 'Товар',
  24 + 'Variants' => 'Модификации',
  25 + 'Variant' => 'Модификация',
  26 + 'Create Product' => 'Создать Товар',
  27 + 'Enable' => 'Доступно',
  28 + 'Disable' => 'Отсутсвует',
  29 + ];
0 30 \ No newline at end of file
... ...