From 01799a55c0794bc2c127a29747e09cda10a915e1 Mon Sep 17 00:00:00 2001 From: yarik Date: Wed, 9 Nov 2016 13:04:45 +0200 Subject: [PATCH] first commit --- behaviors/ArtBoxAccessBehavior.php | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ behaviors/ImageBehavior.php | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ behaviors/MultipleImgBehavior.php | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ behaviors/SaveImgBehavior.php | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ behaviors/SaveMultipleFileBehavior.php | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ behaviors/Slug.php | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/SmsSender.php | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ components/artboximage/ArtboxImage.php | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/artboximage/ArtboxImageHelper.php | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/artboxtree/ArtboxTreeBehavior.php | 443 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/artboxtree/ArtboxTreeHelper.php | 47 +++++++++++++++++++++++++++++++++++++++++++++++ components/artboxtree/ArtboxTreeQueryTrait.php | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/artboxtree/ArtboxTreeWidget.php | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/artboxtree/treegrid/TreeGridColumn.php | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/artboxtree/treegrid/TreeGridWidget.php | 277 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/artboxtree/treelist/TreeListWidget.php | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ components/artboxtree/treemenu/TreeMenuWidget.php | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ composer.json | 15 +++++++++++++++ console/GenerateController.php | 285 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ console/ImportController.php | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ console/SiteMapController.php | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ models/Customer.php | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ models/CustomerSearch.php | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ models/Feedback.php | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ models/FeedbackSearch.php | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ models/Page.php | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ models/PageLang.php | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ models/PageSearch.php | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ translation/ru/app.php | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ translation/ru/blog.php | 22 ++++++++++++++++++++++ translation/ru/product.php | 29 +++++++++++++++++++++++++++++ translation/ua/app.php | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ translation/ua/product.php | 29 +++++++++++++++++++++++++++++ 33 files changed, 4302 insertions(+), 0 deletions(-) create mode 100755 behaviors/ArtBoxAccessBehavior.php create mode 100755 behaviors/ImageBehavior.php create mode 100755 behaviors/MultipleImgBehavior.php create mode 100755 behaviors/SaveImgBehavior.php create mode 100755 behaviors/SaveMultipleFileBehavior.php create mode 100755 behaviors/Slug.php create mode 100755 components/SmsSender.php create mode 100755 components/artboximage/ArtboxImage.php create mode 100755 components/artboximage/ArtboxImageHelper.php create mode 100755 components/artboxtree/ArtboxTreeBehavior.php create mode 100755 components/artboxtree/ArtboxTreeHelper.php create mode 100755 components/artboxtree/ArtboxTreeQueryTrait.php create mode 100755 components/artboxtree/ArtboxTreeWidget.php create mode 100755 components/artboxtree/treegrid/TreeGridColumn.php create mode 100755 components/artboxtree/treegrid/TreeGridWidget.php create mode 100755 components/artboxtree/treelist/TreeListWidget.php create mode 100755 components/artboxtree/treemenu/TreeMenuWidget.php create mode 100644 composer.json create mode 100644 console/GenerateController.php create mode 100755 console/ImportController.php create mode 100755 console/SiteMapController.php create mode 100755 models/Customer.php create mode 100755 models/CustomerSearch.php create mode 100755 models/Feedback.php create mode 100755 models/FeedbackSearch.php create mode 100755 models/Page.php create mode 100755 models/PageLang.php create mode 100755 models/PageSearch.php create mode 100755 translation/ru/app.php create mode 100644 translation/ru/blog.php create mode 100755 translation/ru/product.php create mode 100755 translation/ua/app.php create mode 100755 translation/ua/product.php diff --git a/behaviors/ArtBoxAccessBehavior.php b/behaviors/ArtBoxAccessBehavior.php new file mode 100755 index 0000000..38130bc --- /dev/null +++ b/behaviors/ArtBoxAccessBehavior.php @@ -0,0 +1,141 @@ + 'interception', + ]; + } + + /** + * Check whether current user have access to current action. + * + * @param Event $event + * + * @return void + * @throws \yii\web\ForbiddenHttpException + */ + public function interception($event) + { + if (!isset( Yii::$app->i18n->translations[ 'db_rbac' ] )) { + Yii::$app->i18n->translations[ 'db_rbac' ] = [ + 'class' => 'yii\i18n\PhpMessageSource', + 'sourceLanguage' => 'ru-Ru', + 'basePath' => '@developeruz/db_rbac/messages', + ]; + } + + $route = Yii::$app->getRequest() + ->resolve(); + //Проверяем права по конфигу + $this->createRule(); + $user = Instance::ensure(Yii::$app->user, User::className()); + $request = Yii::$app->getRequest(); + $action = $event->action; + + if (!$this->cheсkByRule($action, $user, $request)) { + //И по AuthManager + if (!$this->checkPermission($route)) { + if ($user->getIsGuest()) { + $user->loginRequired(); + } else { + throw new ForbiddenHttpException(Yii::t('db_rbac', 'Недостаточно прав')); + } + } + + } + } + + /** + * Fill $ruleList with AccessRules + * + * @return void + */ + protected function createRule() + { + foreach ($this->rules as $controller => $rule) { + foreach ($rule as $singleRule) { + if (is_array($singleRule)) { + $option = [ + 'controllers' => [ $controller ], + 'class' => 'yii\filters\AccessRule', + ]; + $this->ruleList[] = Yii::createObject(array_merge($option, $singleRule)); + } + } + } + } + + /** + * Check whether the User allowed to perform action + * + * @param Action $action + * @param User $user + * @param Request $request + * + * @return bool + */ + protected function cheсkByRule($action, $user, $request) + { + + foreach ($this->ruleList as $rule) { + + if ($rule->allows($action, $user, $request)) { + return true; + } + } + return false; + } + + /** + * Check whether the User have permission for current operation + * + * @param array $route + * + * @return bool + */ + protected function checkPermission($route) + { + //$route[0] - is the route, $route[1] - is the associated parameters + $routePathTmp = explode('/', $route[ 0 ]); + $routeVariant = array_shift($routePathTmp); + if (Yii::$app->user->can($routeVariant, $route[ 1 ])) { + return true; + } + /** + * @var string $routePart + */ + foreach ($routePathTmp as $routePart) { + $routeVariant .= '/' . $routePart; + if (Yii::$app->user->can($routeVariant, $route[ 1 ])) { + return true; + } + } + return false; + } + + } + \ No newline at end of file diff --git a/behaviors/ImageBehavior.php b/behaviors/ImageBehavior.php new file mode 100755 index 0000000..a458740 --- /dev/null +++ b/behaviors/ImageBehavior.php @@ -0,0 +1,68 @@ + 'beforeDelete', + ]; + } + + /** + * @param Event $event + */ + public function beforeDelete($event) + { + $file = $this->getImageFile(); + if(file_exists($file)) { + unlink($file); + } + } + + /** + * Get image file path + * + * @return null|string + */ + public function getImageFile() + { + $link = $this->link; + return empty( $this->owner->$link ) ? NULL : \Yii::getAlias('@storage/' . $this->directory . '/' . $this->owner->$link); + } + + /** + * Get image file url + * + * @return null|string + */ + public function getImageUrl() + { + $link = $this->link; + return empty( $this->owner->$link ) ? NULL : '/storage/' . $this->directory . '/' . $this->owner->$link; + } + } \ No newline at end of file diff --git a/behaviors/MultipleImgBehavior.php b/behaviors/MultipleImgBehavior.php new file mode 100755 index 0000000..09e3ef8 --- /dev/null +++ b/behaviors/MultipleImgBehavior.php @@ -0,0 +1,143 @@ +owner; + $query = $owner->hasOne($this->model, $this->links); + $conditions = $this->conditions; + foreach($conditions as $condition) { + $query->andWhere($condition); + } + return $query; + } + + /** + * All images query + * + * @return \yii\db\ActiveQuery + */ + public function getImages() + { + /** + * @var ActiveRecord $owner + */ + $owner = $this->owner; + $query = $owner->hasMany($this->model, $this->links); + $conditions = $this->conditions; + foreach($conditions as $left => $right) { + $query->andWhere([$left => $right]); + } + return $query; + } + + /** + * Get images config array for FileInput + * + * @return array + */ + public function getImagesConfig() + { + $op = []; + $images = $this->getImages()->all(); + $config = $this->config; + if(!isset( $config[ 'id' ] )) { + return $op; + } + foreach($images as $image) { + $op[] = [ + 'caption' => ( isset( $config[ 'caption' ] ) ) ? $image->{$config[ 'caption' ]} : '', + 'url' => ( isset( $config[ 'delete_action' ] ) ) ? Url::to([ + $config[ 'delete_action' ], + 'id' => $image->{$config[ 'id' ]}, + ]) : '', + 'key' => $image->{$config[ 'id' ]}, + 'extra' => [ + 'id' => $image->{$config[ 'id' ]}, + ], + ]; + } + return $op; + } + + /** + * Get images HTML + * + * @param string $preset + * + * @return array + */ + public function getImagesHTML($preset = 'admin_thumb') + { + $op = []; + $images = $this->getImages()->all(); + foreach($images as $image) { + $op[] = ArtboxImageHelper::getImage($image->imageUrl, $preset); + } + return $op; + } + + public function getImageUrl() + { + $image = $this->getImage()->one(); + if(!empty($image)) { + return $image->getImageUrl(); + } else { + return NULL; + } + } + } \ No newline at end of file diff --git a/behaviors/SaveImgBehavior.php b/behaviors/SaveImgBehavior.php new file mode 100755 index 0000000..af9d3d8 --- /dev/null +++ b/behaviors/SaveImgBehavior.php @@ -0,0 +1,95 @@ + 'beforeSave', + ActiveRecord::EVENT_BEFORE_INSERT => 'beforeSave', + ]; + } + + /** + * @param ModelEvent $event + */ + public function beforeSave($event) + { + foreach($this->fields as $field) { + $field_name = $field[ 'name' ]; + $name = $field_name; + if($this->isLanguage) { + $name = '[' . $this->owner->language_id . ']' . $name; + } + + $image = UploadedFile::getInstance($this->owner, $name); + + if(empty( $image ) && $event->name == ActiveRecord::EVENT_BEFORE_UPDATE) { + $this->owner->$field_name = $this->owner->getOldAttribute($field_name); + } elseif(!empty( $image )) { + $imgDir = \Yii::getAlias('@storage/' . $field[ 'directory' ] . '/'); + + if(!is_dir($imgDir)) { + mkdir($imgDir, 0755, true); + } + + $baseName = $image->baseName; + + $iteration = 0; + $file_name = $imgDir . $baseName . '.' . $image->extension; + while(file_exists($file_name)) { + $baseName = $image->baseName . '_' . ++$iteration; + $file_name = $imgDir . $baseName . '.' . $image->extension; + } + unset( $iteration ); + + $this->owner->$field_name = $baseName . '.' . $image->extension; + + $image->saveAs($file_name); + } + } + } + + /** + * @param int $field + * + * @return null|string + */ + public function getImageFile($field = 0) + { + $fieldset = $this->fields[ $field ]; + $name = $fieldset[ 'name' ]; + $directory = $fieldset[ 'directory' ]; + return empty( $this->owner->$name ) ? NULL : '/storage/' . $directory . '/' . $this->owner->$name; + } + + /** + * @param int $field + * + * @return null|string + */ + public function getImageUrl($field = 0) + { + $fieldset = $this->fields[ $field ]; + $name = $fieldset[ 'name' ]; + $directory = $fieldset[ 'directory' ]; + return empty( $this->owner->$name ) ? NULL : '/storage/' . $directory . '/' . $this->owner->$name; + } + } \ No newline at end of file diff --git a/behaviors/SaveMultipleFileBehavior.php b/behaviors/SaveMultipleFileBehavior.php new file mode 100755 index 0000000..d8b3351 --- /dev/null +++ b/behaviors/SaveMultipleFileBehavior.php @@ -0,0 +1,119 @@ + 'downloadFiles', + ActiveRecord::EVENT_AFTER_INSERT => 'downloadFiles', + ]; + } + + /** + * Save files to file table + * + * @param Event $event + */ + public function downloadFiles($event) + { + /** + * @var ActiveRecord $owner + */ + $owner = $this->owner; + $name = $this->name; + $owner->$name = UploadedFile::getInstances($owner, $name); + if(!empty( $files = $this->filesUpload() )) { + $model = $this->model; + $links = $this->links; + $column = $this->column; + foreach($files as $file) { + /** + * @var ActiveRecord $fileModel + */ + $fileModel = new $model(); + foreach($links as $link_owner => $link) { + $fileModel->$link = $owner->$link_owner; + } + $fileModel->$column = $file; + $fileModel->save(); + } + } + $this->detach(); + } + + /** + * Save files to file system + * + * @return array + */ + private function filesUpload() + { + $owner = $this->owner; + $name = $this->name; + $directory = $this->directory; + $fileDir = \Yii::getAlias('@storage/' . $directory . '/'); + if(!is_dir($fileDir)) { + mkdir($fileDir, 0755, true); + } + $files = []; + /** + * @var UploadedFile $file + */ + foreach($owner->$name as $file) { + $fileName = $file->baseName . '.' . $file->extension; + $i = 0; + while(file_exists(\Yii::getAlias($fileDir . $fileName))) { + $fileName = $file->baseName . '_' . ++$i . '.' . $file->extension; + } + $file->saveAs($fileDir . $fileName); + $files[] = $fileName; + } + return $files; + } + } \ No newline at end of file diff --git a/behaviors/Slug.php b/behaviors/Slug.php new file mode 100755 index 0000000..61fa930 --- /dev/null +++ b/behaviors/Slug.php @@ -0,0 +1,200 @@ + 'getSlug', + ActiveRecord::EVENT_BEFORE_UPDATE => 'getSlug', + ]; + } + + /** + * Generate slug + * + * @param yii\base\Event $event + * + * @return void + */ + public function getSlug($event) + { + if(!empty( $this->owner->{$this->inAttribute} )) { + if(empty( $this->owner->{$this->outAttribute} )) { + $this->owner->{$this->outAttribute} = $this->generateSlug($this->owner->{$this->inAttribute}); + } else { + $this->owner->{$this->outAttribute} = $this->generateSlug($this->owner->{$this->outAttribute}); + } + } + } + + /** + * @param string $slug + * + * @return string + */ + private function generateSlug($slug) + { + $slug = $this->slugify($slug); + if($this->checkUniqueSlug($slug)) { + return $slug; + } else { + for($suffix = 2; !$this->checkUniqueSlug($new_slug = $slug . '-' . $suffix); $suffix++) { + } + return $new_slug; + } + } + + /** + * @param string $slug + * + * @return string + */ + private function slugify($slug) + { + if($this->translit) { + return yii\helpers\Inflector::slug( $this->translit( $slug ), '-', true ); + } else { + return $this->slug($slug, '-', true); + } + } + + /** + * @param string $string + * @param string $replacement + * @param bool $lowercase + * + * @return string + */ + private function slug($string, $replacement = '-', $lowercase = true) + { + $string = preg_replace('/[^\p{L}\p{Nd}]+/u', $replacement, $string); + $string = trim($string, $replacement); + return $lowercase ? strtolower($string) : $string; + } + + /** + * Check whether current slug is unique + * + * @param string $slug + * + * @return bool + */ + private function checkUniqueSlug($slug) + { + /** + * @var ActiveRecord $owner + */ + $owner = $this->owner; + $query = $owner->find() + ->where([ + $this->outAttribute => $slug, + ]); + if(!$owner->isNewRecord) { + $pks = $owner->primaryKey(); + if(!empty( $pks )) { + $pk_rules = [ 'and' ]; + foreach($pks as $pk) { + $pk_rules[] = [ $pk => $owner->$pk ]; + } + $query->andWhere([ + 'not', + $pk_rules, + ]); + } + } + return !$query->exists(); + } + + static function translit ($string, $setting = 'all') + { + $letter = array ( + + 'а' => 'a', 'б' => 'b', 'в' => 'v', + 'г' => 'g', 'д' => 'd', 'е' => 'e', + 'ё' => 'e', 'ж' => 'zh', 'з' => 'z', + 'и' => 'i', 'й' => 'y', 'к' => 'k', + 'л' => 'l', 'м' => 'm', 'н' => 'n', + 'о' => 'o', 'п' => 'p', 'р' => 'r', + 'с' => 's', 'т' => 't', 'у' => 'u', + 'ф' => 'f', 'х' => 'h', 'ц' => 'c', + 'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sch', + 'ь' => "", 'ы' => 'y', 'ъ' => "", + 'э' => 'e', 'ю' => 'yu', 'я' => 'ya', + 'ї' => 'yi', 'є' => 'ye', 'і' => 'ee', + + 'А' => 'A', 'Б' => 'B', 'В' => 'V', + 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', + 'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z', + 'И' => 'I', 'Й' => 'Y', 'К' => 'K', + 'Л' => 'L', 'М' => 'M', 'Н' => 'N', + 'О' => 'O', 'П' => 'P', 'Р' => 'R', + 'С' => 'S', 'Т' => 'T', 'У' => 'U', + 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C', + 'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch', + 'Ь' => "", 'Ы' => 'Y', 'Ъ' => "", + 'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya', + 'Ї' => 'Yi', 'Є' => 'Ye', 'І' => 'Ee' + ); + + $symbol = array ( + ' ' => '-', "'" => '', '"' => '', + '!' => '', "@" => '', '#' => '', + '$' => '', "%" => '', '^' => '', + ';' => '', "*" => '', '(' => '', + ')' => '', "+" => '', '~' => '', + '.' => '', ',' => '-', '?' => '', + '…' => '', '№' => 'N', '°' => '', + '`' => '', '|' => '', '&' => '-and-', + '<' => '', '>' => '' + ); + + if ($setting == 'all') + { + $converter = $letter + $symbol; + } + else if ($setting == 'letter') + { + $converter = $letter; + } + else if ($setting == 'symbol') + { + $converter = $symbol; + } + + $url = strtr ($string, $converter); + + $url = str_replace ("---", '-', $url); + $url = str_replace ("--", '-', $url); + + return $url; + } + + } + \ No newline at end of file diff --git a/components/SmsSender.php b/components/SmsSender.php new file mode 100755 index 0000000..d7e657c --- /dev/null +++ b/components/SmsSender.php @@ -0,0 +1,49 @@ +\n"; + $myXML .= ""; + $myXML .= "SENDSMS"; + $myXML .= ' ' . "\n"; + $myXML .= " " . $text . ""; + $myXML .= " " . $recipient . ""; + $myXML .= ""; + $myXML .= ""; + + $ch = curl_init(); + curl_setopt($ch, CURLOPT_USERPWD, $user . ':' . $password); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_URL, 'http://sms-fly.com/api/api.php'); + curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: text/xml", "Accept: text/xml")); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, $myXML); + $response = curl_exec($ch); + curl_close($ch); + + return $response; + } +} diff --git a/components/artboximage/ArtboxImage.php b/components/artboximage/ArtboxImage.php new file mode 100755 index 0000000..0f3ef2c --- /dev/null +++ b/components/artboximage/ArtboxImage.php @@ -0,0 +1,56 @@ + 'jpeg', + 'jpeg' => 'jpeg', + 'png' => 'png', + 'gif' => 'gif', + 'bmp' => 'bmp', + ]; + + /** + * Try to load image and prepare it to manipulation. + * + * @param null|string $file + * @param null|string $driver + * + * @return \yii\image\drivers\Image + * @throws \yii\base\ErrorException + */ + public function load($file = null, $driver = null) + { + if (empty( $file ) || !realpath($file)) { + throw new ErrorException('File name can not be empty and exists'); + } + return Image::factory($file, $driver ? $driver : $this->driver); + } + } \ No newline at end of file diff --git a/components/artboximage/ArtboxImageHelper.php b/components/artboximage/ArtboxImageHelper.php new file mode 100755 index 0000000..84e0768 --- /dev/null +++ b/components/artboximage/ArtboxImageHelper.php @@ -0,0 +1,196 @@ +get('artboximage'); + } + return self::$imageDriver; + } + + /** + * Get named preset from driver preset list. + * + * @param string $preset + * + * @return array|null + */ + public static function getPreset($preset) + { + if (empty( self::$presets )) { + self::$presets = self::getDriver()->presets; + } + return empty( self::$presets[ $preset ] ) ? null : self::$presets[ $preset ]; + } + + /** + * Get image HTML for image + * + * @param string $file + * @param array|string $preset + * @param array $imgOptions + * + * @see Html::img() + * @return string + */ + public static function getImage($file, $preset, $imgOptions = []) + { + $preset_alias = is_array($preset) ? array_keys($preset)[ 0 ] : null; + return Html::img(self::getImageSrc($file, $preset, $preset_alias), $imgOptions); + } + + /** + * Get src for image + * + * @param string $file + * @param string $preset + * @param null|string $preset_alias + * + * @return bool|string + */ + public static function getImageSrc($file, $preset, $preset_alias = null) + { + if (is_string($preset)) { + $preset_alias = $preset; + $preset = self::getPreset($preset); + } + if (empty( $preset ) || empty( $preset_alias )) { + return $file; + } + + $filePath = self::getPathFromUrl($file); + if (!file_exists($filePath) || !preg_match( + '#^(.*)\.(' . self::getExtensionsRegexp() . ')$#', + $file, + $matches + ) + ) { + return $file; + } + return self::getPresetUrl($filePath, $preset, $preset_alias); + } + + /** + * Replace web path with file path + * + * @param string $url + * + * @return string + */ + private static function getPathFromUrl($url) + { + return substr_replace($url, self::getDriver()->rootPath, 0, strlen(self::getDriver()->rootUrl)); + } + + /** + * Replace file path with web path + * + * @param string $path + * + * @return string + */ + private static function getUrlFromPath($path) + { + return substr_replace($path, self::getDriver()->rootUrl, 0, strlen(self::getDriver()->rootPath)); + } + + /** + * Get formatted file url or create it if not exist + * + * @param string $filePath + * @param array $preset + * @param string $preset_alias + * + * @return bool|string + */ + private static function getPresetUrl($filePath, $preset, $preset_alias) + { + $pathinfo = pathinfo($filePath); + $presetPath = $pathinfo[ 'dirname' ] . '/styles/' . strtolower($preset_alias); + $presetFilePath = $presetPath . '/' . $pathinfo[ 'basename' ]; + $presetUrl = self::getUrlFromPath($presetFilePath); + if (file_exists($presetFilePath)) { + return $presetUrl; + } + if (!file_exists($presetPath)) { + @mkdir($presetPath, 0777, true); + } + $output = self::createPresetImage($filePath, $preset, $preset_alias); + if (!empty( $output )) { + $f = fopen($presetFilePath, 'w'); + fwrite($f, $output); + fclose($f); + return $presetUrl; + } + return false; + } + + /** + * Create formatted image. + * Available manipulations: + * * resize + * * flip + * + * @param string $filePath + * @param array $preset + * @param string $preset_alias + * + * @return string + */ + private static function createPresetImage($filePath, $preset, $preset_alias) + { + $image = self::getDriver() + ->load($filePath); + foreach ($preset as $action => $data) { + switch ($action) { + case 'resize': + $width = empty( $data[ 'width' ] ) ? null : $data[ 'width' ]; + $height = empty( $data[ 'height' ] ) ? null : $data[ 'height' ]; + $master = empty( $data[ 'master' ] ) ? null : $data[ 'master' ]; + $image->resize($width, $height, $master); + break; + case 'flip': + $image->flip(@$data[ 'direction' ]); + break; + default: + break; + } + } + return $image->render(); + } + + /** + * Get extensions regexp + * + * @return string regexp + */ + private static function getExtensionsRegexp() + { + $keys = array_keys(self::getDriver()->extensions); + return '(?i)' . join('|', $keys); + } + } \ No newline at end of file diff --git a/components/artboxtree/ArtboxTreeBehavior.php b/components/artboxtree/ArtboxTreeBehavior.php new file mode 100755 index 0000000..b2248a2 --- /dev/null +++ b/components/artboxtree/ArtboxTreeBehavior.php @@ -0,0 +1,443 @@ + $keyNameDepth; + public $primaryKeyMode = true; + + /** + * @var string + */ + public $delimiter = '|'; + + /** + * @var ActiveRecord|self|null + */ + protected $entity; + + /** + * @param ActiveRecord $owner + * @throws Exception + */ + public function attach($owner) + { + parent::attach($owner); + if ($this->keyNameId === null) { + $primaryKey = $owner->primaryKey(); + if (!isset($primaryKey[0])) { + throw new Exception('"' . $owner->className() . '" must have a primary key.'); + } + $this->keyNameId = $primaryKey[0]; + } + } + + public function events() + { + return [ + // @todo Use beforeSave for automatic set MP-params + ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate', + ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert', + ]; + } + + /* + * Main methods + */ + + /* + * get one parent + * use AL-method + */ + public function getParent() { + return $this->getParentAL(); + } + + /* + * get all parents + * use MP-method + */ + public function getParents() { + return $this->getParentsMP(); + } + + /* + * get one-level children items + * use AL-method + */ + public function getChildren() { + return $this->getChildrenAL(); + } + + /* + * get all-level children items + * use MP-method + */ + public function getAllChildren($depth = null, $where = [], $with = null) { + return $this->getAllChildrenMP($depth, $where, $with); + } + + /* + * get all-level children items + * use MP-method + */ + public function getAllChildrenTree($depth = null, $where = [], $with = null) { + $query = $this->getAllChildrenMP($depth, $where, $with); + return $this->buildTree($query->all(), $this->owner->getAttribute($this->keyNameId)); + } + + // @todo Check algorytm + public function buildTree(array $data, $parentId = 0) { + $result = []; + foreach ($data as $key => $element) { + if ($element->getAttribute($this->keyNameParentId) == $parentId) { + unset($data[$key]); + $children = $this->buildTree($data, $element->getAttribute($this->keyNameId)); + $result[] = [ + 'item' => $element, + 'children' => $children + ]; + } + } + return $result; + } + + + /* + * ================================ + * MP-methods + * ================================ + */ + + /* + * Full-path (use MP-method) + */ + public function getParentsMP($depth = null) { + $tableName = $this->owner->tableName(); + $path = $this->owner->getAttribute($this->keyNamePath); + $query = $this->owner->find() + ->andWhere(['<@', "{$tableName}.[[{$this->keyNamePath}]]", $path]); + if ($depth > 0) { + $query->andWhere(['>=', "{$tableName}.[[{$this->keyNameDepth}]]", $this->owner->getAttribute($this->keyNameDepth) - $depth]); + } + $query->andWhere(['<', "{$tableName}.[[{$this->keyNameDepth}]]", $this->owner->getAttribute($this->keyNameDepth)]); + + $orderBy = []; + $orderBy["{$tableName}.[[{$this->keyNameDepth}]]"] = SORT_ASC; + $orderBy["{$tableName}.[[{$this->keyNameId}]]"] = SORT_ASC; + + $query + ->andWhere($this->groupWhere()) + ->addOrderBy($orderBy); + $query->multiple = true; + + return $query; + } + /*public function getParentsMP($depth = null) { + $path = $this->getParentPath(); + if ($path !== null) { + $paths = explode(',', trim($path, '{}')); + if (!$this->primaryKeyMode) { + $path = null; + $paths = array_map( + function ($value) use (&$path) { + return $path = ($path !== null ? $path . ',' : '') . $value; + }, + $paths + ); + } + if ($depth !== null) { + $paths = array_slice($paths, -$depth); + } + } else { + $paths = []; + } + + $tableName = $this->owner->tableName(); + if ($this->primaryKeyMode) { + $condition[] = ["{$tableName}.[[{$this->keyNameId}]]" => $paths]; + } else { + $condition[] = ["{$tableName}.[[{$this->keyNamePath}]]" => $paths]; + } + + $query = $this->owner->find() + ->andWhere($condition) + ->andWhere($this->groupWhere()) + ->addOrderBy(["{$tableName}.[[{$this->keyNamePath}]]" => SORT_ASC]); + $query->multiple = true; + + return $query; + }*/ + + /** + * @param bool $asArray = false + * @return null|string|array + */ + public function getParentPath($asArray = false) + { + return static::getParentPathInternal($this->owner->getAttribute($this->keyNamePath), $asArray); + } + /** + * @return array + */ + protected function groupWhere() + { + $tableName = $this->owner->tableName(); + if ($this->keyNameGroup === null) { + return []; + } else { + return ["{$tableName}.[[{$this->keyNameGroup}]]" => $this->owner->getAttribute($this->keyNameGroup)]; + } + } + + + public function getAllChildrenMP($depth = null, $where = [], $with = null) + { + $tableName = $this->owner->tableName(); + $path = $this->owner->getAttribute($this->keyNamePath); + $query = $this->owner->find() + ->andWhere(['@>', "{$tableName}.[[{$this->keyNamePath}]]", $path]); + + if ($depth > 0) { + $query->andWhere(['<=', "{$tableName}.[[{$this->keyNameDepth}]]", $this->owner->getAttribute($this->keyNameDepth) + $depth]); + } + + $orderBy = []; + $orderBy["{$tableName}.[[{$this->keyNameDepth}]]"] = SORT_ASC; + $orderBy["{$tableName}.[[{$this->keyNameId}]]"] = SORT_ASC; + + if ($where) { + $query->andWhere($where); + } + if ($with) { + $query->with($with); + } + + $query + ->andWhere($this->groupWhere()) + ->addOrderBy($orderBy); + $query->multiple = true; + + return $query; + } + + /* + * ================================ + * AL methods + * ================================ + */ + + /* + * Parent entity (use AL-method) + * @return \yii\db\ActiveRecord + */ + public function getParentAL() { + $parent_id = $this->owner->getAttribute($this->keyNameParentId); + if (empty($parent_id)) + return null; + + $where = [$this->keyNameId => $parent_id]; + if ($this->keyNameGroup) { + $where[$this->keyNameGroup] = $this->owner->getAttribute($this->keyNameGroup); + } + + return $this->owner->find()->where($where)->one(); + } + + /* + * Get parents by AL-method + * @return array + */ + public function getParentsAL() { + $parent_id = $this->owner->getAttribute($this->keyNameParentId); + if ($parent_id == 0) { + return []; + } + + $parent = $this->owner; + $parents = []; + while(true) { + $parent = $parent->getParentAL(); + if (is_null($parent)) + break; + $parents[] = $parent; + } + + return array_reverse($parents); + } + + /* + * Children entities (one-step) (use AL-method) + * @return ActiveQuery + */ + public function getChildrenAL() { + $where = [$this->keyNameParentId => $this->owner->getAttribute($this->keyNameId)]; + if ($this->keyNameGroup) { + $where[$this->keyNameGroup] = $this->owner->getAttribute($this->keyNameGroup); + } + return $this->owner->find()->where($where); + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + /** + * @param array $changedAttributes + * @throws Exception + */ + protected function rebuildChildren($changedAttributes) + { + $path = isset($changedAttributes[$this->keyNamePath]) ? $changedAttributes[$this->keyNamePath] : $this->owner->getAttribute($this->keyNamePath); + $update = []; + $condition = [ + 'and', + ['@>', "[[{$this->keyNamePath}]]", $path, false], + ]; + if ($this->keyNameGroup !== null) { + $group = isset($changedAttributes[$this->keyNameGroup]) ? $changedAttributes[$this->keyNameGroup] : $this->owner->getAttribute($this->keyNameGroup); + $condition[] = [$this->keyNameGroup => $group]; + } + $params = []; + + if (isset($changedAttributes[$this->keyNamePath])) { + $substringExpr = $this->substringExpression( + "[[{$this->keyNamePath}]]", + 'array_length(:pathOld) + 1', + "array_length([[{$this->keyNamePath}]]) - array_length(:pathOld)" + ); + $update[$this->keyNamePath] = new Expression($this->concatExpression([':pathNew', $substringExpr])); + $params[':pathOld'] = $path; + $params[':pathNew'] = $this->owner->getAttribute($this->keyNamePath); + } + + if ($this->keyNameGroup !== null && isset($changedAttributes[$this->keyNameGroup])) { + $update[$this->keyNameGroup] = $this->owner->getAttribute($this->keyNameGroup); + } + + if ($this->keyNameDepth !== null && isset($changedAttributes[$this->keyNameDepth])) { + $delta = $this->owner->getAttribute($this->keyNameDepth) - $changedAttributes[$this->keyNameDepth]; + $update[$this->keyNameDepth] = new Expression("[[{$this->keyNameDepth}]]" . sprintf('%+d', $delta)); + } + if (!empty($update)) { + $this->owner->updateAll($update, $condition, $params); + } + } + + /** + * @param string $path + * @param string $delimiter + * @param bool $asArray = false + * @return null|string|array + */ + protected static function getParentPathInternal($path, $asArray = false) + { + $path = explode(',', trim($path, '{}')); + array_pop($path); + if ($asArray) { + return $path; + } + return count($path) > 0 ? implode(',', $path) : null; + } + + protected function toLike($path) { + return strtr($path . ',', ['%' => '\%', '_' => '\_', '\\' => '\\\\']) . '%'; + } + + protected function concatExpression($items) + { + if ($this->owner->getDb()->driverName === 'sqlite' || $this->owner->getDb()->driverName === 'pgsql') { + return implode(' || ', $items); + } + return 'CONCAT(' . implode(',', $items) . ')'; + } + + protected function substringExpression($string, $from, $length) + { + if ($this->owner->getDb()->driverName === 'sqlite') { + return "SUBSTR({$string}, {$from}, {$length})"; + } + return "SUBSTRING({$string}, {$from}, {$length})"; + } + + // ======================================================= + public function afterInsert() { + $this->withSave(); + $this->owner->updateAttributes([$this->keyNamePath => $this->owner->getAttribute($this->keyNamePath), $this->keyNameDepth => $this->owner->getAttribute($this->keyNameDepth)]); + } + + public function beforeUpdate() + { + if ($this->owner->getIsNewRecord()) { + throw new NotSupportedException('Method "' . $this->owner->className() . '::insert" is not supported for inserting new entitys.'); + } + $this->withSave(); + } + + protected function withSave() { + $id = $this->owner->getAttribute($this->keyNameId); + $parent_id = $this->owner->getAttribute($this->keyNameParentId); + + if (is_null($parent_id)) { + $parent_id = 0; + } + + // check parent_id value is changed! + /*if ($this->owner->getOldAttribute($this->keyNameParentId) == $parent_id) { + return; + }*/ + + // rebuild parents entities + if ($parent_id == 0) { + $depth = 0; + $path = [intval($id)]; + } else { + $parents = $this->getParentsAL(); + $path = []; + $depth = 0; + foreach ($parents as $entity) { + $path[] = $entity->getAttribute($this->keyNameId); + $depth++; + } + $path[] = intval($id); + } + + $path = '{'. implode(',', $path) .'}'; + + // rebuild children entities (recurcive) +// $this->rebuildChildren([ +// $this->keyNamePath => $path +// ]); + + $this->owner->setAttribute($this->keyNamePath, $path); +// $this->owner->setAttribute($this->keyNamePath, $path); + $this->owner->setAttribute($this->keyNameDepth, $depth); + } + + public function recursiveRebuildChildren() { + $children = $this->getChildrenAL()->all(); + $root_path = explode(',', $this->owner->getAttribute($this->keyNamePath)); + $root_depth = $this->owner->getAttribute($this->keyNameDepth); + + /** @var $child ActiveRecord */ + foreach ($children as $child) { + $path = $root_path; + $path[] = $child->getAttribute($this->keyNameId); + $depth = $root_depth + 1; + + $child->recursiveRebuildChildren(); + } + } +} \ No newline at end of file diff --git a/components/artboxtree/ArtboxTreeHelper.php b/components/artboxtree/ArtboxTreeHelper.php new file mode 100755 index 0000000..f1b2d96 --- /dev/null +++ b/components/artboxtree/ArtboxTreeHelper.php @@ -0,0 +1,47 @@ +depth+1) . $value; + $result[$key] = $row; + if (!empty($item['children'])) { + self::recursiveTreeMap($result, $item['children'], $from, $to, $symbol); + } + } + } +} \ No newline at end of file diff --git a/components/artboxtree/ArtboxTreeQueryTrait.php b/components/artboxtree/ArtboxTreeQueryTrait.php new file mode 100755 index 0000000..3440ea1 --- /dev/null +++ b/components/artboxtree/ArtboxTreeQueryTrait.php @@ -0,0 +1,100 @@ +modelClass; + self::$model = new $class; + } + return self::$model; + } + + public function getTree($group = NULL, $with = NULL) + { + $model = $this->getModel(); + if($group !== NULL) { + $this->andWhere([ $model->keyNameGroup => $group ]); + } + if($with) { + $this->with($with); + } + $data = $this->all(); + if(empty( $data )) { + return []; + } + + return $this->buildTree($data); + } + + private function recursiveRebuild($tree, $parentPath = NULL, $depth = 0) + { + $model = $this->getModel(); + + foreach($tree as $row) { + $path = ( is_null($parentPath) ? '' : $parentPath . $model->delimiter ) . $row[ 'item' ]->getAttribute($model->keyNameId); + $row[ 'item' ]->setAttribute($model->keyNamePath, $path); + $row[ 'item' ]->setAttribute($model->keyNameDepth, $depth); + $row[ 'item' ]->save(); + if(!empty( $row[ 'children' ] )) { + $this->recursiveRebuild($row[ 'children' ], $path, $depth + 1); + } + } + } + + /** + * @param int $group + */ + public function rebuildMP($group, $with = NULL) + { + $tree = $this->getTree($group, $with); + + $this->recursiveRebuild($tree); + } + + protected function buildTree(array $data, $parentId = 0) + { + $model = $this->getModel(); + + $result = []; + foreach($data as $element) { + if($element[ $model->keyNameParentId ] == $parentId) { + $children = $this->buildTree($data, $element[ $model->keyNameId ]); + $result[] = [ + 'item' => $element, + 'children' => $children, + ]; + } + } + return $result; + } + + public function normalizeTreeData(array $data, $parentId = NULL) + { + $model = $this->getModel(); + + $result = []; + foreach($data as $element) { + if($element[ $model->keyNameParentId ] == $parentId) { + $result[] = $element; + $children = $this->normalizeTreeData($data, $element[ $model->keyNameId ]); + if($children) { + $result = array_merge($result, $children); + } + } + } + return $result; + } + } \ No newline at end of file diff --git a/components/artboxtree/ArtboxTreeWidget.php b/components/artboxtree/ArtboxTreeWidget.php new file mode 100755 index 0000000..d24ed76 --- /dev/null +++ b/components/artboxtree/ArtboxTreeWidget.php @@ -0,0 +1,138 @@ +dataProvider === null) { + throw new InvalidConfigException('The "dataProvider" property must be set.'); + } + if ($this->keyNameId === null) { + throw new InvalidConfigException('The "keyNameId" property must be set.'); + } + if ($this->formatter == null) { + $this->formatter = Yii::$app->getFormatter(); + } elseif (is_array($this->formatter)) { + $this->formatter = Yii::createObject($this->formatter); + } + if (!$this->formatter instanceof Formatter) { + throw new InvalidConfigException('The "formatter" property must be either a Format object or a configuration array.'); + } + } + + /** + * Runs the widget. + */ + public function run() + { + if (!empty($this->assetBundle) && class_exists($this->assetBundle)) { + $view = $this->getView(); + $assetBundle = $this->assetBundle; + $assetBundle::register($view); + } + if ($this->dataProvider->getCount() == 0) { + return $this->renderEmptyResult(); + } + + parent::run(); + } + + protected function renderEmptyResult() { + return empty($this->emptyResult) ? Yii::t('artbox', 'TreeViewEmptyResult') : Yii::t('artbox', $this->emptyResult); + } + + /** + * Normalize tree data + * @param array $data + * @param string $parentId + * @return array + */ + protected function normalizeTreeData(array $data, $parentId = null) { + $result = []; + foreach ($data as $element) { + if ($element[$this->keyNameParentId] == $parentId) { + $result[] = $element; + $children = $this->normalizeTreeData($data, $element[$this->keyNameId]); + if ($children) { + $result = array_merge($result, $children); + } + } + } + return $result; + } + + /** + * Hierarchy tree data + * @param array $data + * @param string $parentId + * @return array + */ + protected function hierarchyTreeData(array $data, $parentId = null) { + $result = []; + foreach ($data as $element) { + if ($element[$this->keyNameParentId] == $parentId) { + $children = $this->hierarchyTreeData($data, $element[$this->keyNameId]); + $result[] = [ + 'item' => $element, + 'children' => $children + ]; + } + } + return $result; + } +} \ No newline at end of file diff --git a/components/artboxtree/treegrid/TreeGridColumn.php b/components/artboxtree/treegrid/TreeGridColumn.php new file mode 100755 index 0000000..5474766 --- /dev/null +++ b/components/artboxtree/treegrid/TreeGridColumn.php @@ -0,0 +1,250 @@ + + */ +class TreeGridColumn extends Object { + + /** + * @var TreeGrid the grid view object that owns this column. + */ + public $grid; + + /** + * @var string the header cell content. Note that it will not be HTML-encoded. + */ + public $header; + + /** + * @var string the footer cell content. Note that it will not be HTML-encoded. + */ + public $footer; + + /** + * @var callable This is a callable that will be used to generate the content of each cell. + * The signature of the function should be the following: `function ($model, $key, $index, $column)`. + * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered + * and `$column` is a reference to the [[TreeColumn]] object. + */ + public $content; + + /** + * @var boolean whether this column is visible. Defaults to true. + */ + public $visible = true; + + /** + * @var array the HTML attributes for the column group tag. + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public $options = []; + + /** + * @var array the HTML attributes for the header cell tag. + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public $headerOptions = []; + + /** + * @var array|\Closure the HTML attributes for the data cell tag. This can either be an array of + * attributes or an anonymous function ([[Closure]]) that returns such an array. + * The signature of the function should be the following: `function ($model, $key, $index, $column)`. + * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered + * and `$column` is a reference to the [[Column]] object. + * A function may be used to assign different attributes to different rows based on the data in that row. + * + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public $contentOptions = []; + + /** + * @var array the HTML attributes for the footer cell tag. + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public $footerOptions = []; + + /** + * @var string the attribute name associated with this column. When neither [[content]] nor [[value]] + * is specified, the value of the specified attribute will be retrieved from each data model and displayed. + * + * Also, if [[label]] is not specified, the label associated with the attribute will be displayed. + */ + public $attribute; + + /** + * @var string label to be displayed in the [[header|header cell]] and also to be used as the sorting + * link label when sorting is enabled for this column. + * If it is not set and the models provided by the GridViews data provider are instances + * of [[\yii\db\ActiveRecord]], the label will be determined using [[\yii\db\ActiveRecord::getAttributeLabel()]]. + * Otherwise [[\yii\helpers\Inflector::camel2words()]] will be used to get a label. + */ + public $label; + + /** + * @var boolean whether the header label should be HTML-encoded. + * @see label + */ + public $encodeLabel = true; + + /** + * @var string|\Closure an anonymous function or a string that is used to determine the value to display in the current column. + * + * If this is an anonymous function, it will be called for each row and the return value will be used as the value to + * display for every data model. The signature of this function should be: `function ($model, $key, $index, $column)`. + * Where `$model`, `$key`, and `$index` refer to the model, key and index of the row currently being rendered + * and `$column` is a reference to the [[DataColumn]] object. + * + * You may also set this property to a string representing the attribute name to be displayed in this column. + * This can be used when the attribute to be displayed is different from the [[attribute]] that is used for + * sorting and filtering. + * + * If this is not set, `$model[$attribute]` will be used to obtain the value, where `$attribute` is the value of [[attribute]]. + */ + public $value; + + /** + * @var string|array in which format should the value of each data model be displayed as (e.g. `"raw"`, `"text"`, `"html"`, + * `['date', 'php:Y-m-d']`). Supported formats are determined by the [[GridView::formatter|formatter]] used by + * the [[GridView]]. Default format is "text" which will format the value as an HTML-encoded plain text when + * [[\yii\i18n\Formatter]] is used as the [[GridView::$formatter|formatter]] of the GridView. + */ + public $format = 'text'; + + /** + * Renders the header cell. + */ + public function renderHeaderCell() + { + return Html::tag('th', $this->renderHeaderCellContent(), $this->headerOptions); + } + + /** + * Renders the footer cell. + */ + public function renderFooterCell() + { + return Html::tag('td', $this->renderFooterCellContent(), $this->footerOptions); + } + + /** + * Renders a data cell. + * @param mixed $model the data model being rendered + * @param mixed $key the key associated with the data model + * @param integer $index the zero-based index of the data item among the item array returned by [[GridView::dataProvider]]. + * @return string the rendering result + */ + public function renderDataCell($model, $key, $index, $is_first = false, $symbol = '–') + { + if ($this->contentOptions instanceof Closure) { + $options = call_user_func($this->contentOptions, $model, $key, $index, $this); + } else { + $options = $this->contentOptions; + } + return Html::tag('td', ($is_first ? str_repeat($symbol, $model->depth) : '') . $this->renderDataCellContent($model, $key, $index), $options); + } + + /** + * Renders the header cell content. + * The default implementation simply renders [[header]]. + * This method may be overridden to customize the rendering of the header cell. + * @return string the rendering result + */ + protected function renderHeaderCellContent() + { + if ($this->header !== null || $this->label === null && $this->attribute === null) { + return trim($this->header) !== '' ? $this->header : $this->grid->emptyCell; + } + + $provider = $this->grid->dataProvider; + + if ($this->label === null) { + if ($provider instanceof ActiveDataProvider && $provider->query instanceof ActiveQueryInterface) { + /* @var $model Model */ + $model = new $provider->query->modelClass; + $label = $model->getAttributeLabel($this->attribute); + } else { + $models = $provider->getModels(); + if (($model = reset($models)) instanceof Model) { + /* @var $model Model */ + $label = $model->getAttributeLabel($this->attribute); + } else { + $label = Inflector::camel2words($this->attribute); + } + } + } else { + $label = $this->label; + } + + return $this->encodeLabel ? Html::encode($label) : $label; + } + + /** + * Renders the footer cell content. + * The default implementation simply renders [[footer]]. + * This method may be overridden to customize the rendering of the footer cell. + * @return string the rendering result + */ + protected function renderFooterCellContent() + { + return trim($this->footer) !== '' ? $this->footer : $this->grid->emptyCell; + } + + /** + * Renders the data cell content. + * @param mixed $model the data model + * @param mixed $key the key associated with the data model + * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]]. + * @return string the rendering result + */ + protected function renderDataCellContent($model, $key, $index) + { + if ($this->content === null) { + return $this->grid->formatter->format($this->getDataCellValue($model, $key, $index), $this->format); + } else { + if ($this->content !== null) { + return call_user_func($this->content, $model, $key, $index, $this); + } else { + return $this->grid->emptyCell; + } + } + + + } + + /** + * Returns the data cell value. + * @param mixed $model the data model + * @param mixed $key the key associated with the data model + * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]]. + * @return string the data cell value + */ + public function getDataCellValue($model, $key, $index) + { + if ($this->value !== null) { + if (is_string($this->value)) { + return ArrayHelper::getValue($model, $this->value); + } else { + return call_user_func($this->value, $model, $key, $index, $this); + } + } elseif ($this->attribute !== null) { + return ArrayHelper::getValue($model, $this->attribute); + } + return null; + } + +} \ No newline at end of file diff --git a/components/artboxtree/treegrid/TreeGridWidget.php b/components/artboxtree/treegrid/TreeGridWidget.php new file mode 100755 index 0000000..9996437 --- /dev/null +++ b/components/artboxtree/treegrid/TreeGridWidget.php @@ -0,0 +1,277 @@ + 'table table-striped table-bordered']; + + /** + * @var array The plugin options + */ + public $pluginOptions = []; + + /** + * @var boolean whether to show the grid view if [[dataProvider]] returns no data. + */ + public $showOnEmpty = true; + + public $rowOptions = []; + + /** + * @var Closure an anonymous function that is called once BEFORE rendering each data model. + * It should have the similar signature as [[rowOptions]]. The return result of the function + * will be rendered directly. + */ + public $beforeRow; + + /** + * @var Closure an anonymous function that is called once AFTER rendering each data model. + * It should have the similar signature as [[rowOptions]]. The return result of the function + * will be rendered directly. + */ + public $afterRow; + + /** + * @var boolean whether to show the header section of the grid table. + */ + public $showHeader = true; + + /** + * @var array the HTML attributes for the table header row. + * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. + */ + public $headerRowOptions = []; + + /** + * @var boolean whether to show the footer section of the grid table. + */ + public $showFooter = false; + + /** + * @var string the HTML display when the content of a cell is empty + */ + public $emptyCell = ' '; + + public $levelSymbol = '–'; + + /** + * Init the widget object. + */ + public function init() { + parent::init(); + + $this->initColumns(); + } + + /** + * Runs the widget. + */ + public function run() { + $run = parent::run(); + if (!is_null($run)) + return $run; + + if ($this->showOnEmpty || $this->dataProvider->getCount() > 0) { + $pagination = $this->dataProvider->getPagination(); + $pagination->setPageSize($this->dataProvider->getTotalCount()); + + $header = $this->showHeader ? $this->renderTableHeader() : false; + $body = $this->renderItems(); + $footer = $this->showFooter ? $this->renderTableFooter() : false; + + $content = array_filter([ + $header, + $body, + $footer + ]); + + return Html::tag('table', implode("\n", $content), $this->options); + } else { + return $this->renderEmptyResult(); + } + } + + /** + * Renders the table header. + * @return string the rendering result. + */ + public function renderTableHeader() + { + $cells = []; + foreach ($this->columns as $column) { + /* @var $column TreeGridColumn */ + $cells[] = $column->renderHeaderCell(); + } + $content = Html::tag('tr', implode('', $cells), $this->headerRowOptions); + return "\n" . $content . "\n"; + } + + /** + * Renders the table footer. + * @return string the rendering result. + */ + public function renderTableFooter() + { + $cells = []; + foreach ($this->columns as $column) { + /* @var $column TreeGridColumn */ + $cells[] = $column->renderFooterCell(); + } + $content = Html::tag('tr', implode('', $cells), $this->footerRowOptions); + return "\n" . $content . "\n"; + } + + /** + * Renders the data models for the grid view. + */ + public function renderItems() + { + $rows = []; + $models = array_values($this->dataProvider->getModels()); + $keys = $this->dataProvider->getKeys(); + $models = TaxOption::find()->normalizeTreeData($models, $this->rootParentId); + foreach ($models as $index => $model) { + $key = $keys[$index]; + if ($this->beforeRow !== null) { + $row = call_user_func($this->beforeRow, $model, $key, $index, $this); + if (!empty($row)) { + $rows[] = $row; + } + } + + $rows[] = $this->renderTableRow($model, $key, $index); + + if ($this->afterRow !== null) { + $row = call_user_func($this->afterRow, $model, $key, $index, $this); + if (!empty($row)) { + $rows[] = $row; + } + } + } + + if (empty($rows)) { + $colspan = count($this->columns); + return "" . $this->renderEmpty() . ""; + } else { + return implode("\n", $rows); + } + } + + /** + * Renders a table row with the given data model and key. + * @param mixed $model the data model to be rendered + * @param mixed $key the key associated with the data model + * @param integer $index the zero-based index of the data model among the model array returned by [[dataProvider]]. + * @return string the rendering result + */ + public function renderTableRow($model, $key, $index) + { + $cells = []; + /* @var $column TreeGridColumn */ + $i = 0; + foreach ($this->columns as $column) { + $cells[] = $column->renderDataCell($model, $key, $index, $i == 0, $this->levelSymbol); + $i++; + } + if ($this->rowOptions instanceof Closure) { + $options = call_user_func($this->rowOptions, $model, $key, $index, $this); + } else { + $options = $this->rowOptions; + } + $options['data-key'] = is_array($key) ? json_encode($key) : (string) $key; + + $id = ArrayHelper::getValue($model, $this->keyNameId); + Html::addCssClass($options, "treegrid-$id"); + + $parentId = ArrayHelper::getValue($model, $this->keyNameParentId); + if ($parentId) { + Html::addCssClass($options, "treegrid-parent-$parentId"); + } + + return Html::tag('tr', implode('', $cells), $options); + } + + /** + * Creates column objects and initializes them. + */ + protected function initColumns() + { + if (empty($this->columns)) { + $this->guessColumns(); + } + foreach ($this->columns as $i => $column) { + if (is_string($column)) { + $column = $this->createDataColumn($column); + } else { + $column = Yii::createObject(array_merge([ + 'class' => $this->dataColumnClass ? : TreeGridColumn::className(), + 'grid' => $this, + ], $column)); + } + if (!$column->visible) { + unset($this->columns[$i]); + continue; + } + $this->columns[$i] = $column; + } + } + + /** + * Creates a [[DataColumn]] object based on a string in the format of "attribute:format:label". + * @param string $text the column specification string + * @return DataColumn the column instance + * @throws InvalidConfigException if the column specification is invalid + */ + protected function createDataColumn($text) + { + if (!preg_match('/^([^:]+)(:(\w*))?(:(.*))?$/', $text, $matches)) { + throw new InvalidConfigException('The column must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"'); + } + + return Yii::createObject([ + 'class' => $this->dataColumnClass ? : TreeGridColumn::className(), + 'grid' => $this, + 'attribute' => $matches[1], + 'format' => isset($matches[3]) ? $matches[3] : 'text', + 'label' => isset($matches[5]) ? $matches[5] : null, + ]); + } + + /** + * This function tries to guess the columns to show from the given data + * if [[columns]] are not explicitly specified. + */ + protected function guessColumns() + { + $models = $this->dataProvider->getModels(); + $model = reset($models); + if (is_array($model) || is_object($model)) { + foreach ($model as $name => $value) { + $this->columns[] = $name; + } + } + } +} \ No newline at end of file diff --git a/components/artboxtree/treelist/TreeListWidget.php b/components/artboxtree/treelist/TreeListWidget.php new file mode 100755 index 0000000..a752c2c --- /dev/null +++ b/components/artboxtree/treelist/TreeListWidget.php @@ -0,0 +1,62 @@ +hierarchyTreeData(array_values($this->dataProvider->getModels()), $this->rootParentId); + return $this->renderTreelist($models); + } + + protected function renderTreelist($models) { + foreach ($models as $index => $model) { + $row = $this->renderTreelistItem($model['item']); + $children = empty($model['children']) ? '' : $this->renderTreelist($model['children']); + $output[] = '
  • '. $row . $children .'
  • '; + } + + if (!empty($output)) + return ''; + } + + protected function renderTreelistItem($model) + { + $options = []; + $id = ArrayHelper::getValue($model, $this->keyNameId); + Html::addCssClass($options, "treelistitem-$id"); + + $parent_id = ArrayHelper::getValue($model, $this->keyNameParentId); + if ($parent_id) { + Html::addCssClass($options, "treelistitem-parent-$parent_id"); + } + +// if (is_string($this->value)) { +// return ArrayHelper::getValue($model, $this->value); +// } else { +// return call_user_func($this->value, $model, $key, $index, $this); +// } + + return Html::tag('span', ArrayHelper::getValue($model, $this->displayField), $options); + } +} \ No newline at end of file diff --git a/components/artboxtree/treemenu/TreeMenuWidget.php b/components/artboxtree/treemenu/TreeMenuWidget.php new file mode 100755 index 0000000..f654293 --- /dev/null +++ b/components/artboxtree/treemenu/TreeMenuWidget.php @@ -0,0 +1,62 @@ +hierarchyTreeData(array_values($this->dataProvider->getModels()), $this->rootParentId); + return $this->renderTreelist($models); + } + + protected function renderTreelist($models) { + foreach ($models as $index => $model) { + $row = $this->renderTreelistItem($model['item']); + $children = empty($model['children']) ? '' : $this->renderTreelist($model['children']); + $output[] = '
  • '. $row . $children .'
  • '; + } + + if (!empty($output)) + return ''; + } + + protected function renderTreelistItem($model) + { + $options = []; + $id = ArrayHelper::getValue($model, $this->keyNameId); + Html::addCssClass($options, "treelistitem-$id"); + + $parent_id = ArrayHelper::getValue($model, $this->keyNameParentId); + if ($parent_id) { + Html::addCssClass($options, "treelistitem-parent-$parent_id"); + } + +// if (is_string($this->value)) { +// return ArrayHelper::getValue($model, $this->value); +// } else { +// return call_user_func($this->value, $model, $key, $index, $this); +// } + + return Html::tag('span', ArrayHelper::getValue($model, $this->displayField), $options); + } +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..d7ce866 --- /dev/null +++ b/composer.json @@ -0,0 +1,15 @@ +{ + "name": "artweb/artbox", + "description": "Yii2 light-weight CMS", + "license": "BSD-3-Clause", + "require": { + "php": ">=7.0", + "yiisoft/yii2": "*", + "developeruz/yii2-db-rbac": "*" + }, + "autoload": { + "psr-4": { + "artweb\\artbox\\": "" + } + } +} \ No newline at end of file diff --git a/console/GenerateController.php b/console/GenerateController.php new file mode 100644 index 0000000..7217bfe --- /dev/null +++ b/console/GenerateController.php @@ -0,0 +1,285 @@ + 'ru_RU', + 3 => 'uk_UA', + ]; + + /** + * Faker Generators instances + * + * @var Generator[] $fakers + */ + private $fakers = []; + + /** + * Create faker Generators for all $locales + * + * @param \yii\base\Action $action + * + * @return bool + */ + public function beforeAction($action) + { + $parent = parent::beforeAction($action); + $fakers = []; + foreach ($this->locales as $locale_id => $locale) { + $fakers[ $locale_id ] = Factory::create($locale); + } + $this->fakers = $fakers; + return $parent; + } + + /** + * Generate fake Brands + * + * @param int $count Brands count + * + * @return int + */ + public function actionBrand(int $count = 10): int + { + /** + * @var Brand[] $models + */ + $models = []; + $fakers = $this->fakers; + for ($i = 0; $i < $count; $i++) { + $models[ $i ] = \Yii::createObject(Brand::className()); + $models[ $i ]->generateLangs(); + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) { + $modelLang->title = $fakers[ $language_id ]->company; + } + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) { + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW); + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW); + echo "Brand '$title' inserted under $id ID.\n"; + }; + } + return 0; + } + + /** + * Generate fake categories + * + * @param int $count Category count + * + * @return int + */ + public function actionCategory(int $count = 10):int + { + /** + * @var Category[] $models + */ + $models = []; + $fakers = $this->fakers; + for ($i = 0; $i < $count; $i++) { + $models[ $i ] = \Yii::createObject( + [ + 'class' => Category::className(), + 'depth' => 0, + 'parent_id' => 0, + ] + ); + $models[ $i ]->generateLangs(); + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) { + $modelLang->title = ucfirst($fakers[ $language_id ]->word); + } + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) { + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW); + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW); + echo "Category '$title' inserted under $id ID.\n"; + }; + } + return 0; + } + + /** + * Generate fake products with variants, categories and tax options + * + * @param int $count Product count + * + * @return int + */ + public function actionProduct(int $count = 10):int + { + /** + * @var Product[] $models + */ + $models = []; + $fakers = $this->fakers; + $brands = Brand::find() + ->limit(20) + ->asArray() + ->column(); + $categories = Category::find() + ->limit(100) + ->asArray() + ->column(); + $product_options = TaxOption::find() + ->joinWith('taxGroup') + ->where([ 'tax_group.level' => TaxGroup::GROUP_PRODUCT ]) + ->limit(50) + ->asArray() + ->column(); + $variant_options = TaxOption::find() + ->joinWith('taxGroup') + ->where([ 'tax_group.level' => TaxGroup::GROUP_VARIANT ]) + ->limit(50) + ->asArray() + ->column(); + for ($i = 0; $i < $count; $i++) { + $models[ $i ] = \Yii::createObject( + [ + 'class' => Product::className(), + 'brand_id' => $fakers[ 2 ]->randomElement($brands), + ] + ); + $models[ $i ]->setCategories( + $fakers[ 2 ]->randomElements($categories, $fakers[ 2 ]->randomDigitNotNull) + ); + $models[ $i ]->setOptions( + $fakers[ 2 ]->randomElements($product_options, $fakers[ 2 ]->randomDigitNotNull) + ); + $models[ $i ]->generateLangs(); + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) { + $modelLang->title = ucfirst($fakers[ $language_id ]->word); + } + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) { + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW); + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW); + echo "Product '$title' inserted under $id ID.\n"; + $variant_count = $fakers[ 2 ]->numberBetween(4, 10); + for ($j = 0; $j < $variant_count; $j++) { + /** + * @var ProductVariant $variant + */ + $variant = \Yii::createObject( + [ + 'class' => ProductVariant::className(), + 'product_id' => $models[ $i ]->id, + 'sku' => $fakers[ 2 ]->uuid, + 'price' => $fakers[ 2 ]->randomFloat(2, 100, 10000), + 'stock' => 10, + 'product_unit_id' => 1, + ] + ); + $variant->setOptions( + $fakers[ 2 ]->randomElements($variant_options, $fakers[ 2 ]->randomDigitNotNull) + ); + $variant->generateLangs(); + foreach ($variant->modelLangs as $variant_language_id => $variantLang) { + $variantLang->title = ucfirst($fakers[ $variant_language_id ]->word); + } + if ($variant->save() && $variant->transactionStatus) { + $variant_title = $this->ansiFormat($variant->modelLangs[ 2 ]->title, Console::FG_YELLOW); + $variant_id = $this->ansiFormat($variant->id, Console::FG_YELLOW); + echo "Variant '$variant_title' inserted under $variant_id ID for product $title.\n"; + } + } + }; + } + return 0; + } + + /** + * Generate fake tax groups with tax options. + * + * @param int $count_product Tax Groups for Product + * @param int $count_variant Tax Groups for ProductVariant + * + * @return int + */ + public function actionTax(int $count_product = 10, int $count_variant = 10):int + { + $categories = Category::find() + ->asArray() + ->column(); + $this->generateTax(TaxGroup::GROUP_PRODUCT, $count_product, $categories); + $this->generateTax(TaxGroup::GROUP_VARIANT, $count_variant, $categories); + return 0; + } + + /** + * Generates tax groups amd tax options for actionTax + * + * @param int $level Tax Group for Product or ProductVariant + * @param int $count Tax Group count + * @param array $categories Categories for Tax Groups + * + * @see GenerateController::actionTax() + */ + private function generateTax(int $level, int $count, array $categories = []) + { + $count_option = 10; + $fakers = $this->fakers; + /** + * @var TaxGroup[] $models + */ + $models = []; + for ($i = 0; $i < $count; $i++) { + $models[ $i ] = \Yii::createObject( + [ + 'class' => TaxGroup::className(), + 'is_filter' => true, + 'display' => true, + ] + ); + $models[ $i ]->level = $level; + $models[ $i ]->setCategories($categories); + $models[ $i ]->generateLangs(); + foreach ($models[ $i ]->modelLangs as $language_id => $modelLang) { + $modelLang->title = ucfirst($fakers[ $language_id ]->word); + } + if ($models[ $i ]->save() && $models[ $i ]->transactionStatus) { + for ($j = 0; $j < $count_option; $j++) { + /** + * @var TaxOption $option + */ + $option = \Yii::createObject( + TaxOption::className() + ); + $option->tax_group_id = $models[ $i ]->id; + $option->generateLangs(); + foreach ($option->modelLangs as $option_language_id => $taxOptionLang) { + $taxOptionLang->value = ucfirst($fakers[ $option_language_id ]->word); + } + $option->save(); + } + $title = $this->ansiFormat($models[ $i ]->modelLangs[ 2 ]->title, Console::FG_YELLOW); + $id = $this->ansiFormat($models[ $i ]->id, Console::FG_YELLOW); + $element = $this->ansiFormat( + ( $level === TaxGroup::GROUP_PRODUCT ) ? 'Product' : 'Variant', + Console::FG_RED + ); + echo "Category '$title' inserted for $element under $id ID.\n"; + }; + } + } + } + \ No newline at end of file diff --git a/console/ImportController.php b/console/ImportController.php new file mode 100755 index 0000000..445a60c --- /dev/null +++ b/console/ImportController.php @@ -0,0 +1,57 @@ +stderr('Task already executed'); + return Controller::EXIT_CODE_ERROR; + } + return fopen ($filename, 'r'); + } + + public function actionProducts() { +// if (file_exists(Yii::getAlias('@uploadDir/goProducts.lock'))) { +// $this->errors[] = 'Task already executed'; +// return Controller::EXIT_CODE_ERROR; +// } +// $ff = fopen(Yii::getAlias('@uploadDir/goProducts.lock'), 'w+'); +// fclose($ff); + $model = new Import(); + $model->goProducts(0, null); +// unlink(Yii::getAlias('@uploadDir/goProducts.lock')); + return Controller::EXIT_CODE_NORMAL; + } + + public function actionPrices() { + if (file_exists(Yii::getAlias('@uploadDir/goPrices.lock'))) { + $this->stderr('Task already executed'); + return Controller::EXIT_CODE_ERROR; + } + $ff = fopen(Yii::getAlias('@uploadDir/goPrices.lock'), 'w+'); + fclose($ff); + $model = new Import(); + $data = $model->goPrices(0, null); + unlink(Yii::getAlias('@uploadDir/goPrices.lock')); + return Controller::EXIT_CODE_NORMAL; + } + + private function saveNotFoundRecord (array $line, $filename) + { + $str = implode (';', $line)."\n"; + $str = iconv ("UTF-8//TRANSLIT//IGNORE", "windows-1251", $str); + + $fg = fopen (Yii::getAlias('@uploadDir') .'/'. $filename, 'a+'); + fputs ($fg, $str); + fclose ($fg); + } +} \ No newline at end of file diff --git a/console/SiteMapController.php b/console/SiteMapController.php new file mode 100755 index 0000000..842c2f1 --- /dev/null +++ b/console/SiteMapController.php @@ -0,0 +1,220 @@ +search($category, $filter); + if(!empty($productProvider->models)){ + return true; + } else { + return false; + } + } + + + + public function getAddStatic(){ + return [ + 'http://www.rukzachok.com.ua', + 'http://www.rukzachok.com.ua/catalog' + ]; + } + + + public function getProducts() { + return Product::find()->all(); + + } + + + public function getSeoLinks() { + return Seo::find()->where(['meta' => ''])->all(); + + } + + public function getStaticPages(){ + return Page::find()->all(); + } + + + public function getCategories(){ + return Category::find()->all(); + } + + + public function getArticles(){ + return Article::find()->all(); + } + + public function getBrands($category){ + + return $category->brands; + } + + /** + * @param $category Category; + * @return mixed + */ + + public function getFilters($category){ + + return $category->getActiveFilters()->all(); + + } + + + public function checkUrl($url){ + if(!in_array($url, $this->urlList)){ + $this->urlList[] = $url; + return true; + } else { + return false; + } + } + + + public function createRow( $url, $priority, &$content ){ + if($this->checkUrl($url)){ + print $this->count++ . "\n"; + $content .= '' . + '' . $url . '' . + '' . date('Y-m-d') . '' . + 'Daily' . + '' . $priority .'' . + ''; + } + } + + + public function actionProcess() { + + $config = ArrayHelper::merge( + require(__DIR__ . '/../../frontend/config/main.php'), + require(__DIR__ . '/../../common/config/main.php') + + ); + + Yii::$app->urlManager->addRules($config['components']['urlManager']['rules']); + + + + $dirName = Yii::getAlias('@frontend').'/web'; + + $filename = 'sitemap.xml'; + + setlocale(LC_ALL, 'ru_RU.CP1251'); + $handle = fopen($dirName .'/'. $filename, "w"); + + $content = ''; + + foreach ($this->getAddStatic() as $page) { + $this->createRow($page , 1,$content); + } + + foreach ($this->getStaticPages() as $page) { + $url = Url::to(['text/index','translit' => $page->translit]); + $this->createRow($url , 1,$content); + } + + foreach ($this->getCategories() as $category) { + $url = Url::to(['catalog/category', 'category' => $category]); + $this->createRow($url , 1,$content); + } + + + foreach ($this->getProducts() as $product) { + + $url = Url::to(['catalog/product', 'product' => $product]); + $this->createRow($url , 0.9, $content); + } + + + foreach ($this->getArticles() as $article) { + + $url = Url::to(['articles/show', 'translit' => $article->translit, 'id' => $article->id,]); + $this->createRow($url , 0.8,$content); + + } + + + foreach($this->getCategories() as $category){ + foreach ($this->getBrands($category) as $brand) { + if($this->checkFilter($category, ['brands' => [$brand->id]])){ + $url = Url::to(['catalog/category', 'category' => $category, 'filters' => ['brands' => [$brand->alias]]]) ; + $this->createRow($url , 0.8, $content); + } + } + } + + + foreach($this->getCategories() as $category){ + foreach ($this->getFilters($category) as $filter) { + if($this->checkFilter($category, [$filter['group_alias'] => [$filter['option_alias']]])){ + $url = Url::to(['catalog/category', 'category' => $category, 'filters' => [$filter['group_alias'] => [$filter['option_alias']]] ]); + $this->createRow($url , 0.8, $content); + } + + } + } + + foreach($this->getSeoLinks() as $link){ + $url = Yii::$app->urlManager->baseUrl.$link->url; + $this->createRow($url , 0.7, $content); + + } + + + +// foreach($this->getCategories() as $category){ +// foreach ($this->getFilters($category) as $filter1) { +// foreach ($this->getFilters($category) as $filter2) { +// if($this->checkFilter($category, [$filter1['group_alias'] => [$filter1['option_alias']],$filter2['group_alias'] => [$filter2['option_alias']]] )){ +// $url = Url::to(['catalog/category', 'category' => $category, 'filters' => [$filter1['group_alias'] => [$filter1['option_alias']],$filter2['group_alias'] => [$filter2['option_alias']]] ]); +// $this->createRow($url , 0.7, $content); +// } +// +// } +// +// foreach ($this->getBrands($category) as $brand) { +// if($this->checkFilter($category, ['brands' => [$brand->id], $filter1['group_alias'] => [$filter1['option_alias']]] )){ +// $url = Url::to(['catalog/category', 'category' => $category, 'filters' => ['brands' => [$brand->alias],$filter1['group_alias'] => [$filter1['option_alias']]]]); +// $this->createRow($url , 0.7,$content); +// } +// +// } +// } +// } + + + + $content .= ''; + + fwrite($handle, $content); + fclose($handle); + + print $dirName .'/'. $filename; + } + +} diff --git a/models/Customer.php b/models/Customer.php new file mode 100755 index 0000000..92e2372 --- /dev/null +++ b/models/Customer.php @@ -0,0 +1,160 @@ + '10', + ], + [ + [ + 'username', + 'name', + 'surname', + 'phone', + 'email', + 'password_reset_token', + ], + 'string', + 'max' => 255, + ], + [ + [ + 'gender', + 'auth_key', + ], + 'string', + 'max' => 32, + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('app', 'id'), + 'username' => Yii::t('app', 'username'), + 'name' => Yii::t('app', 'cname'), + 'surname' => Yii::t('app', 'surname'), + 'phone' => Yii::t('app', 'phone'), + 'gender' => Yii::t('app', 'gender'), + 'birth_day' => Yii::t('app', 'birth_day'), + 'birth_month' => Yii::t('app', 'birth_month'), + 'birth_year' => Yii::t('app', 'birth_year'), + 'body' => Yii::t('app', 'body'), + 'group_id' => Yii::t('app', 'group_id'), + 'email' => Yii::t('app', 'email'), + 'auth_key' => Yii::t('app', 'auth_key'), + 'password_reset_token' => Yii::t('app', 'password_reset_token'), + 'status' => Yii::t('app', 'status'), + 'created_at' => Yii::t('app', 'created_at'), + 'updated_at' => Yii::t('app', 'updated_at'), + ]; + } + + /** + * Finds user by email + * + * @param string $email + * + * @return static|null + */ + public static function findByEmail($email) + { + return static::findOne( + [ + 'email' => $email, + 'status' => self::STATUS_ACTIVE, + ] + ); + } + + /** + * Get full name + * + * @return string + */ + public function getName() + { + return $this->username . ' ' . $this->surname; + } + + public function getPassword() + { + return false; + } + + } diff --git a/models/CustomerSearch.php b/models/CustomerSearch.php new file mode 100755 index 0000000..b81e8d9 --- /dev/null +++ b/models/CustomerSearch.php @@ -0,0 +1,128 @@ + $query, + ] + ); + + $this->load($params); + + if (!$this->validate()) { + // uncomment the following line if you do not want to return any records when validation fails + // $query->where('0=1'); + return $dataProvider; + } + + // grid filtering conditions + $query->andFilterWhere( + [ + 'id' => $this->id, + 'birth_day' => $this->birth_day, + 'birth_month' => $this->birth_month, + 'birth_year' => $this->birth_year, + 'group_id' => $this->group_id, + ] + ); + + $query->andFilterWhere( + [ + 'like', + 'username', + $this->username, + ] + ) + ->andFilterWhere( + [ + 'like', + 'name', + $this->name, + ] + ) + ->andFilterWhere( + [ + 'like', + 'surname', + $this->surname, + ] + ) + ->andFilterWhere( + [ + 'like', + 'phone', + $this->phone, + ] + ) + ->andFilterWhere( + [ + 'like', + 'body', + $this->body, + ] + ); + + return $dataProvider; + } + } diff --git a/models/Feedback.php b/models/Feedback.php new file mode 100755 index 0000000..beecd3a --- /dev/null +++ b/models/Feedback.php @@ -0,0 +1,116 @@ + [ + 'name', + 'phone', + ], + self::SCENARIO_CALLBACK => [ 'phone' ], + ] + ); + return $scenarios; + } + + /** + * @inheritdoc + */ + public function behaviors() + { + return [ + [ + 'class' => TimestampBehavior::className(), + 'updatedAtAttribute' => false, + ], + [ + 'class' => AttributeBehavior::className(), + 'attributes' => [ + ActiveRecord::EVENT_BEFORE_INSERT => 'ip', + ], + 'value' => function ($event) { + return \Yii::$app->request->userIP; + }, + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ + 'phone', + 'name', + ], + 'required', + ], + [ + [ 'phone' ], + 'match', + 'pattern' => '/^\+38\(\d{3}\)\d{3}-\d{2}-\d{2}$/', + ], + [ + [ + 'name', + 'phone', + ], + 'string', + 'max' => 255, + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('app', 'id'), + 'name' => Yii::t('app', 'name'), + 'phone' => Yii::t('app', 'phone'), + 'created_at' => Yii::t('app', 'created_at'), + 'ip' => Yii::t('app', 'ip'), + ]; + } + } diff --git a/models/FeedbackSearch.php b/models/FeedbackSearch.php new file mode 100755 index 0000000..a5b052c --- /dev/null +++ b/models/FeedbackSearch.php @@ -0,0 +1,98 @@ + $query, + ] + ); + + $this->load($params); + + if (!$this->validate()) { + // uncomment the following line if you do not want to return any records when validation fails + // $query->where('0=1'); + return $dataProvider; + } + + // grid filtering conditions + $query->andFilterWhere( + [ + 'id' => $this->id, + ] + ); + + $query->andFilterWhere( + [ + 'like', + 'name', + $this->name, + ] + ) + ->andFilterWhere( + [ + 'like', + 'phone', + $this->phone, + ] + ) + ->andFilterWhere( + [ + 'like', + 'ip', + $this->ip, + ] + ); + + return $dataProvider; + } + } diff --git a/models/Page.php b/models/Page.php new file mode 100755 index 0000000..6b18009 --- /dev/null +++ b/models/Page.php @@ -0,0 +1,91 @@ + [ + 'class' => LanguageBehavior::className(), + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'title' ], + 'safe', + ], + [ + [ + 'in_menu', + ], + 'boolean', + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('app', 'id'), + 'in_menu' => Yii::t('app', 'in_menu'), + ]; + } + } diff --git a/models/PageLang.php b/models/PageLang.php new file mode 100755 index 0000000..e4c4b20 --- /dev/null +++ b/models/PageLang.php @@ -0,0 +1,148 @@ + [ + 'class' => 'common\behaviors\Slug', + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ + 'title', + 'body', + ], + 'required', + ], + [ + [ + 'body', + 'seo_text', + ], + 'string', + ], + [ + [ + 'title', + 'meta_title', + 'meta_keywords', + 'meta_description', + 'h1', + 'alias', + ], + 'string', + 'max' => 255, + ], + [ + [ + 'page_id', + 'language_id', + ], + 'unique', + 'targetAttribute' => [ + 'page_id', + 'language_id', + ], + 'message' => 'The combination of Page ID and Language ID has already been taken.', + ], + [ + [ 'language_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Language::className(), + 'targetAttribute' => [ 'language_id' => 'id' ], + ], + [ + [ 'page_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Page::className(), + 'targetAttribute' => [ 'page_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'page_id' => Yii::t('app', 'page_id'), + 'language_id' => Yii::t('app', 'language_id'), + 'title' => Yii::t('app', 'title'), + 'body' => Yii::t('app', 'body'), + 'meta_title' => Yii::t('app', 'meta_title'), + 'meta_keywords' => Yii::t('app', 'meta_keywords'), + 'meta_description' => Yii::t('app', 'meta_description'), + 'seo_text' => Yii::t('app', 'seo_text'), + 'h1' => Yii::t('app', 'h1'), + 'alias' => Yii::t('app', 'alias'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getLanguage() + { + return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getPage() + { + return $this->hasOne(Page::className(), [ 'id' => 'page_id' ]); + } + } diff --git a/models/PageSearch.php b/models/PageSearch.php new file mode 100755 index 0000000..d13e26e --- /dev/null +++ b/models/PageSearch.php @@ -0,0 +1,106 @@ +joinWith('lang'); + + // add conditions that should always apply here + + $dataProvider = new ActiveDataProvider( + [ + 'query' => $query, + 'sort' => [ + 'attributes' => [ + 'title' => [ + 'asc' => [ 'page_lang.title' => SORT_ASC ], + 'desc' => [ 'page_lang.title' => SORT_DESC ], + ], + 'id', + 'in_menu', + ], + ], + ] + ); + + $this->load($params); + + if (!$this->validate()) { + // uncomment the following line if you do not want to return any records when validation fails + // $query->where('0=1'); + return $dataProvider; + } + + // grid filtering conditions + $query->andFilterWhere( + [ + 'id' => $this->id, + 'in_menu' => $this->in_menu, + ] + ) + ->andFilterWhere( + [ + 'like', + 'page_lang.title', + $this->title, + ] + ); + + return $dataProvider; + } + } diff --git a/translation/ru/app.php b/translation/ru/app.php new file mode 100755 index 0000000..29f9ecd --- /dev/null +++ b/translation/ru/app.php @@ -0,0 +1,176 @@ + 'ID', + 'username' => 'Логин', + 'cname' => 'Имя', + 'surname' => 'Фамилия', + 'auth_key' => 'Ключ аутентификации', + 'password_hash' => 'Хэш пароля', + 'password_reset_token' => 'Password Reset Token', + 'email' => 'Логин (e-mail)', + 'phone' => 'Телефон', + 'status' => 'Статус', + 'gender' => 'Пол', + 'birth_day' => 'Birth Day', + 'birth_month' => 'Birth Month', + 'birth_year' => 'Birth Year', + 'group_id' => 'Group ID', + 'created_at' => 'Created At', + 'updated_at' => 'Updated At', + 'verifyCode' => 'Код проверки', + 'password' => 'Пароль', + 'password_repeat' => 'Повторить пароль', + 'registration' => 'Регистрация', + 'message' => 'Этот {field} уже занят', + 'message_match_password' => 'Пароли не совпадают', + 'exit' => 'Выход', + 'enter' => 'Войти', + 'your_personal_area' => 'Вход в личный кабинет', + 'forgot_password' => 'Забыли пароль?', + 'rememberMe' => 'Запомнить меня', + 'articles' => 'Всего товаров', + 'code' => 'Код: {0}', + 'checkout' => 'оформить заказ', + 'sum' => 'Сумма', + 'continue_shopping' => 'продолжить покупки', + 'edit_personal_data' => 'Редактировать личные данные', + 'personal_data' => 'Личные данные', + 'my_orders' => 'Мои заказы', + 'bookmarks' => 'Закладки', + 'basket' => 'Корзина', + 'banner_id' => 'Banner ID', + 'image' => 'Изображение', + 'alt' => 'Описание', + 'title' => 'Заголовок', + 'url' => 'Ссылка', + 'width' => 'Ширина', + 'height' => 'Высота', + 'blog_id' => 'Blog ID', + 'user_id' => 'User ID', + 'name' => 'Название', + 'link' => 'Ссылка', + 'user_add_id' => 'User Add ID', + 'view_count' => 'Количество просмотров', + 'description' => 'Описание', + 'cover' => 'Фото главное', + 'event_id' => 'Event ID', + 'alias' => 'Ссылка', + 'body' => 'Тело', + 'meta_title' => 'Мета заголовок', + 'h1' => 'H1', + 'seo_text' => 'Сео Текст', + 'end_at' => 'Срок действия по', + 'order_items_id' => 'Order Items ID', + 'order_id' => 'Order ID', + 'item_id' => 'Item ID', + 'item_count' => 'Количество', + 'price' => 'Цена', + 'customer_id' => 'Customer ID', + 'delivery' => 'Доставка', + 'payment' => 'Оплата', + 'seo_id' => 'Seo ID', + 'controller' => 'Controller', + 'seo_category_id' => 'Seo Category ID', + 'seo_dynamic_id' => 'Seo Dynamic ID', + 'action' => 'Action', + 'fields' => 'Поля', + 'param' => 'Параметры', + 'key' => 'Ключ', + 'service_id' => 'Service ID', + 'slider_id' => 'Slider ID', + 'speed' => 'Скорость', + 'duration' => 'Продолжительность', + 'slider_image_id' => 'Slider Image ID', + 'sort' => 'Сортировка', + 'order_name' => 'Ф.И.О', + 'order_phone' => 'Контактный телефон', + 'order_email' => 'email', + 'order_comment' => 'Комментарии', + 'articlesID' => 'ID', + 'articlesDate' => 'Дата', + 'articlesImage' => 'Изображение', + 'lang-Articles ID' => '', + 'lang-Language ID' => '', + 'lang-Title' => '', + 'lang-Body' => '', + 'lang-Meta Title' => '', + 'lang-Meta Keywords' => '', + 'lang-Meta Description' => '', + 'lang-Seo Text' => '', + 'lang-H1' => '', + 'lang-Body Preview' => '', + 'language_id' => '', + 'bg_id' => '', + 'feedback_id' => 'Feedback ID', + 'ip' => 'IP', + 'productName' => 'Продукт', + 'op_name' => 'Вид', + 'art' => 'Артикул', + 'cost' => 'Цена за один', + 'count' => 'Кол.', + 'sumCost' => 'Сумма', + 'in_menu' => 'Show in menu', + 'page_id' => 'Page ID', + 'meta_keywords' => 'Meta Keywords', + 'meta_description' => 'Meta Description', + 'product_spec_id' => 'Product Spec ID', + 'product_id' => 'Product ID', + 'tech_spec_link' => 'Tech Spec Link', + 'tech_char_link' => 'Tech Char Link', + 'techSpecFile' => 'techSpecFile', + 'techCharFile' => 'techCharFile', + 'tech_spec_text' => 'Tech Spec Text', + 'instruction' => 'Instruction', + 'product_to_project_id' => 'Product To Project ID', + 'product_variant_id' => 'Product Variant ID', + 'project_id' => 'Project ID', + 'product_to_rating_id' => 'Product To Rating ID', + 'value' => 'Value', + 'images' => 'Images', + 'project_image_id' => 'Project Image ID', + 'meta' => 'Meta', + 'date_time' => 'Дата', + 'template_location_id' => 'Template Location ID', + 'template_location_name' => 'Template Location Name', + 'template_location_title' => 'Template Location Title', + 'is_slider' => 'Is Slider', + 'is_banner' => 'Is Banner', + 'order_delivery_id' => 'order Delivery ID', + 'text' => 'Text', + 'emailis' => 'Такой email уже есть.', + 'меню' => 'меню', + 'контрактный отдел' => 'контрактный отдел', + 'отдел по работе с дизайнерами и архитекторами' => 'отдел по работе с дизайнерами и архитекторами', + 'диз_арх_2' => 'отдел по работе
    с дизайнерами
    и архитекторами', + 'search' => 'поиск', + 'copy1' => '2003 - 2016 Все права защищены и охраняются действующим законодательством Украины.', + 'copy2' => 'Использование материалов с данного сайта возможно только с письменного разрешения компании ООО «Витекс Украина».', + 'form1' => 'Запрос на просчет', + 'comment' => 'комментарий', + 'submit' => 'Отправить', + 'Сертификаты' => 'Сертификаты', + 'Монтаж, уборка, уход' => 'Монтаж, уборка, уход', + 'Галерея объектов' => 'Галерея объектов', + 'скачать' => 'скачать', + 'Документ технической документации' => 'Документ технической документации', + 'Вы также можете скачать таблицу с ' => 'Вы также можете скачать таблицу с ', + 'техническими характеристиками' => 'техническими характеристиками', + 'Номер по каталогу' => 'Номер по каталогу', + 'Заказать образец' => 'Заказать образец', + 'Технические характеристики' => 'Технические характеристики', + 'Контрактные продукты' => 'Контрактные продукты', + 'Статьи' => 'Статьи', + 'Контакты' => 'Контакты', + 'Отправить запрос' => 'Отправить запрос', + 'Создание сайтов' => 'Создание сайтов', + 'Подробнее' => 'Подробнее', + 'Запрос на просчет' => 'Запрос на просчет', + 'comment2' => 'Комментарий', + 'продолжить выбор' => 'продолжить выбор', + 'Отправить' => 'Отправить', + 'success1' => 'Ваша заявка принята.', + 'success2' => 'Мы свяжемся в Вами в ближайшее время', + 'Поиск' => 'Поиск', + 'Результаты поиска для' => 'Результаты поиска для', + 'certs' => 'Сертификаты', + ]; \ No newline at end of file diff --git a/translation/ru/blog.php b/translation/ru/blog.php new file mode 100644 index 0000000..936138e --- /dev/null +++ b/translation/ru/blog.php @@ -0,0 +1,22 @@ + 'Выберите категорию ...', + 'Select tag' => 'Выберите тэг ...', + 'Has no parent rubric' => 'Без категории', + 'Waiting for results' => 'Загрузка ...', + 'Select related products' => 'Выберите сопутствующие товары', + 'Select related articles' => 'Выберите статьи', + 'Blog Articles' => 'Статьи блога', + 'Create Blog Article' => 'Создать статью', + 'Update Blog Article: ' => 'Обновить статью: ', + 'Not active' => 'Не активна', + 'Active' => 'Активна', + 'Are you sure you want to delete this item?' => 'Вы точно хотите это удалить ?', + 'Update' => 'Обновить', + 'Blog Categories' => 'Рубрики', + 'Create Blog Category' => 'Создать рубрику', + 'Update Blog Category: ' => 'Обновить рубрику: ', + 'Blog Tags' => 'Тэги', + 'Create Blog Tag' => 'Создать тэг', + 'Update Blog Tag: ' => 'Обновить тэг: ', + ]; \ No newline at end of file diff --git a/translation/ru/product.php b/translation/ru/product.php new file mode 100755 index 0000000..1b39818 --- /dev/null +++ b/translation/ru/product.php @@ -0,0 +1,29 @@ + 'Категории', + 'Create Category' => 'Создать Категорию', + 'Name' => 'Наименование', + 'Remote ID' => 'ID в 1С', + 'Search for "{keywords}"' => 'Поиск по "{keywords}"', + 'Search for "{keywords}" in category "{category}"' => 'Поиск по "{keywords}" в категории "{category}"', + 'Promo products' => 'Акционные товары', + 'New products' => 'Новинки', + 'Top products' => 'Популярные', + 'Similar products' => 'Похожие товары', + 'Brands' => 'Бренды', + 'Brand' => 'Бренд', + 'Category' => 'Категория', + 'Select brand' => 'Выберите бренд', + 'Select category' => 'Выберите категорию', + 'SKU' => 'Артикул', + 'Stock' => 'Остаток', + 'Price' => 'Цена', + 'Price Old' => 'Старая Цена', + 'Products' => 'Товары', + 'Product' => 'Товар', + 'Variants' => 'Модификации', + 'Variant' => 'Модификация', + 'Create Product' => 'Создать Товар', + 'Enable' => 'Доступно', + 'Disable' => 'Отсутсвует', + ]; \ No newline at end of file diff --git a/translation/ua/app.php b/translation/ua/app.php new file mode 100755 index 0000000..e49e1c0 --- /dev/null +++ b/translation/ua/app.php @@ -0,0 +1,176 @@ + 'ID', + 'username' => "Логін", + 'cname' => 'Ім\'я', + 'surname' => 'Фамилия', + 'auth_key' => 'Ключ аутентифікації', + 'password_hash' => 'Хеш паролю', + 'password_reset_token' => 'Password Reset Token', + 'email' => 'Логін (e-mail)', + 'phone' => 'Телефон', + 'status' => 'Статус', + 'gender' => 'Пол', + 'birth_day' => 'Birth Day', + 'birth_month' => 'Birth Month', + 'birth_year' => 'Birth Year', + 'group_id' => 'Group ID', + 'created_at' => 'Створено', + 'updated_at' => 'Оновлено', + 'verifyCode' => 'Код перевірки', + 'password' => 'Пароль', + 'password_repeat' => 'Повторити пароль', + 'registration' => 'Реєстрація', + 'message' => 'Цей {field} вже зайнято', + 'message_match_password' => 'Пароли не совпадают', + 'exit' => 'Вихід', + 'enter' => 'Війти', + 'your_personal_area' => 'Вхід в особистий кабінет', + 'forgot_password' => 'Забули пароль?', + 'rememberMe' => 'Запам\'ятати мене', + 'articles' => 'Всього товарів', + 'code' => 'Код: {0}', + 'checkout' => 'оформити замовлення', + 'sum' => 'Сума', + 'continue_shopping' => 'продовжити покупки', + 'edit_personal_data' => 'Редагувати особисті дані', + 'personal_data' => 'Особисті дані', + 'my_orders' => 'Мої замовлення', + 'bookmarks' => 'Закладки', + 'basket' => 'Кошик', + 'banner_id' => 'Banner ID', + 'image' => 'Зображення', + 'alt' => 'Опис', + 'title' => 'Заголовок', + 'url' => 'Посилання', + 'width' => 'Ширина', + 'height' => 'Висота', + 'blog_id' => 'Blog ID', + 'user_id' => 'User ID', + 'name' => 'Назва', + 'link' => 'Посилання', + 'user_add_id' => 'User Add ID', + 'view_count' => 'Кількість переглядів', + 'description' => 'Опис', + 'cover' => 'Фото головне', + 'event_id' => 'Event ID', + 'alias' => 'Посилання', + 'body' => 'Тіло', + 'meta_title' => 'Мета заголовок', + 'h1' => 'H1', + 'seo_text' => 'Сео Текст', + 'end_at' => 'Термін дії до', + 'order_items_id' => 'Order Items ID', + 'order_id' => 'Order ID', + 'item_id' => 'Item ID', + 'item_count' => 'Кількість', + 'price' => 'Ціна', + 'customer_id' => 'Customer ID', + 'delivery' => 'Доставка', + 'payment' => 'Оплата', + 'seo_id' => 'Seo ID', + 'controller' => 'Controller', + 'seo_category_id' => 'Seo Category ID', + 'seo_dynamic_id' => 'Seo Dynamic ID', + 'action' => 'Action', + 'fields' => 'Поля', + 'param' => 'Параметри', + 'key' => 'Ключ', + 'service_id' => 'Service ID', + 'slider_id' => 'Slider ID', + 'speed' => 'Швидкість', + 'duration' => 'Тривалість', + 'slider_image_id' => 'Slider Image ID', + 'sort' => 'Сортування', + 'order_name' => 'П.І.Б.', + 'order_phone' => 'Контактний телефон', + 'order_email' => 'email', + 'order_comment' => 'Коментар', + 'articlesID' => 'ID', + 'articlesDate' => 'Дата', + 'articlesImage' => 'Зображення', + 'lang-Articles ID' => '', + 'lang-Language ID' => '', + 'lang-Title' => '', + 'lang-Body' => '', + 'lang-Meta Title' => '', + 'lang-Meta Keywords' => '', + 'lang-Meta Description' => '', + 'lang-Seo Text' => '', + 'lang-H1' => '', + 'lang-Body Preview' => '', + 'language_id' => '', + 'bg_id' => '', + 'feedback_id' => 'Feedback ID', + 'ip' => 'IP', + 'productName' => 'Продукт', + 'op_name' => 'Вид', + 'art' => 'Артикул', + 'cost' => 'Цена за один', + 'count' => 'Кол.', + 'sumCost' => 'Сумма', + 'in_menu' => 'Show in menu', + 'page_id' => 'Page ID', + 'meta_keywords' => 'Meta Keywords', + 'meta_description' => 'Meta Description', + 'product_spec_id' => 'Product Spec ID', + 'product_id' => 'Product ID', + 'tech_spec_link' => 'Tech Spec Link', + 'tech_char_link' => 'Tech Char Link', + 'techSpecFile' => 'techSpecFile', + 'techCharFile' => 'techCharFile', + 'tech_spec_text' => 'Tech Spec Text', + 'instruction' => 'Instruction', + 'product_to_project_id' => 'Product To Project ID', + 'product_variant_id' => 'Product Variant ID', + 'project_id' => 'Project ID', + 'product_to_rating_id' => 'Product To Rating ID', + 'value' => 'Value', + 'images' => 'Images', + 'project_image_id' => 'Project Image ID', + 'meta' => 'Meta', + 'date_time' => 'Дата', + 'template_location_id' => 'Template Location ID', + 'template_location_name' => 'Template Location Name', + 'template_location_title' => 'Template Location Title', + 'is_slider' => 'Is Slider', + 'is_banner' => 'Is Banner', + 'order_delivery_id' => 'order Delivery ID', + 'text' => 'Text', + 'emailis' => 'Такой email уже есть.', + 'меню' => 'меню', + 'контрактный отдел' => 'контрактне відділення', + 'отдел по работе с дизайнерами и архитекторами' => 'відділення по роботі з дизайнерами та архітекторами', + 'диз_арх_2' => 'відділення по роботі
    з дизайнерами
    та архітекторами', + 'search' => 'пошук', + 'copy1' => '2003 - 2016 Всі права захищені та охорняються чинним законодавством України.', + 'copy2' => 'Використання матеріалів з даного сайту можливе лише з письмого дозволу компанії ТОВ «Вітекс Україна».', + 'form1' => 'Запит на прорахування', + 'comment' => 'коментар', + 'submit' => 'Відправити', + 'Сертификаты' => 'Сертифікати', + 'Монтаж, уборка, уход' => 'Монтаж, прибирання, догляд', + 'Галерея объектов' => "Галерея об'єктів", + 'скачать' => 'завантажити', + 'Документ технической документации' => 'Документ технічної документації', + 'Вы также можете скачать таблицу с ' => 'Ви також можете завантажити таблицю з ', + 'техническими характеристиками' => 'технічними характеристиками', + 'Номер по каталогу' => 'Номер за каталогом', + 'Заказать образец' => 'Замовити зразок', + 'Технические характеристики' => 'Технічні характеристики', + 'Контрактные продукты' => 'Контрактні продукти', + 'Статьи' => 'Статті', + 'Контакты' => 'Контакти', + 'Отправить запрос' => 'Надіслати запит', + 'Создание сайтов' => 'Створення сайтів', + 'Подробнее' => 'Подробнее', + 'Запрос на просчет' => 'Запит на прорахування', + 'comment2' => 'Коментар', + 'продолжить выбор' => 'продовжити вибір', + 'Отправить' => 'Відправити', + 'success1' => 'Ваша заявка прийнята.', + 'success2' => "Ми зв'яжемось з Вами найближчим часом", + 'Поиск' => 'Пошук', + 'Результаты поиска для' => 'Результати пошуку для', + 'certs' => 'Сертифікати', + ]; \ No newline at end of file diff --git a/translation/ua/product.php b/translation/ua/product.php new file mode 100755 index 0000000..1b39818 --- /dev/null +++ b/translation/ua/product.php @@ -0,0 +1,29 @@ + 'Категории', + 'Create Category' => 'Создать Категорию', + 'Name' => 'Наименование', + 'Remote ID' => 'ID в 1С', + 'Search for "{keywords}"' => 'Поиск по "{keywords}"', + 'Search for "{keywords}" in category "{category}"' => 'Поиск по "{keywords}" в категории "{category}"', + 'Promo products' => 'Акционные товары', + 'New products' => 'Новинки', + 'Top products' => 'Популярные', + 'Similar products' => 'Похожие товары', + 'Brands' => 'Бренды', + 'Brand' => 'Бренд', + 'Category' => 'Категория', + 'Select brand' => 'Выберите бренд', + 'Select category' => 'Выберите категорию', + 'SKU' => 'Артикул', + 'Stock' => 'Остаток', + 'Price' => 'Цена', + 'Price Old' => 'Старая Цена', + 'Products' => 'Товары', + 'Product' => 'Товар', + 'Variants' => 'Модификации', + 'Variant' => 'Модификация', + 'Create Product' => 'Создать Товар', + 'Enable' => 'Доступно', + 'Disable' => 'Отсутсвует', + ]; \ No newline at end of file -- libgit2 0.21.4