Commit 01799a55c0794bc2c127a29747e09cda10a915e1
0 parents
first commit
Showing
33 changed files
with
4302 additions
and
0 deletions
Show diff stats
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | +} | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 = '–') { | |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 = '–') | |
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 | ... | ... |
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 = ' '; | |
77 | + | |
78 | + public $levelSymbol = '–'; | |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | +} | ... | ... |
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 | + } | ... | ... |
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 | + } | ... | ... |
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 | + } | ... | ... |
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 | + } | ... | ... |
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 | + } | ... | ... |
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 | + } | ... | ... |
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 | + } | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |
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 | ... | ... |