diff --git a/Module.php b/Module.php new file mode 100755 index 0000000..5e1ae6a --- /dev/null +++ b/Module.php @@ -0,0 +1,22 @@ +owner; - return $owner->hasMany(TaxOption::className(), [ 'tax_option_id' => 'option_id' ]) - ->viaTable(ProductOption::tableName(), [ 'product_id' => $owner->getTableSchema()->primaryKey[ 0 ] ]) - ->joinWith('taxGroup') - ->all(); - } - - } \ No newline at end of file diff --git a/artbox-ecommerce/composer.json b/artbox-ecommerce/composer.json deleted file mode 100644 index a5453b2..0000000 --- a/artbox-ecommerce/composer.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "artweb/artbox-ecommerce", - "description": "Yii2 light-weight CMS", - "license": "BSD-3-Clause", - "require": { - "php": ">=7.0", - "yiisoft/yii2": "*", - "developeruz/yii2-db-rbac": "*" - }, - "autoload": { - "psr-4": { - "artweb\\artbox\\ecommerce\\": "" - } - } -} \ No newline at end of file diff --git a/artbox-ecommerce/config.php b/artbox-ecommerce/config.php deleted file mode 100755 index 3e57f5e..0000000 --- a/artbox-ecommerce/config.php +++ /dev/null @@ -1,8 +0,0 @@ - [ - 'category_group' => 1, - 'brand_group' => 2, - ], - ]; \ No newline at end of file diff --git a/artbox-ecommerce/controllers/ManageController.php b/artbox-ecommerce/controllers/ManageController.php deleted file mode 100755 index 22637d0..0000000 --- a/artbox-ecommerce/controllers/ManageController.php +++ /dev/null @@ -1,407 +0,0 @@ - [ - 'class' => VerbFilter::className(), - 'actions' => [ - 'delete' => [ 'POST' ], - ], - ], - ]; - } - - /** - * Lists all Product models. - * - * @return mixed - */ - public function actionIndex() - { - $searchModel = new ProductSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render( - 'index', - [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ] - ); - } - - /** - * Displays a single Product model. - * - * @param integer $id - * - * @return mixed - */ - public function actionView($id) - { - $model = $this->findModel($id); - $categories = $model->getCategories() - ->with('lang') - ->all(); - $variants = $model->getVariants() - ->with('lang') - ->all(); - $properties = $model->getProperties(); - return $this->render( - 'view', - [ - 'model' => $this->findModel($id), - 'categories' => $categories, - 'variants' => $variants, - 'properties' => $properties, - ] - ); - } - - /** - * Creates a new Product model. - * If creation is successful, the browser will be redirected to the 'view' page. - * - * @return mixed - */ - public function actionCreate() - { - $model = new Product(); - $model->generateLangs(); - if ($model->load(Yii::$app->request->post())) { - $model->loadLangs(\Yii::$app->request); - if ($model->save() && $model->transactionStatus) { - return $this->redirect( - [ - 'view', - 'id' => $model->id, - ] - ); - } - } - return $this->render( - 'create', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - ] - ); - } - - /** - * Updates an existing Product model. - * If update is successful, the browser will be redirected to the 'view' page. - * - * @param integer $id - * - * @return mixed - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - $model->generateLangs(); - if ($model->load(Yii::$app->request->post())) { - $model->loadLangs(\Yii::$app->request); - if ($model->save() && $model->transactionStatus) { - return $this->redirect( - [ - 'view', - 'id' => $model->id, - ] - ); - } - } - /** - * @var ActiveQuery $groups - */ - $groups = $model->getTaxGroupsByLevel(0); - return $this->render( - 'update', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - 'groups' => $groups, - ] - ); - } - - /** - * Deletes an existing Product model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * - * @param integer $id - * - * @return mixed - */ - public function actionDelete($id) - { - $this->findModel($id) - ->delete(); - return $this->redirect([ 'index' ]); - } - - /** - * Deletes an existing ProductImage model. - * - * @param int $id - */ - public function actionDeleteImage($id) - { - $image = ProductImage::findOne($id); - - if ($image) { - $image->delete(); - } - - print '1'; - exit; - } - - /** - * Toggle product top status - * - * @param int $id Product ID - * - * @return \yii\web\Response - */ - public function actionIsTop($id) - { - $model = $this->findModel($id); - - $model->is_top = intval(empty( $model->is_top )); - - $model->save(false, [ 'is_top' ]); - - return $this->redirect([ 'index' ]); - } - - /** - * Toggle product new status - * - * @param int $id Product ID - * - * @return \yii\web\Response - */ - public function actionIsNew($id) - { - $model = $this->findModel($id); - - $model->is_new = intval(empty( $model->is_new )); - - $model->save(false, [ 'is_new' ]); - - return $this->redirect([ 'index' ]); - } - - /** - * Toggle product discount status - * - * @param int $id Product ID - * - * @return \yii\web\Response - */ - public function actionIsDiscount($id) - { - $model = $this->findModel($id); - - $model->is_discount = intval(empty( $model->is_discount )); - - $model->save(false, [ 'is_discount' ]); - - return $this->redirect([ 'index' ]); - } - - /** - * Perform product import - * - * @return string - */ - public function actionImport() - { - $model = new Import(); - - $languages = Language::find() - ->select( - [ - 'name', - 'id', - ] - ) - ->where([ 'status' => 1 ]) - ->orderBy([ 'default' => SORT_DESC ]) - ->asArray() - ->indexBy('id') - ->column(); - - if ($model->load(Yii::$app->request->post())) { - \Yii::$app->session->set('export_lang', $model->lang); - $file = UploadedFile::getInstances($model, 'file'); - $method = 'go' . ucfirst($model->type); - $target = Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFile' . ucfirst($model->type)); - if (empty( $file )) { - $model->errors[] = 'File not upload'; - } elseif ($method == 'goPrices' && $file[ 0 ]->name != 'file_1.csv') { - $model->errors[] = 'File need "file_1.csv"'; - } elseif ($method == 'goProducts' && $file[ 0 ]->name == 'file_1.csv') { - $model->errors[] = 'File can not "file_1.csv"'; - } elseif ($model->validate() && $file[ 0 ]->saveAs($target)) { - // PROCESS PAGE - return $this->render( - 'import-process', - [ - 'model' => $model, - 'method' => $model->type, - 'target' => $target, - ] - ); - } else { - $model->errors[] = 'File can not be upload or other error'; - } - } - - return $this->render( - 'import', - [ - 'model' => $model, - 'languages' => $languages, - ] - ); - } - - /** - * Import products via AJAX - * - * @return array - * @throws \HttpRequestException - */ - public function actionProducts() - { - $from = Yii::$app->request->get('from', 0); - - $model = new Import(); - - if (Yii::$app->request->isAjax) { - Yii::$app->response->format = Response::FORMAT_JSON; - return $model->goProducts($from, 1); - } else { - throw new \HttpRequestException('Must be AJAX'); - } - } - - /** - * Import prices via AJAX - * - * @return array - * @throws \HttpRequestException - */ - public function actionPrices() - { - $from = Yii::$app->request->get('from', 0); - - $model = new Import(); - - if (Yii::$app->request->isAjax) { - Yii::$app->response->format = Response::FORMAT_JSON; - return $model->goPrices($from, 10); - } else { - throw new \HttpRequestException('Must be AJAX'); - } - } - - /** - * Export proccess via AJAX - * - * @param int $from - * @param string $filename - * - * @return array - * @throws \HttpRequestException - */ - public function actionExportProcess($from, $filename) - { - - $model = new Export(); - if (Yii::$app->request->isAjax) { - Yii::$app->response->format = Response::FORMAT_JSON; - return $model->process($filename, $from); - } else { - throw new \HttpRequestException('Must be AJAX'); - } - } - - /** - * Perform export - * - * @return string - */ - public function actionExport() - { - $model = new Export(); - - if ($model->load(Yii::$app->request->post())) { - \Yii::$app->session->set('export_lang', $model->lang); - return $this->render( - 'export-process', - [ - 'model' => $model, - 'method' => 'export', - ] - ); - } - - return $this->render( - 'export', - [ - 'model' => $model, - ] - ); - } - - /** - * Finds the Product model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * - * @param integer $id - * - * @return Product the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (( $model = Product::find() - ->where([ 'id' => $id ]) - ->with('lang') - ->one() ) !== null - ) { - return $model; - } else { - throw new NotFoundHttpException('The requested page does not exist.'); - } - } - } diff --git a/artbox-ecommerce/controllers/ProductUnitController.php b/artbox-ecommerce/controllers/ProductUnitController.php deleted file mode 100755 index 61f8313..0000000 --- a/artbox-ecommerce/controllers/ProductUnitController.php +++ /dev/null @@ -1,168 +0,0 @@ - [ - 'class' => VerbFilter::className(), - 'actions' => [ - 'delete' => [ 'POST' ], - ], - ], - ]; - } - - /** - * Lists all ProductUnit models. - * - * @return mixed - */ - public function actionIndex() - { - $searchModel = new ProductUnitSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render( - 'index', - [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ] - ); - } - - /** - * Displays a single ProductUnit model. - * - * @param integer $id - * - * @return mixed - */ - public function actionView($id) - { - return $this->render( - 'view', - [ - 'model' => $this->findModel($id), - ] - ); - } - - /** - * Creates a new ProductUnit model. - * If creation is successful, the browser will be redirected to the 'view' page. - * - * @return mixed - */ - public function actionCreate() - { - $model = new ProductUnit(); - $model->generateLangs(); - if ($model->load(Yii::$app->request->post())) { - $model->loadLangs(\Yii::$app->request); - if ($model->save() && $model->transactionStatus) { - return $this->redirect( - [ - 'view', - 'id' => $model->id, - ] - ); - } - } - return $this->render( - 'create', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - ] - ); - } - - /** - * Updates an existing ProductUnit model. - * If update is successful, the browser will be redirected to the 'view' page. - * - * @param integer $id - * - * @return mixed - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - $model->generateLangs(); - if ($model->load(Yii::$app->request->post())) { - $model->loadLangs(\Yii::$app->request); - if ($model->save() && $model->transactionStatus) { - return $this->redirect( - [ - 'view', - 'id' => $model->id, - ] - ); - } - } - return $this->render( - 'update', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - ] - ); - } - - /** - * Deletes an existing ProductUnit model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * - * @param integer $id - * - * @return mixed - */ - public function actionDelete($id) - { - $this->findModel($id) - ->delete(); - - return $this->redirect([ 'index' ]); - } - - /** - * Finds the ProductUnit model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * - * @param integer $id - * - * @return ProductUnit the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (( $model = ProductUnit::find() - ->where([ 'id' => $id ]) - ->with('lang') - ->one() ) !== null - ) { - return $model; - } else { - throw new NotFoundHttpException('The requested page does not exist.'); - } - } - } diff --git a/artbox-ecommerce/controllers/TaxGroupController.php b/artbox-ecommerce/controllers/TaxGroupController.php deleted file mode 100755 index 58ec043..0000000 --- a/artbox-ecommerce/controllers/TaxGroupController.php +++ /dev/null @@ -1,172 +0,0 @@ - [ - 'class' => VerbFilter::className(), - 'actions' => [ - 'delete' => [ 'POST' ], - ], - ], - ]; - } - - /** - * Lists all TaxGroup models. - * - * @param $level integer - * - * @return mixed - */ - public function actionIndex($level) - { - $searchModel = new TaxGroupSearch(); - $dataProvider = $searchModel->search(\Yii::$app->request->queryParams, $level); - /** - * @var ActiveQuery $query - */ - $query = $dataProvider->query; - $query->with('options') - ->with('categories'); - - return $this->render( - 'index', - [ - 'dataProvider' => $dataProvider, - 'searchModel' => $searchModel, - 'level' => $level, - ] - ); - } - - /** - * Creates a new TaxGroup model. - * If creation is successful, the browser will be redirected to the 'view' page. - * - * @param $level integer - * - * @return mixed - */ - public function actionCreate($level) - { - $model = new TaxGroup(); - $model->generateLangs(); - if ($model->load(Yii::$app->request->post()) && $model->validate()) { - $model->loadLangs(\Yii::$app->request); - $model->level = $level; - if ($model->save() && $model->transactionStatus) { - return $this->redirect( - [ - 'index', - 'level' => $level, - ] - ); - } - } - return $this->render( - 'create', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - ] - ); - } - - /** - * Updates an existing TaxGroup model. - * If update is successful, the browser will be redirected to the 'view' page. - * - * @param $level integer - * @param integer $id - * - * @return mixed - */ - public function actionUpdate($level, $id) - { - $model = $this->findModel($id); - $model->generateLangs(); - if ($model->load(Yii::$app->request->post())) { - $model->loadLangs(\Yii::$app->request); - if ($model->save() && $model->transactionStatus) { - return $this->redirect( - [ - 'index', - 'level' => $level, - ] - ); - } - } - return $this->render( - 'update', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - 'level' => $level, - ] - ); - } - - /** - * Deletes an existing TaxGroup model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * - * @param $level integer - * @param integer $id - * - * @return mixed - */ - public function actionDelete($level, $id) - { - $this->findModel($id) - ->delete(); - return $this->redirect( - [ - 'index', - 'level' => $level, - ] - ); - } - - /** - * Finds the TaxGroup model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * - * @param integer $id - * - * @return TaxGroup the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (( $model = TaxGroup::find() - ->with('lang') - ->where([ 'id' => $id ]) - ->one() ) !== null - ) { - return $model; - } else { - throw new NotFoundHttpException('The requested page does not exist.'); - } - } - } diff --git a/artbox-ecommerce/controllers/TaxOptionController.php b/artbox-ecommerce/controllers/TaxOptionController.php deleted file mode 100755 index cd00b1f..0000000 --- a/artbox-ecommerce/controllers/TaxOptionController.php +++ /dev/null @@ -1,198 +0,0 @@ - [ - 'class' => VerbFilter::className(), - 'actions' => [ - 'delete' => [ 'POST' ], - ], - ], - ]; - } - - /** - * Lists all TaxOption models. - * - * @return mixed - */ - public function actionIndex() - { - $group = $this->findGroup(Yii::$app->request->queryParams[ 'group' ]); - $searchModel = new TaxOptionSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * @var ActiveQuery $query - */ - $query = $dataProvider->query; - $query->andWhere([ 'tax_group_id' => $group->id ]); - if ($group->level) { - $query->with('productVariants'); - } else { - $query->with('products'); - } - return $this->render( - 'index', - [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - 'group' => $group, - ] - ); - } - - /** - * Creates a new TaxOption model. - * If creation is successful, the browser will be redirected to the 'view' page. - * - * @return mixed - */ - public function actionCreate() - { - $group = $this->findGroup(Yii::$app->request->queryParams[ 'group' ]); - $model = new TaxOption( - [ - 'tax_group_id' => $group->id, - ] - ); - $model->generateLangs(); - if ($model->load(Yii::$app->request->post())) { - $model->loadLangs(\Yii::$app->request); - if ($model->save() && $model->transactionStatus) { - return is_null(Yii::$app->request->post('create_and_new')) ? $this->redirect( - [ - 'index', - 'group' => $group->id, - ] - ) : $this->redirect(array_merge([ 'create' ], Yii::$app->request->queryParams)); - } - } - return $this->render( - 'create', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - 'group' => $group, - ] - ); - } - - /** - * Updates an existing TaxOption model. - * If update is successful, the browser will be redirected to the 'view' page. - * - * @param string $id - * - * @return mixed - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - $group = $this->findGroup($model->tax_group_id); - $model->generateLangs(); - if ($model->load(Yii::$app->request->post())) { - $model->loadLangs(\Yii::$app->request); - if ($model->save() && $model->transactionStatus) { - return $this->redirect( - [ - 'index', - 'group' => $group->id, - ] - ); - } - } - return $this->render( - 'update', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - 'group' => $group, - ] - ); - } - - /** - * Deletes an existing TaxOption model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * - * @param string $id - * - * @return mixed - */ - public function actionDelete($id) - { - $model = $this->findModel($id); - $group_id = $model->tax_group_id; - - $model->delete(); - - return $this->redirect( - [ - 'index', - 'group' => $group_id, - ] - ); - } - - /** - * Finds the TaxOption model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * - * @param string $id - * - * @return TaxOption the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (( $model = TaxOption::find() - ->with('lang') - ->where([ 'id' => $id ]) - ->one() ) !== null - ) { - return $model; - } else { - throw new NotFoundHttpException('The requested page does not exist.'); - } - } - - /** - * @param int $id - * - * @return null|TaxGroup - * @throws NotFoundHttpException - */ - protected function findGroup($id) - { - if (( $model = TaxGroup::find() - ->with('lang') - ->where([ 'id' => $id ]) - ->one() ) !== null - ) { - return $model; - } else { - throw new NotFoundHttpException('The requested page does not exist.'); - } - } - } diff --git a/artbox-ecommerce/controllers/VariantController.php b/artbox-ecommerce/controllers/VariantController.php deleted file mode 100755 index 4d326e3..0000000 --- a/artbox-ecommerce/controllers/VariantController.php +++ /dev/null @@ -1,315 +0,0 @@ - [ - 'class' => VerbFilter::className(), - 'actions' => [ - 'delete' => [ 'POST' ], - ], - ], - ]; - } - - /** - * Lists all ProductVariant models. - * - * @param int $product_id - * - * @return mixed - */ - public function actionIndex($product_id) - { - $product = $this->findProduct($product_id); - $searchModel = new ProductVariantSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * @var ActiveQuery $query - */ - $query = $dataProvider->query; - $query->with('image') - ->andWhere([ 'product_id' => $product->id ]); - - return $this->render( - 'index', - [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - 'product' => $product, - ] - ); - } - - /** - * Displays a single ProductVariant model. - * - * @param integer $id - * - * @return mixed - */ - public function actionView($id) - { - $model = $this->findModel($id); - $properties = $model->getProperties(); - return $this->render( - 'view', - [ - 'model' => $model, - 'properties' => $properties, - ] - ); - } - - /** - * Creates a new ProductVariant model. - * If creation is successful, the browser will be redirected to the 'view' page. - * - * @param int $product_id - * - * @return mixed - */ - public function actionCreate($product_id) - { - $product = $this->findProduct($product_id); - $model = new ProductVariant(); - $model->product_id = $product->id; - $model->generateLangs(); - if ($model->load(Yii::$app->request->post())) { - $model->loadLangs(\Yii::$app->request); - if ($model->save() && $model->transactionStatus) { - $model->stock = $this->saveStocks($model, Yii::$app->request->post('ProductStock', [])); - if ($model->save(true, [ 'stock' ]) && $model->transactionStatus) { - return $this->redirect( - [ - 'index', - 'product_id' => $product->id, - ] - ); - } - } - } - $groups = $model->getTaxGroupsByLevel(1); - return $this->render( - 'create', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - 'groups' => $groups, - 'stocks' => [ new ProductStock() ], - 'product' => $product, - ] - ); - } - - /** - * Updates an existing ProductVariant model. - * If update is successful, the browser will be redirected to the 'view' page. - * - * @param integer $product_id - * @param integer $id - * - * @return mixed - */ - public function actionUpdate($product_id, $id) - { - $product = $this->findProduct($product_id); - $model = $this->findModel($id); - $model->generateLangs(); - if ($model->load(Yii::$app->request->post())) { - $model->loadLangs(\Yii::$app->request); - if ($model->save() && $model->transactionStatus) { - $model->stock = $this->saveStocks($model, Yii::$app->request->post('ProductStock', [])); - if ($model->save(true, [ 'stock' ]) && $model->transactionStatus) { - return $this->redirect( - [ - 'index', - 'product_id' => $product_id, - ] - ); - } - } - } - $groups = $model->getTaxGroupsByLevel(1); - return $this->render( - 'update', - [ - 'model' => $model, - 'modelLangs' => $model->modelLangs, - 'groups' => $groups, - 'stocks' => ( !empty( $model->variantStocks ) ) ? $model->variantStocks : [ new ProductStock ], - 'product' => $product, - ] - ); - } - - /** - * Deletes an existing ProductVariant model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * - * @param integer $product_id - * @param integer $id - * - * @return mixed - */ - public function actionDelete($product_id, $id) - { - - $this->findModel($id) - ->delete(); - - return $this->redirect( - [ - 'index', - 'product_id' => $product_id, - ] - ); - } - - /** - * Deletes an existing ProductImage model. - * - * @param $id - */ - public function actionDeleteImage($id) - { - $image = ProductImage::findOne($id); - - if ($image) { - $image->delete(); - } - - print '1'; - exit; - } - - /** - * Save ProductStocks for ProductVariant and return total count of products. - * - * @param \artweb\artbox\ecommerce\models\ProductVariant $productVariant - * @param array|null $productStocks - * - * @return int - */ - protected function saveStocks(ProductVariant $productVariant, array $productStocks = null) - { - $total_quantity = 0; - if (!empty( $productStocks )) { - $productVariant->unlinkAll('stocks', true); - $sorted_array = []; - foreach ($productStocks as $productStock) { - if (!empty( $productStock[ 'title' ] ) && !empty( $productStock[ 'quantity' ] )) { - if (!empty( $sorted_array[ $productStock[ 'title' ] ] )) { - $sorted_array[ $productStock[ 'title' ] ] += $productStock[ 'quantity' ]; - } else { - $sorted_array[ $productStock[ 'title' ] ] = $productStock[ 'quantity' ]; - } - } - } - $productStocks = $sorted_array; - $stock_names = array_keys($productStocks); - $stocks = Stock::find() - ->joinWith('lang') - ->where([ 'stock_lang.title' => $stock_names ]) - ->indexBy(function($row) { - /** - * @var Stock $row - */ - return $row->lang->title; - }) - ->all(); - foreach ($productStocks as $stockName => $quantity) { - $quantity = (int) $quantity; - if (!array_key_exists($stockName, $stocks)) { - $stock = new Stock(); - $stock->generateLangs(); - foreach ($stock->modelLangs as $modelLang) { - $modelLang->title = $stockName; - } - if (!$stock->save() || !$stock->transactionStatus) { - continue; - } - } else { - $stock = $stocks[ $stockName ]; - } - $psModel = new ProductStock( - [ - 'product_variant_id' => $productVariant->id, - 'stock_id' => $stock->id, - 'quantity' => $quantity, - ] - ); - if ($psModel->save()) { - $total_quantity += $quantity; - } - } - } else { - $productVariant->unlinkAll('stocks', true); - } - return $total_quantity; - } - - /** - * Finds the ProductVariant model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * - * @param integer $id - * - * @return ProductVariant the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (( $model = ProductVariant::find() - ->where([ 'id' => $id ]) - ->with('lang') - ->one() ) !== null - ) { - return $model; - } else { - throw new NotFoundHttpException('The requested page does not exist.'); - } - } - - /** - * @param int $product_id - * - * @return Product - * @throws NotFoundHttpException - */ - protected function findProduct($product_id) - { - if (( $model = Product::find() - ->with('lang') - ->where([ 'id' => $product_id ]) - ->one() ) !== null - ) { - return $model; - } else { - throw new NotFoundHttpException('The requested page does not exist.'); - } - } - } diff --git a/artbox-ecommerce/helpers/FilterHelper.php b/artbox-ecommerce/helpers/FilterHelper.php deleted file mode 100755 index 5113145..0000000 --- a/artbox-ecommerce/helpers/FilterHelper.php +++ /dev/null @@ -1,313 +0,0 @@ -joinWith('lang') - ->where([ 'is_filter' => 'TRUE' ]) - ->all(), - 'lang.alias' - ); - } else { - return static::$optionsList; - } - - } - - /** - * Return custom filter-option link - * - * @param array $filter - * @param string $key - * @param mixed $value - * @param bool $remove - * - * @return array - */ - public static function getFilterForOption(array $filter, string $key, $value, bool $remove = false) - { - - $optionsTemplate = self::optionsTemplate(); - array_unshift($optionsTemplate, "special", "brands"); - - $result = $filter; - - if (is_array($value)) { - foreach ($value as $value_key => $value_items) { - if (!is_array($value_items)) { - $value_items = [ $value_items ]; - } - foreach ($value_items as $value_item) { - if ($remove && isset( $result[ $key ] ) && ( $i = array_search( - $value_item, - $result[ $key ][ $value_key ] - ) ) !== false - ) { - unset( $result[ $key ][ $value_key ][ $i ] ); - if (empty( $result[ $key ][ $value_key ] )) { - unset( $result[ $key ][ $value_key ] ); - } - } else { - if (!isset( $result[ $key ][ $value_key ] ) || array_search( - $value_item, - $result[ $key ][ $value_key ] - ) === false - ) { - $result[ $key ][ $value_key ][] = $value_item; - } - } - } - } - } else { - if ($remove && isset( $result[ $key ] ) && ( $i = array_search($value, $result[ $key ]) ) !== false) { - unset( $result[ $key ][ $i ] ); - if (empty( $result[ $key ] )) { - unset( $result[ $key ] ); - } - } else { - if (!isset( $result[ $key ] ) || array_search($value, $result[ $key ]) === false) { - $result[ $key ][] = $value; - } - } - } - - $filterView = []; - - foreach ($optionsTemplate as $optionKey) { - if (isset( $result[ $optionKey ] )) { - $filterView[ $optionKey ] = $result[ $optionKey ]; - } - - } - - return $filterView; - } - - /** - * Fill query with filter conditions - * - * @param ActiveQuery $query - * @param array $params - */ - public static function setQueryParams(ActiveQuery $query, array $params) - { - $last_query = null; - foreach ($params as $key => $param) { - switch ($key) { - case 'special': - self::filterSpecial($param, $query); - break; - case 'brands': - self::filterBrands($param, $query); - break; - case 'keywords': - self::filterKeywords($param, $query); - break; - case 'prices': - self::filterPrices($param, $query); - break; - default: - $last_query = self::filterOptions($param, $last_query); - break; - } - } - // If tax option filters were provided filter query with them - if (!empty( $last_query )) { - $query->andWhere([ 'product.id' => $last_query ]); - } - } - - /** - * Tax Option filter - * - * @param string[] $params - * @param \yii\db\Query|null $last_query - * - * @return Query - */ - private static function filterOptions(array $params, Query $last_query = null): Query - { - $variant_query = ( new Query() )->distinct() - ->select('product_variant.product_id as products') - ->from('product_variant_option') - ->innerJoin( - 'product_variant', - 'product_variant_option.product_variant_id = product_variant.id' - ) - ->innerJoin( - 'tax_option', - 'tax_option.id = product_variant_option.option_id' - ) - ->innerJoin( - 'tax_option_lang', - 'tax_option_lang.tax_option_id = tax_option.id' - ) - ->where([ 'tax_option_lang.alias' => $params ]); - $product_query = ( new Query() )->distinct() - ->select('product_option.product_id as products') - ->from('product_option') - ->innerJoin('tax_option', 'product_option.option_id = tax_option.id') - ->innerJoin( - 'tax_option_lang', - 'tax_option_lang.tax_option_id = tax_option.id' - ) - ->where( - [ 'tax_option_lang.alias' => $params ] - ) - ->union($variant_query); - $query = ( new Query() )->select('products') - ->from([ 'result_table' => $product_query ]); - if (!empty( $last_query )) { - $query->andWhere([ 'product.id' => $last_query ]); - } - return $query; - } - - /** - * Fill $query with special filters (used in Product) - * - * @param array $params - * @param \yii\db\ActiveQuery $query - */ - private static function filterSpecial(array $params, ActiveQuery $query) - { - $conditions = []; - /** - * @var string $key - */ - foreach ($params as $key => $param) { - $conditions[] = [ - '=', - Product::tableName() . '.' . $key, - $param, - ]; - } - /* If 2 or more special conditions get all that satisfy at least one of them. */ - if (count($conditions) > 1) { - array_unshift($conditions, 'or'); - } else { - $conditions = $conditions[ 0 ]; - } - $query->andFilterWhere($conditions); - } - - /** - * Fill query with brands filter - * - * @param int[] $param - * @param \yii\db\ActiveQuery $query - */ - private static function filterBrands(array $param, ActiveQuery $query) - { - $query->andFilterWhere([ Product::tableName() . '.brand_id' => $param ]); - } - - /** - * Fill query with keywords filter - * - * @param array $params - * @param \yii\db\ActiveQuery $query - */ - private static function filterKeywords(array $params, ActiveQuery $query) - { - $conditions = []; - if (!empty( $params )) { - if (!is_array($params)) { - $params = [ $params ]; - } - /** - * @var string $param Inputed keyword - */ - foreach ($params as $param) { - $conditions[] = [ - 'or', - [ - 'ilike', - ProductLang::tableName() . '.title', - $param, - ], - [ - 'ilike', - BrandLang::tableName() . '.title', - $param, - ], - [ - 'ilike', - CategoryLang::tableName() . '.title', - $param, - ], - [ - 'ilike', - ProductVariantLang::tableName() . '.title', - $param, - ], - ]; - } - } - if (count($conditions) > 1) { - array_unshift($conditions, 'or'); - } else { - $conditions = $conditions[ 0 ]; - } - $query->andFilterWhere($conditions); - } - - /** - * Fill query with price limits filter - * - * @param array $params - * @param \yii\db\ActiveQuery $query - */ - private static function filterPrices(array $params, ActiveQuery $query) - { - $conditions = []; - if (!empty( $params[ 'min' ] ) && $params[ 'min' ] > 0) { - $conditions[] = [ - '>=', - ProductVariant::tableName() . '.price', - $params[ 'min' ], - ]; - } - if (!empty( $params[ 'max' ] ) && $params[ 'max' ] > 0) { - $conditions[] = [ - '<=', - ProductVariant::tableName() . '.price', - $params[ 'max' ], - ]; - } - if (count($conditions) > 1) { - array_unshift($conditions, 'and'); - } else { - $conditions = $conditions[ 0 ]; - } - $query->andFilterWhere($conditions); - } - - } - \ No newline at end of file diff --git a/artbox-ecommerce/helpers/ProductHelper.php b/artbox-ecommerce/helpers/ProductHelper.php deleted file mode 100755 index 2962c6a..0000000 --- a/artbox-ecommerce/helpers/ProductHelper.php +++ /dev/null @@ -1,184 +0,0 @@ -getTree(null, 'lang'); - } - - /** - * Add $product_id to last products in session. Limit 16 products. - * - * @param int $product_id - */ - public static function addLastProducts(int $product_id) - { - $last_products = self::getLastProducts(); - if (!in_array($product_id, $last_products)) { - $last_products[] = intval($product_id); - if (count($last_products) > 16) { - array_shift($last_products); - } - Yii::$app->session->set('last_products', $last_products); - } - } - - /** - * Get last products ids from session or last Product models with ProductVariant, which are in stock if - * $as_object is true - * - * @param bool $as_object - * - * @return array - */ - public static function getLastProducts(bool $as_object = false) - { - $last_products = Yii::$app->session->get('last_products', []); - if ($as_object) { - $last_products = Product::find() - ->joinWith([ 'variant' ]) - ->where([ 'product.id' => $last_products ]) - ->andWhere( - [ - '!=', - 'product_variant.stock', - 0, - ] - ) - ->indexBy('id') - ->all(); - } - return array_reverse($last_products); - } - - /** - * Get special Products array with ProductVariants, which are in stock - * Available types: - * * top - * * new - * * promo - * - * @param string $type - * @param int $count - * - * @return Product[] - */ - public static function getSpecialProducts(string $type, int $count) - { - switch ($type) { - case 'top': - $data = [ 'is_top' => true ]; - break; - case 'new': - $data = [ 'is_new' => true ]; - break; - case 'promo': - $data = [ 'is_discount' => true ]; - break; - default: - return []; - break; - } - return Product::find() - ->with('lang') - ->joinWith('variants.lang') - ->where($data) - ->andWhere( - [ - '!=', - 'productVariant.stock', - 0, - ] - ) - ->limit($count) - ->all(); - } - - /** - * Get ActiveQuery to get similar products to $product - * - * @param Product $product - * @param int $count - * - * @return ActiveQuery - */ - public static function getSimilarProducts(Product $product, $count = 10): ActiveQuery - { - $query = Product::find(); - if (empty( $product->properties )) { - $query->where('0 = 1'); - return $query; - } - $query->innerJoinWith('variants') - ->joinWith('categories') - ->where( - [ - '!=', - 'product_variant.stock', - 0, - ] - ) - ->andWhere( - [ 'product_category.category_id' => ArrayHelper::getColumn($product->categories, 'id') ] - ); - $options = []; - foreach ($product->properties as $group) { - foreach ($group->options as $option) { - $options[] = $option->id; - } - } - if (!empty( $options )) { - $query->innerJoinWith('options') - ->andWhere([ 'product_option.option_id' => $options ]); - } else { - $query->where('0 = 1'); - return $query; - } - $query->andWhere( - [ - '!=', - 'product.id', - $product->id, - ] - ); - $query->groupBy('product.id'); - $query->limit($count); - return $query; - } - - /** - * Add last category id to session - * - * @param int $category_id - */ - public static function addLastCategory(int $category_id) - { - \Yii::$app->session->set('last_category_id', $category_id); - } - - /** - * Get last category id from session - * - * @return int - */ - public static function getLastCategory(): int - { - return \Yii::$app->session->get('last_category_id'); - } - } \ No newline at end of file diff --git a/artbox-ecommerce/models/Brand.php b/artbox-ecommerce/models/Brand.php deleted file mode 100755 index bd4d78c..0000000 --- a/artbox-ecommerce/models/Brand.php +++ /dev/null @@ -1,108 +0,0 @@ - SaveImgBehavior::className(), - 'fields' => [ - [ - 'name' => 'image', - 'directory' => 'brand', - ], - ], - ], - 'language' => [ - 'class' => LanguageBehavior::className(), - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'in_menu' ], - 'boolean', - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('product', 'Brand ID'), - 'image' => Yii::t('product', 'Image'), - ]; - } - - /** - * Get all products with current brand - * - * @return \yii\db\ActiveQuery - */ - public function getProducts() - { - return $this->hasMany(Product::className(), [ 'brand_id' => 'id' ]); - } - } diff --git a/artbox-ecommerce/models/BrandLang.php b/artbox-ecommerce/models/BrandLang.php deleted file mode 100755 index 36fcdff..0000000 --- a/artbox-ecommerce/models/BrandLang.php +++ /dev/null @@ -1,137 +0,0 @@ - [ - 'class' => 'common\behaviors\Slug', - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'title' ], - 'required', - ], - [ - [ 'seo_text' ], - 'string', - ], - [ - [ - 'title', - 'meta_title', - 'meta_robots', - 'meta_description', - 'alias', - ], - 'string', - 'max' => 255, - ], - [ - [ - 'brand_id', - 'language_id', - ], - 'unique', - 'targetAttribute' => [ - 'brand_id', - 'language_id', - ], - 'message' => 'The combination of Brand ID and Language ID has already been taken.', - ], - [ - [ 'brand_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Brand::className(), - 'targetAttribute' => [ 'brand_id' => 'id' ], - ], - [ - [ 'language_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Language::className(), - 'targetAttribute' => [ 'language_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'brand_id' => Yii::t('app', 'Brand ID'), - 'language_id' => Yii::t('app', 'Language ID'), - 'title' => Yii::t('app', 'Name'), - 'meta_title' => Yii::t('app', 'Meta Title'), - 'meta_robots' => Yii::t('app', 'Meta Robots'), - 'meta_description' => Yii::t('app', 'Meta Desc'), - 'seo_text' => Yii::t('app', 'Seo Text'), - 'alias' => Yii::t('app', 'Alias'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getBrand() - { - return $this->hasOne(Brand::className(), [ 'id' => 'brand_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getLanguage() - { - return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); - } - } diff --git a/artbox-ecommerce/models/BrandSearch.php b/artbox-ecommerce/models/BrandSearch.php deleted file mode 100755 index 6aaa8d0..0000000 --- a/artbox-ecommerce/models/BrandSearch.php +++ /dev/null @@ -1,104 +0,0 @@ -joinWith('lang'); - - // add conditions that should always apply here - - $dataProvider = new ActiveDataProvider( - [ - 'query' => $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; - }*/ - - $dataProvider->setSort( - [ - 'attributes' => [ - 'id', - 'brandName' => [ - 'asc' => [ 'brand_lang.title' => SORT_ASC ], - 'desc' => [ 'brand_lang.title' => SORT_DESC ], - ], - ], - ] - ); - - // grid filtering conditions - $query->andFilterWhere( - [ - 'brand.id' => $this->id, - ] - ) - ->andFilterWhere( - [ - 'ilike', - 'brand_lang.title', - $this->brandName, - ] - ); - - return $dataProvider; - } - } diff --git a/artbox-ecommerce/models/Category.php b/artbox-ecommerce/models/Category.php deleted file mode 100755 index c4eb1cc..0000000 --- a/artbox-ecommerce/models/Category.php +++ /dev/null @@ -1,313 +0,0 @@ - [ - 'class' => ArtboxTreeBehavior::className(), - 'keyNameGroup' => null, - 'keyNamePath' => 'path', - ], - 'language' => [ - 'class' => LanguageBehavior::className(), - ], - [ - 'class' => SaveImgBehavior::className(), - 'fields' => [ - [ - 'name' => 'image', - 'directory' => 'categories', - ], - ], - ], - ]; - } - - /** - * @inheritdoc - */ - public static function tableName() - { - return 'category'; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ - 'parent_id', - 'depth', - 'product_unit_id', - ], - 'integer', - ], - [ - [ - 'path', - ], - 'string', - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('product', 'Category ID'), - 'parent_id' => Yii::t('product', 'Parent ID'), - 'path' => Yii::t('product', 'Path'), - 'depth' => Yii::t('product', 'Depth'), - 'image' => Yii::t('product', 'Image'), - 'imageUrl' => Yii::t('product', 'Image'), - 'product_unit_id' => Yii::t('product', 'Product Unit ID'), - 'remote_id' => Yii::t('product', 'Remote ID'), - ]; - } - - public static function find() - { - return new CategoryQuery(get_called_class()); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductUnit() - { - return $this->hasOne(ProductUnit::className(), [ 'id' => 'product_unit_id' ]); - } - - /** - * @return ActiveQuery - */ - public function getProducts() - { - return $this->hasMany(Product::className(), [ 'id' => 'product_id' ]) - ->viaTable('product_category', [ 'category_id' => 'id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductCategories() - { - return $this->hasMany(ProductCategory::className(), [ 'category_id' => 'id' ]); - } - - /** - * Get all brands for Category query - * - * @return ActiveQuery - */ - public function getBrands() - { - return $this->hasMany(Brand::className(), [ 'id' => 'brand_id' ]) - ->via('products'); - } - - /** - * Get Tax Groups by level - * * 0 for Product - * * 1 for ProductVariant - * - * @param int $level - * - * @return ActiveQuery - */ - public function getTaxGroupsByLevel(int $level) - { - if ($level !== 0 || $level !== 1) { - throw new InvalidParamException('Level supports only 0 and 1 values'); - } - return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) - ->viaTable('tax_group_to_category', [ 'category_id' => 'id' ]) - ->andWhere([ 'level' => $level ]); - } - - /** - * Леша найди путь как убрать это, мб в базу записать просто по умолчанию значение и notnull - * - * @param bool $insert - * - * @return bool - */ - public function beforeSave($insert) - { - if (parent::beforeSave($insert)) { - - if (empty( $this->parent_id )) { - $this->parent_id = 0; - } - - return true; - } - return false; - } - - /** - * Get query for activefilter for current category - * - * @return Query - */ - public function getActiveFilters() - { - $language_id = Language::getCurrent()->id; - $query1 = ( new Query() )->distinct() - ->select( - [ - 'option_id', - ] - ) - ->from('tax_option') - ->innerJoin( - 'product_variant_option', - 'tax_option.id = product_variant_option.option_id' - ) - ->innerJoin('tax_group', 'tax_group.id = tax_option.tax_group_id') - ->innerJoin( - 'product_variant', - 'product_variant.id = product_variant_option.product_variant_id' - ) - ->innerJoin('product', 'product.id = product_variant.product_id') - ->innerJoin('product_category', 'product_category.product_id = product.id') - ->where( - [ - 'product_category.category_id' => $this->id, - 'tax_group.is_filter' => true, - ] - ) - ->andWhere( - [ - '!=', - 'product_variant.stock', - 0, - ] - ); - - $query2 = ( new Query() )->distinct() - ->select( - [ - 'option_id', - ] - ) - ->from('tax_option') - ->innerJoin( - 'product_option', - 'tax_option.id = product_option.option_id' - ) - ->innerJoin('tax_group', 'tax_group.id = tax_option.tax_group_id') - ->innerJoin('product', 'product.id = product_option.product_id') - ->innerJoin('product_category', 'product_category.product_id = product.id') - ->innerJoin('product_variant', 'product_variant.product_id = product.id') - ->where( - [ - 'product_category.category_id' => $this->id, - 'tax_group.is_filter' => true, - ] - ) - ->andWhere( - [ - '!=', - 'product_variant.stock', - 0, - ] - ); - $query3 = ( new Query() )->select( - [ - 'tax_option.*', - 'tax_option_lang.alias as option_alias', - 'tax_group_lang.alias as group_alias', - 'tax_option_lang.value as value', - 'tax_option.sort AS tax_option_sort', - 'tax_group.sort AS tax_group_sort', - ] - ) - ->from([ 'tax_option' ]) - ->where([ 'tax_option.id' => $query1->union($query2) ]) - ->innerJoin('tax_group', 'tax_group.id = tax_option.tax_group_id') - ->innerJoin('tax_option_lang', 'tax_option.id = tax_option_lang.tax_option_id') - ->innerJoin('tax_group_lang', 'tax_group.id = tax_group_lang.tax_group_id') - ->andWhere([ 'tax_option_lang.language_id' => $language_id ]) - ->andWhere([ 'tax_group_lang.language_id' => $language_id ]) - ->orderBy('tax_option.sort, tax_group.sort'); - return $query3; - } - - /** - * Get query to get all TaxGroup for current Category - * - * @return ActiveQuery - */ - public function getTaxGroups() - { - return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) - ->viaTable('tax_group_to_category', [ 'category_id' => 'id' ]); - } - } diff --git a/artbox-ecommerce/models/CategoryLang.php b/artbox-ecommerce/models/CategoryLang.php deleted file mode 100755 index 3a10105..0000000 --- a/artbox-ecommerce/models/CategoryLang.php +++ /dev/null @@ -1,139 +0,0 @@ - [ - 'class' => 'common\behaviors\Slug', - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'title' ], - 'required', - ], - [ - [ - 'seo_text', - 'alias', - ], - 'string', - ], - [ - [ - 'title', - 'meta_title', - 'meta_robots', - 'meta_description', - 'h1', - ], - 'string', - 'max' => 255, - ], - [ - [ - 'category_id', - 'language_id', - ], - 'unique', - 'targetAttribute' => [ - 'category_id', - 'language_id', - ], - 'message' => 'The combination of Category ID and Language ID has already been taken.', - ], - [ - [ 'category_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Category::className(), - 'targetAttribute' => [ 'category_id' => 'id' ], - ], - [ - [ 'language_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Language::className(), - 'targetAttribute' => [ 'language_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'category_id' => Yii::t('app', 'Category ID'), - 'language_id' => Yii::t('app', 'Language ID'), - 'title' => Yii::t('app', 'Name'), - 'meta_title' => Yii::t('app', 'Meta Title'), - 'meta_robots' => Yii::t('app', 'Meta Robots'), - 'meta_description' => Yii::t('app', 'Meta Desc'), - 'seo_text' => Yii::t('app', 'Seo Text'), - 'h1' => Yii::t('app', 'H1'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getCategory() - { - return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getLanguage() - { - return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); - } - } diff --git a/artbox-ecommerce/models/CategoryQuery.php b/artbox-ecommerce/models/CategoryQuery.php deleted file mode 100755 index ed0b7d4..0000000 --- a/artbox-ecommerce/models/CategoryQuery.php +++ /dev/null @@ -1,34 +0,0 @@ -joinWith('lang'); - - $dataProvider = new ActiveDataProvider( - [ - 'query' => $query, - 'sort' => false, - ] - ); - - $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( - [ - 'category.id' => $this->id, - ] - ) - ->andFilterWhere( - [ - 'ilike', - 'category_lang.title', - $this->categoryName, - ] - ); - - $query->orderBy( - [ - 'category.path' => SORT_ASC, - 'category.depth' => SORT_ASC, - 'category.id' => SORT_ASC, - ] - ); - - return $dataProvider; - } - } diff --git a/artbox-ecommerce/models/Export.php b/artbox-ecommerce/models/Export.php deleted file mode 100755 index 2aa0dd4..0000000 --- a/artbox-ecommerce/models/Export.php +++ /dev/null @@ -1,207 +0,0 @@ - Language::getCurrent()->id, - ], - ]; - } - - /** - * Perform product export - * - * @param null|string $filename Export csv file name - * @param int $from Product start - * - * @return array - */ - public function process($filename = null, $from = 0) - { - $limit = 100; - - if (empty( $filename )) { - $filename = 'products_' . date('d_m_Y_H_i') . '.csv'; - $handle = fopen(\Yii::getAlias('@storage/sync/') . $filename, "w"); - } else { - $handle = fopen(\Yii::getAlias('@storage/sync/') . $filename, "a"); - } - - $language = Language::findOne(\Yii::$app->session->get('export_lang', Language::getDefaultLanguage()->id)); - Language::setCurrent($language->url); - - /** - * @var Product[] $products - */ - $products = Product::find() - ->with('variantsWithFilters', 'brand.lang', 'categories.lang', 'filters', 'images') - ->joinWith('lang', true, 'INNER JOIN') - ->limit($limit) - ->offset($from) - ->all(); - $filesize = Product::find() - ->joinWith('lang', true, 'INNER JOIN') - ->count(); - foreach ($products as $product) { - $mods = []; - $filterString = $this->convertFilterToString($product->filters); - - foreach ($product->variantsWithFilters as $variant) { - /** - * @var ProductVariant $variant - */ - $color = $variant->lang->title; - $mods[] = $variant->sku . $this->generateID( - $variant->remote_id - ) . '=' . $this->convertFilterToString( - $variant->filters - ) . '=' . $color . '=' . ( ( !empty( $variant->image ) ) ? $variant->image->image : '' ) . '=' . $variant->stock; - } - - $fotos = []; - foreach ($product->images as $image) { - $fotos[] = $image->image; - } - - $categories = []; - foreach ($product->categories as $value) { - $categories[] = $value->lang->title . $this->generateID($value->remote_id); - } - - $categories = implode(',', $categories); - - $list = [ - $categories, - //A - категории через запятую Название(remote_id) - ( ( !empty( $product->brand ) ) ? $product->brand->lang->title . $this->generateID( - $product->brand->remote_id - ) : '' ), - //B - бренд Название(remote_id) - $product->lang->title . $this->generateID($product->remote_id), - //C - название товара Название(remote_id) - ( ( !empty( $product->lang->description ) ) ? $product->lang->description : '' ), - //D - описание товара Описание(remote_id) - $filterString, - //E - характеристики товара. Структура: [Группа1(remote_id):Характеристика11(remote_id),Характеристика12(remote_id)]*[Группа2(remote_id):Характеристика21(remote_id),Характеристика22(remote_id)] - ( !empty( $product->variant ) ) ? $product->variant->price_old : '', - //F - страрая цена - ( !empty( $product->variant ) ) ? $product->variant->price : '', - //G - новая цена - intval($product->is_discount), - //H - товар акционный (1/0) - '', - //I - пустой - intval($product->is_new), - //J - товар новинка - intval($product->is_top), - //K - товар в топе - $product->video, - //L - ссылка на видео (iframe) - implode(',', $fotos), - //M - название файлов через запятую, картинки должны хранится в /storage/sync/product_images - // Все последующие модификации: SKU(remote_id)=[Группа1(remote_id):Характеристика11(remote_id),Характеристика12(remote_id)]*[Группа2(remote_id):Характеристика21(remote_id),Характеристика22(remote_id)]=Название=Изображение=Остаток - ]; - $to_write = array_merge($list, $mods); - fputcsv($handle, $to_write, ';'); - unset( $product ); - } - - fclose($handle); - - $from += $limit; - $end = false; - if ($from > $filesize) { - $end = true; - } - - $result = [ - 'end' => $end, - 'from' => $from, - 'totalsize' => $filesize, - 'filename' => $filename, - ]; - - if ($end) { - $result = array_merge( - $result, - [ - 'link' => '/storage/sync/' . $filename, - ] - ); - } - - return $result; - } - - /** - * Stringify filters for export - * * Result: [filterName1:filterValue11,filterValue12]*[filterName2:filterValue21,filterValue22] - * - * @param $filters - * - * @return string - */ - public function convertFilterToString($filters) - { - $filtersArray = []; - /** - * @var TaxOption[] $filters - */ - foreach ($filters as $filter) { - $filtersArray[ $filter->taxGroup->lang->title . $this->generateID( - $filter->taxGroup->remote_id - ) ][] = $filter->lang->value . $this->generateID($filter->remote_id); - } - $filterString = []; - - foreach ($filtersArray as $filterName => $filterRows) { - $row = implode(',', $filterRows); - $filterString[] = "[{$filterName}:{$row}]"; - } - return implode('*', $filterString); - } - - /** - * Generate remote ID string - * - * @param string $id Remote ID - * - * @return string Formatted remote ID - */ - private function generateID(string $id):string - { - return sprintf('(#%s#)', $id); - } - } - \ No newline at end of file diff --git a/artbox-ecommerce/models/Import.php b/artbox-ecommerce/models/Import.php deleted file mode 100755 index 7662973..0000000 --- a/artbox-ecommerce/models/Import.php +++ /dev/null @@ -1,841 +0,0 @@ - 'csv', - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'file' => Yii::t('product', 'File'), - ]; - } - - /** - * Get import type - * - * @see Import::type - * @return string - */ - public function getType() - { - if (!$this->type) { - $this->type = 'products'; - } - return $this->type; - } - - /** - * Import prices - * - * @param int $from Start row - * @param null $limit Row limit - * - * @return array|bool Array if OK, false otherwise - */ - public function goPrices($from = 0, $limit = null) - { - set_time_limit(0); - - if (!( $handle = $this->getProductsFile('uploadFilePrices') )) { - $this->errors[] = 'File not found'; - return false; - } - - $filesize = filesize(Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFilePrices')); - if ($from) { - fseek($handle, $from); - } - - $j = 0; - - $is_utf = ( preg_match( - '//u', - file_get_contents( - Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFilePrices'), - null, - null, - null, - 1000000 - ) - ) ); - - while (( empty( $limit ) || $j++ < $limit ) && ( $data = fgetcsv($handle, 10000, ";") ) !== false) { - foreach ($data as &$value) { - if (!$is_utf) { - $value = iconv('windows-1251', "UTF-8//TRANSLIT//IGNORE", $value); - } - $value = trim($value); - } - - // данные строк - $modification_code = @$data[ 0 ]; - $price = floatval(@$data[ 1 ]); - $price_promo = floatval(@$data[ 2 ]); - $count = intval(@$data[ 3 ]); - $city_name = @$data[ 4 ]; - $product_title = @$data[ 5 ]; - - if (empty ( $modification_code )) { - continue; - } - // товары в пути - if (empty ( $city_name )) { - $this->output[] = 'Товар ' . $product_title . ' в пути'; - continue; - } - /** - * @var ProductVariant $productVariant - */ - if (( $productVariant = ProductVariant::find() - ->filterWhere([ 'sku' => $modification_code ]) - ->one() ) === null - ) { - $this->output[] = 'Для товара ' . $product_title . ' не найдено соотвествие'; - continue; - } - // ===== Set stock ==== - if ($city_name) { - if (( $stock = Stock::find() - ->filterWhere([ 'title' => trim($city_name) ]) - ->one() ) === null - ) { - // Create stock - $stock = new Stock(); - $stock->title = trim($city_name); - $stock->save(false); - } - - $productStock = ProductStock::find() - ->where( - [ - 'product_variant_id' => $productVariant->id, - 'stock_id' => $stock->id, - ] - ) - ->one(); - if (!$productStock instanceof ProductStock) { - $productStock = new ProductStock(); - $productStock->product_variant_id = $productVariant->id; - $productStock->stock_id = $stock->id; - } - $productStock->quantity = $count; - - $productStock->save(false); - $productStocks = ProductStock::find() - ->where( - [ 'product_variant_id' => $productVariant->id ] - ) - ->andWhere( - [ - '<>', - 'stock_id', - $stock->id, - ] - ) - ->all(); - - $quantity = array_sum(ArrayHelper::getColumn($productStocks, 'quantity')) + $count; - } else { - - $productStocks = ProductStock::find() - ->where( - [ 'product_variant_id' => $productVariant->id ] - ) - ->all(); - - if ($productStocks instanceof ProductStock) { - $quantity = array_sum(ArrayHelper::getColumn($productStocks, 'quantity')) + $count; - } else { - $quantity = 0; - } - - } - - if ($price_promo) { - $productVariant->price_old = $price; - $productVariant->price = $price_promo; - } else { - $productVariant->price = $price; - $productVariant->price_old = $price_promo; - } - - $productVariant->stock = $quantity; - - $productVariant->save(false); - - $this->output[] = 'Товар ' . $product_title . ' успешно сохранен'; - } - - $result = [ - 'end' => feof($handle), - 'from' => ftell($handle), - 'totalsize' => $filesize, - 'items' => $this->output, - - ]; - - fclose($handle); - - if ($result[ 'end' ]) { - unlink(Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFilePrices')); - } - - return $result; - } - - /** - * Pull name and remote_id from formatted string - * - * @param string $name - * - * @return array - */ - private function parseName(string $name):array - { - $pattern = '/^(?P.*)(?:\(#(?P\w+)#\))?$/U'; - $name = trim($name); - $matches = []; - if (preg_match($pattern, $name, $matches)) { - if (!isset( $matches[ 'remote_id' ] )) { - $matches[ 'remote_id' ] = ''; - } - return $matches; - } - return [ - 'name' => $name, - 'remote_id' => '', - ]; - } - - /** - * Save categories - * - * @param array $catalog_names - * - * @return int[] Category IDs - * @throws \Exception - */ - private function saveCatalog(array $catalog_names):array - { - $category_id = []; - - foreach ($catalog_names as $catalog_name) { - // ==== Set category ==== - $parsed_name = $this->parseName($catalog_name); - if (!empty( $parsed_name[ 'remote_id' ] ) && ( $category = Category::find() - ->joinWith('lang') - ->andFilterWhere( - [ 'remote_id' => $parsed_name[ 'remote_id' ] ] - ) - ->one() ) !== null - ) { - if (!empty( $category->lang )) { - $category->lang->title = $parsed_name[ 'name' ]; - $category->lang->save(false); - } else { - throw new \Exception( - 'Category with ID ' . $category->id . ' and lang ' . Language::getCurrent( - )->id . ' doesn\'t exist' - ); - } - - } else { - // Create category - $category = new Category(); - $category->generateLangs(); - $category_langs = $category->modelLangs; - foreach ($category_langs as $category_lang) { - $category_lang->title = $parsed_name[ 'name' ]; - } - $category->remote_id = $parsed_name[ 'remote_id' ]; - $category->save(false); - } - $category_id[] = $category->id; - } - return $category_id; - } - - /** - * Save brand - * - * @param string|null $brand_name - * - * @return int|null New Brand ID if inserted or exist or null if skipped - * @throws \Exception - */ - private function saveBrand(string $brand_name = null):int - { - - $parsed_name = $this->parseName($brand_name); - if (!empty( $brand_name )) { - /** - * @var Brand $brand - */ - if (!empty( $parsed_name[ 'remote_id' ] ) && ( $brand = Brand::find() - ->joinWith('lang') - ->andFilterWhere( - [ 'remote_id' => $parsed_name[ 'remote_id' ] ] - ) - ->one() ) !== null - ) { - if (!empty( $brand->lang )) { - $brand->lang->title = $parsed_name[ 'name' ]; - $brand->lang->save(false); - } else { - throw new \Exception( - 'Brand with ID ' . $brand->id . ' and lang ' . Language::getCurrent( - )->id . ' doesn\'t exist' - ); - } - return $brand->id; - } else { - // Create brand - $brand = new Brand(); - $brand->generateLangs(); - $brand_langs = $brand->modelLangs; - foreach ($brand_langs as $brand_lang) { - $brand_lang->title = $parsed_name[ 'name' ]; - } - $brand->remote_id = $parsed_name[ 'remote_id' ]; - $brand->save(false); - return $brand->id; - } - } - return null; - } - - /** - * Save Product or ProductVariant photoes - * - * @param string[] $fotos Photoes names - * @param int $product_id - * @param int|null $product_variant_id Null if photo for Product - */ - private function saveFotos(array $fotos, int $product_id, int $product_variant_id = null) - { - if (!empty( $fotos )) { - foreach ($fotos as $foto) { - if (empty( $foto )) { - continue; - } - $source_image = Yii::getAlias('@uploadDir') . '/product_images/' . urlencode($foto); - if (file_exists($source_image)) { - if (( $productImage = ProductImage::find() - ->andWhere([ 'image' => $foto ]) - ->andWhere([ 'product_id' => $product_id ]) - ->andFilterWhere( - [ 'product_variant_id' => $product_variant_id ] - ) - ->one() ) === null - ) { - copy($source_image, Yii::getAlias('@productsDir') . "/" . $foto); - $productImage = new ProductImage(); - $productImage->product_id = $product_id; - $productImage->product_variant_id = $product_variant_id; - $productImage->image = $foto; - $productImage->save(false); - } - } - } - } - } - - /** - * Save ProductVariants - * - * @param array $data ProductVariats data - * @param float $product_cost_old Old price - * @param int $product_id Product ID - * @param array $category_id Ca - * @param float|null $product_cost - * - * @return int[] Array of ProductVariants IDs - * @throws \Exception - */ - private function saveVariants( - array $data, - float $product_cost_old, - int $product_id, - array $category_id, - float $product_cost = null - ):array - { - $MOD_ARRAY = []; - for ($i = 13; $i < count($data); $i++) { - if (!empty ( $data[ $i ] )) { - $mod_arr = explode('=', $data[ $i ]); - $mod_art = $mod_arr[ 0 ]; - $mod_art_parsed = $this->parseName($mod_art); - $variant_filters = explode('*', $mod_arr[ 1 ]); - $mod_name = $mod_arr[ 2 ]; - if (empty( $mod_name )) { - $mod_name = $mod_art_parsed[ 'name' ]; - } - $mod_image = $mod_arr[ 3 ]; - $mod_stock = isset( $mod_arr[ 4 ] ) ? $mod_arr[ 4 ] : 1; - $mod_cost = isset( $product_cost ) ? floatval($product_cost) : 0; - $mod_old_cost = floatval($product_cost_old); - // Check product variant - /** - * @var ProductVariant $_productVariant - */ - if (( $_productVariant = ProductVariant::find() - ->joinWith('lang') - ->andFilterWhere( - [ 'remote_id' => $mod_art_parsed[ 'remote_id' ] ] - ) - ->andFilterWhere( - [ 'product_variant.product_id' => $product_id ] - ) - ->one() ) === null - ) { - $_productVariant = new ProductVariant(); - $_productVariant->product_id = $product_id; - if (!empty( $mod_art_parsed[ 'remote_id' ] )) { - $_productVariant->remote_id = $mod_art_parsed[ 'remote_id' ]; - } - $_productVariant->generateLangs(); - $product_variant_langs = $_productVariant->modelLangs; - foreach ($product_variant_langs as $product_variant_lang) { - $product_variant_lang->title = $mod_name; - } - } else { - if (!empty( $_productVariant->lang )) { - $_productVariant->lang->title = $mod_name; - $_productVariant->lang->save(false); - } else { - throw new \Exception( - 'Product variant with ID ' . $_productVariant->id . ' and lang ' . Language::getCurrent( - )->id . ' doesn\'t exist' - ); - } - } - $_productVariant->product_unit_id = 1; - $_productVariant->sku = $mod_art_parsed[ 'name' ]; - $_productVariant->price = $mod_cost; - $_productVariant->price_old = $mod_old_cost; - $_productVariant->stock = $mod_stock; - - if (!empty ( $variant_filters )) { - $variants_options = $this->saveFilters($variant_filters, 1, $category_id); - } - - if (isset( $variants_options ) && !empty( $variants_options )) { - $_productVariant->options = $variants_options; - } - - /** - * @todo set to false - */ - $_productVariant->save(false); - - $MOD_ARRAY[] = $_productVariant->id; - $this->saveFotos([ $mod_image ], $product_id, $_productVariant->id); - } - } - return $MOD_ARRAY; - } - - /** - * Perform product import - * - * @param int $from Begin row - * @param null $limit Row limit - * - * @return array|bool Array if OK, false if error - */ - public function goProducts($from = 0, $limit = null) - { - set_time_limit(0); - - if (!( $handle = $this->getProductsFile('uploadFileProducts') )) { - $this->errors[] = 'File not found'; - return false; - } - - $filesize = filesize(Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFileProducts')); - - if ($from) { - fseek($handle, $from); - } - - $j = 0; - - $is_utf = ( preg_match( - '//u', - file_get_contents( - Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFileProducts'), - null, - null, - null, - 1000000 - ) - ) ); - - $result_items = []; - - while (( empty( $limit ) || $j++ < $limit ) && ( $data = fgetcsv($handle, 10000, ";") ) !== false) { - try { - - foreach ($data as &$value) { - if (!$is_utf) { - $value = iconv('windows-1251', "UTF-8//TRANSLIT//IGNORE", $value); - } - $value = trim($value); - } - // будет всегда 19 элементов - for ($i = 0; $i <= 18; $i++) { - if (!isset ( $data[ $i ] )) { - $data[ $i ] = null; - } - } - // 1 Группа (категория) - $catalog_names = explode(',', $data[ 0 ]); - if (empty ( $catalog_names )) { - $result_items[] = "Не указана категория (строка $j)"; - continue; - } - - // 2 Бренд - $brand_name = $data[ 1 ]; - // if(empty ( $brand_name )) { - // $result_items[] = "Не указан бренд (строка $j)"; - // continue; - // } - - // 3 Название товара - $product_name = $data[ 2 ]; - if (empty ( $product_name )) { - $result_items[] = "Не указано наименование товара (строка $j)"; - continue; - } - - // 5 Описание товара - $product_body = $data[ 3 ]; - - // 6 Фильтр - $filters = explode('*', $data[ 4 ]); - - // 11 Цена акция - $product_cost_old = floatval($data[ 6 ]); - - $product_cost = null; - // 10 Цена - if ($product_cost_old) { - $product_cost_old = floatval($data[ 5 ]); - $product_cost = floatval($data[ 6 ]); - } - - // 12 Акция - $product_discount = (bool) $data[ 7 ]; - - // 13 Сопуд. Тов. - $similar = explode(',', $data[ 8 ]); - - // 14 Новинки - $product_new = (bool) $data[ 9 ]; - - // 15 Топ продаж - $product_top = (bool) $data[ 10 ]; - - // 17 ВИДЕО КОД - $product_video = $data[ 11 ]; - - // 18 Галлерея фото - $fotos = []; - if (trim($data[ 12 ])) { - $fotos = explode(',', trim($data[ 12 ])); - } - - // $lang = \Yii::$app->session->get('export_lang', Language::getDefaultLanguage()->id); - // /** - // * @var Language $language - // */ - // $language = Language::find() - // ->where([ 'id' => $lang ]) - // ->one(); - // Language::setCurrent($language->url); - $categories = $this->saveCatalog($catalog_names); - - $brand_id = $this->saveBrand($brand_name); - - $options = []; - if (!empty ( $filters )) { - $options = $this->saveFilters($filters, 0, $categories); - } - $parsed_name = $this->parseName($product_name); - /** - * @var Product $_product - */ - if (!empty( $parsed_name[ 'remote_id' ] ) && ( $_product = Product::find() - ->joinWith('lang') - ->andFilterWhere( - [ 'remote_id' => $parsed_name[ 'remote_id' ] ] - ) - ->one() ) !== null - ) { - if (!empty( $_product->lang )) { - $_product->lang->title = $parsed_name[ 'name' ]; - $_product->lang->description = $product_body; - $_product->lang->save(false); - } else { - throw new \Exception( - 'Product with ID ' . $_product->id . ' and lang ' . Language::getCurrent( - )->id . ' doesn\'t exist' - ); - } - } else { - $_product = new Product(); - $_product->generateLangs(); - $product_langs = $_product->modelLangs; - foreach ($product_langs as $product_lang) { - $product_lang->title = $parsed_name[ 'name' ]; - $product_lang->description = $product_body; - } - } - - $is_new_product = empty( $_product->id ); - - $_product->categories = $categories; - - $_product->brand_id = $brand_id; - - $_product->video = $product_video; - $_product->is_top = $product_top; - $_product->is_discount = $product_discount; - $_product->is_new = $product_new; - if (!empty( $options )) { - $_product->options = $options; - } - - if (!empty( $_product->lang )) { - $product_name_inserted = $_product->lang->title; - } else { - $product_name_inserted = $_product->modelLangs[ Language::$current->id ]->title; - } - - if (( $_product->save(false) === false ) || !$_product->transactionStatus) { - $result_items[] = 'Product #' . $product_name_inserted . ' not saved' . " (line $j)"; - continue; - } - - $this->saveFotos($fotos, $_product->id); - // нужно для проставления характеристик относящихся к модификациям - - $this->saveVariants($data, $product_cost_old, $_product->id, $_product->categories, $product_cost); - - // $_product->save(false); - - $result_items[] = "Product {$product_name_inserted} #{$_product->id} saved (" . ( $is_new_product ? 'new product' : 'exists product' ) . ")" . " (line $j)"; - - } catch (\Exception $e) { - $result_items[] = $e->getMessage() . '(line ' . $j . ')'; - } - - } - - $result = [ - 'end' => feof($handle), - 'from' => ftell($handle), - 'totalsize' => $filesize, - 'items' => $result_items, - ]; - - fclose($handle); - - if ($result[ 'end' ]) { - // unlink(Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFileProducts')); - } - - return $result; - } - - /** - * Get import file - * - * @param string $file_type - * - * @return bool|resource false if File not found and file resource if OK - */ - private function getProductsFile($file_type) - { - $filename = Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@' . $file_type); - if (!is_file($filename)) { - $this->errors[] = "File $filename not found"; - return false; - } - return fopen($filename, 'r'); - } - - /** - * Save filters - * - * @param array $filters array of filters like [['pol'='мужской'],['god' = - * '2013'],['volume'='25 л']*['size'='49 x 30 x - * 20см'],['composition'='600D полиэстер']] - * @param int $level 0 for products and 1 for product variant - * @param int[] $catalog_names array catalogs id - * - * @return array - * @throws \Exception - */ - private function saveFilters(array $filters, int $level, array $catalog_names):array - { - $options = []; - foreach ($filters as $filter) { - - preg_match_all('/\[(.*):(.*)\]/', $filter, $filter); - - if (empty( $filter[ 1 ][ 0 ] )) { - continue; - } - $filter_name = trim($filter[ 1 ][ 0 ]); - $parsed_group_name = $this->parseName($filter_name); - - /** - * @var TaxGroup $taxGroup - */ - if (!empty( $parsed_group_name[ 'remote_id' ] ) && ( $taxGroup = TaxGroup::find() - ->joinWith('lang') - ->andFilterWhere( - [ 'remote_id' => $parsed_group_name[ 'remote_id' ] ] - ) - ->one() ) !== null - ) { - if (!empty( $taxGroup->lang )) { - $taxGroup->lang->title = $parsed_group_name[ 'name' ]; - $taxGroup->lang->save(false); - } else { - throw new \Exception( - 'Tax group with ID ' . $taxGroup->id . ' and lang ' . Language::getCurrent( - )->id . ' doesn\'t exist' - ); - } - } else { - $taxGroup = new TaxGroup(); - $taxGroup->generateLangs(); - $tax_group_langs = $taxGroup->modelLangs; - foreach ($tax_group_langs as $tax_group_lang) { - $tax_group_lang->title = $parsed_group_name[ 'name' ]; - } - $taxGroup->level = $level; - $taxGroup->categories = $catalog_names; - $taxGroup->is_filter = false; - $taxGroup->save(false); - } - $filters_options = explode(',', $filter[ 2 ][ 0 ]); - foreach ($filters_options as $filter_options) { - $parsed_option_name = $this->parseName($filter_options); - /** - * @var TaxOption $option - */ - - if (!empty( $parsed_option_name[ 'remote_id' ] ) && ( $option = TaxOption::find() - ->joinWith('lang') - ->andFilterWhere( - [ 'remote_id' => $parsed_option_name[ 'remote_id' ] ] - ) - ->andFilterWhere( - [ 'tax_group_id' => $taxGroup->id ] - ) - ->one() ) !== null - ) { - if (!empty( $option->lang )) { - $option->lang->value = $parsed_option_name[ 'name' ]; - $option->lang->save(false); - } else { - throw new \Exception( - 'Tax option with ID ' . $option->id . ' and lang ' . Language::getCurrent( - )->id . ' doesn\'t exist' - ); - } - } else { - // Create option - $option = new TaxOption(); - $option->generateLangs(); - $option_langs = $option->modelLangs; - foreach ($option_langs as $option_lang) { - $option_lang->value = $parsed_option_name[ 'name' ]; - } - $option->id = $taxGroup->id; - $option->save(false); - } - $options[] = $option->id; - } - } - return $options; - } - } - \ No newline at end of file diff --git a/artbox-ecommerce/models/Product.php b/artbox-ecommerce/models/Product.php deleted file mode 100755 index bae90fe..0000000 --- a/artbox-ecommerce/models/Product.php +++ /dev/null @@ -1,614 +0,0 @@ - [ - 'class' => SaveMultipleFileBehavior::className(), - 'name' => 'imagesUpload', - 'directory' => 'products', - 'column' => 'image', - 'links' => [ - 'id' => 'product_id', - ], - 'model' => ProductImage::className(), - ], - 'multipleImage' => [ - 'class' => MultipleImgBehavior::className(), - 'links' => [ - 'product_id' => 'id', - ], - 'conditions' => [ - 'product_variant_id' => null, - ], - 'model' => ProductImage::className(), - 'config' => [ - 'caption' => 'image', - 'delete_action' => '/product/manage/delete-image', - 'id' => 'id', - ], - ], - 'language' => [ - 'class' => LanguageBehavior::className(), - ], - 'defaultVariant' => DefaultVariantBehavior::className(), - ]; - } - - /** - * @inheritdoc - */ - public static function tableName() - { - return 'product'; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'brand_id' ], - 'integer', - ], - [ - [ - 'categories', - 'variants', - 'options', - 'imagesUpload', - ], - 'safe', - ], - [ - [ - 'video', - ], - 'safe', - ], - [ - [ - 'is_top', - 'is_new', - 'is_discount', - ], - 'boolean', - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('product', 'ID'), - 'brand_id' => Yii::t('product', 'Brand'), - 'categories' => Yii::t('product', 'Categories'), - 'category' => Yii::t('product', 'Category'), - 'image' => Yii::t('product', 'Image'), - 'images' => Yii::t('product', 'Images'), - 'video' => Yii::t('product', 'Video embeded'), - 'variants' => Yii::t('product', 'Variants'), - 'is_top' => Yii::t('product', 'Is top'), - 'is_new' => Yii::t('product', 'Is new'), - 'is_discount' => Yii::t('product', 'Is promo'), - ]; - } - - /** - * Get Brand query to current Product - * - * @return \yii\db\ActiveQuery - */ - public function getBrand() - { - return $this->hasOne(Brand::className(), [ 'id' => 'brand_id' ]); - } - - /** - * Get ProductVariant query to current Product - * - * @return \yii\db\ActiveQuery - */ - public function getVariant() - { - return $this->hasOne(ProductVariant::className(), [ 'product_id' => 'id' ]); - } - - /** - * Synonim of getVariant() - * - * @see Product::getVariant() - * @return \yii\db\ActiveQuery - */ - public function getProductVariant() - { - return $this->getVariant(); - } - - /** - * Get ProductVariants query to current Product - * - * @return \yii\db\ActiveQuery - */ - public function getVariants() - { - return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]); - } - - /** - * Synonim of getVariants() - * - * @see Product::getVariants() - * @return \yii\db\ActiveQuery - */ - public function getProductVariants() - { - return $this->getVariant(); - } - - /** - * Get ProductVariant query fetching only available in stock to current Product - * - * @see Product::getVariant() - * @return \yii\db\ActiveQuery - */ - public function getEnabledVariant() - { - return $this->hasOne(ProductVariant::className(), [ 'product_id' => 'id' ]) - ->andWhere( - [ - '!=', - ProductVariant::tableName() . '.stock', - 0, - ] - ); - } - - /** - * Get ProductVariants query fetching only available in stock to current Product - * - * @see Product::getVariants() - * @return \yii\db\ActiveQuery - */ - public function getEnabledVariants() - { - return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]) - ->andWhere( - [ - '!=', - ProductVariant::tableName() . '.stock', - 0, - ] - ); - } - - /** - * Get random ProductVariant price or 0 if not exist - * - * @param bool $exception Whether to throw exception if variant not exist - * - * @return float - * @throws \yii\web\NotFoundHttpException - */ - public function getVariantPrice(bool $exception = false): float - { - if (!empty( $this->variant )) { - return $this->variant->price; - } elseif ($exception) { - throw new NotFoundHttpException('Product with ID ' . $this->id . ' hasn\'t got variants'); - } else { - return 0; - } - } - - /** - * Get random ProductVariant that in stock price or 0 or exception if not exist - * - * @param bool $exception Whether to throw exception if variant not exist - * - * @return float - * @throws \yii\web\NotFoundHttpException - */ - public function getEnabledVariantPrice(bool $exception = false): float - { - if (!empty( $this->enabledVariant )) { - return $this->enabledVariant->price; - } elseif ($exception) { - throw new NotFoundHttpException('Product with ID ' . $this->id . ' hasn\'t got enabled variants'); - } else { - return 0; - } - } - - /** - * Get Product name concatenated with Brand name - * - * @return string - */ - public function getFullname():string - { - return empty( $this->brand ) ? $this->lang->title : $this->brand->lang->title . ' ' . $this->lang->title; - } - - /** - * Get Category query for current Product - * - * @return ActiveQuery - */ - public function getCategory() - { - return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]) - ->viaTable('product_category', [ 'product_id' => 'id' ]); - } - - /** - * Get Categories query for current Product - * - * @return ActiveQuery - */ - public function getCategories() - { - return $this->hasMany(Category::className(), [ 'id' => 'category_id' ]) - ->viaTable('product_category', [ 'product_id' => 'id' ]); - } - - /** - * @param bool $index - * - * @return array - */ - public function getCategoryNames(bool $index = false): array - { - if ($index) { - $result = ArrayHelper::map($this->categories, 'id', 'lang.title'); - } else { - $result = ArrayHelper::getColumn($this->categories, 'lang.title'); - } - return $result; - } - - /** - * Get ProductVariants query with lang, filters and image for current Product - * - * @return ActiveQuery - */ - public function getVariantsWithFilters() - { - return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]) - ->joinWith('lang') - ->with( - [ - 'filters', - 'image', - ] - ); - } - - /** - * Get TaxOptions query for current Product - * - * @return ActiveQuery - */ - public function getOptions() - { - return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) - ->viaTable('product_option', [ 'product_id' => 'id' ]); - } - - /** - * Get TaxOptions query for current Product joined with TaxGroups - * - * @see Product::getOptions() - * @return ActiveQuery - */ - public function getFilters() - { - return $this->getOptions() - ->joinWith('taxGroup.lang') - ->joinWith('lang'); - } - - /** - * Get all TaxGroups for current Product filled with $customOptions that satisfy current Product - * - * @return TaxGroup[] - */ - public function getProperties(): array - { - $groups = $options = []; - foreach ($this->getOptions() - ->with('lang') - ->all() as $option) { - /** - * @var TaxOption $option - */ - $options[ $option[ 'tax_group_id' ] ][] = $option; - } - foreach (TaxGroup::find() - ->where([ 'id' => array_keys($options) ]) - ->with('lang') - ->all() as $group) { - /** - * @var TaxGroup $group - */ - if (!empty( $options[ $group->id ] )) { - $group->customOptions = $options[ $group->id ]; - $groups[] = $group; - } - } - return $groups; - } - - /** - * Get Stock query where current Product is in stock - * - * @return ActiveQuery - */ - public function getStocks() - { - return $this->hasMany(Stock::className(), [ 'id' => 'stock_id' ]) - ->via('productStocks'); - } - - /** - * Get ProductStocks query for current Product - * - * @return ActiveQuery - */ - public function getProductStocks() - { - return $this->hasMany(ProductStock::className(), [ 'product_variant_id' => 'id' ]) - ->via('variants'); - } - - /** - * Get quantity of all ProductVariants for current Product - * - * @see Product::getProductStocks() - * @return int - */ - public function getQuantity():int - { - return $this->getProductStocks() - ->sum('quantity'); - } - - /** - * Override Categories and TaxOptions - * - * @inheritdoc - */ - public function afterSave($insert, $changedAttributes) - { - parent::afterSave($insert, $changedAttributes); - - if (!empty( $this->categories )) { - $categories = Category::findAll($this->categories); - $this->unlinkAll('categories', true); - foreach ($categories as $category) { - $this->link('categories', $category); - } - } - - if (!empty( $this->options )) { - $options = TaxOption::findAll($this->options); - $this->unlinkAll('options', true); - foreach ($options as $option) { - $this->link('options', $option); - } - } - } - - /** - * Recalculate rating for artboxcomment module - * - * @todo Rewrite with behavior - * @return bool - */ - public function recalculateRating():bool - { - /** - * @var ProductToRating $averageRating - */ - $average = $this->getComments() - ->joinWith('rating') - ->select([ 'average' => 'avg(artbox_comment_rating.value)::float' ]) - ->scalar(); - if (!$average) { - $average = 0; - } - $averageRating = $this->averageRating; - if (!empty( $averageRating )) { - $averageRating->value = $average; - } else { - $averageRating = new ProductToRating( - [ - 'product_id' => $this->id, - 'value' => $average, - ] - ); - } - if ($averageRating->save()) { - return true; - } else { - return false; - } - } - - /** - * Get CommmentModel query for artboxcomment module - * - * @todo Rewrite with behavior - * @return ActiveQuery - */ - public function getComments() - { - return $this->hasMany(CommentModel::className(), [ 'entity_id' => 'id' ]) - ->where( - [ - 'artbox_comment.entity' => self::className(), - 'artbox_comment.status' => CommentModel::STATUS_ACTIVE, - 'artbox_comment.artbox_comment_pid' => null, - ] - ); - } - - /** - * Get ProductToRating query in order to get average rating for current Product - * - * @return \yii\db\ActiveQuery - */ - public function getAverageRating() - { - return $this->hasOne(ProductToRating::className(), [ 'product_id' => 'id' ]); - } - - /** - * Get TaxGroupToCategories query via product_category table - * - * @return ActiveQuery - */ - public function getCategoriesToGroups() - { - return $this->hasMany(TaxGroupToCategory::className(), [ 'category_id' => 'category_id' ]) - ->viaTable('product_category', [ 'product_id' => 'id' ]); - } - - /** - * Get TaxGroups query for current Product according to level - * * 0 - Product Tax Groups - * * 1 - ProductVariant Tax Groups - * - * @param int $level - * - * @return ActiveQuery - * @throws InvalidParamException - */ - public function getTaxGroupsByLevel(int $level = 0) - { - if ($level !== 0 && $level !== 1) { - throw new InvalidParamException( - 'Level must be 0 for Product Tax Groups or 1 for Product Variant Tax Groups' - ); - } - return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) - ->via('categoriesToGroups') - ->where([ 'level' => $level ]) - ->distinct(); - } - - /** - * Setter for Categories - * - * @param array $values - */ - public function setCategories($values) - { - $this->categories = $values; - } - - /** - * Setter for Options - * - * @param array $values - */ - public function setOptions($values) - { - $this->options = $values; - } - } diff --git a/artbox-ecommerce/models/ProductCategory.php b/artbox-ecommerce/models/ProductCategory.php deleted file mode 100755 index d304460..0000000 --- a/artbox-ecommerce/models/ProductCategory.php +++ /dev/null @@ -1,74 +0,0 @@ - true, - 'targetClass' => Category::className(), - 'targetAttribute' => [ 'category_id' => 'id' ], - ], - [ - [ 'product_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Product::className(), - 'targetAttribute' => [ 'product_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'product_id' => Yii::t('product', 'Product'), - 'category_id' => Yii::t('product', 'Category'), - ]; - } - - public function getCategory() - { - return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]); - } - - public function getProduct() - { - return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); - } - } diff --git a/artbox-ecommerce/models/ProductImage.php b/artbox-ecommerce/models/ProductImage.php deleted file mode 100755 index 7adc9ed..0000000 --- a/artbox-ecommerce/models/ProductImage.php +++ /dev/null @@ -1,120 +0,0 @@ - ImageBehavior::className(), - 'link' => 'image', - 'directory' => 'products', - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'product_id' ], - 'required', - ], - [ - [ - 'id', - 'product_id', - 'product_variant_id', - ], - 'integer', - ], - [ - [ - 'alt', - 'title', - 'image', - ], - 'string', - 'max' => 255, - ], - [ - [ 'product_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Product::className(), - 'targetAttribute' => [ 'product_id' => 'id' ], - ], - [ - [ 'product_variant_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => ProductVariant::className(), - 'targetAttribute' => [ 'product_variant_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('product', 'Product Image ID'), - 'product_id' => Yii::t('product', 'Product ID'), - 'product_variant_id' => Yii::t('product', 'Product Variant ID'), - 'product' => Yii::t('product', 'Product'), - 'product_variant' => Yii::t('product', 'Product Variant'), - 'image' => Yii::t('product', 'Image'), - 'alt' => Yii::t('product', 'Alt'), - 'title' => Yii::t('product', 'Title'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProduct() - { - $return = $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); - return $return; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductVariant() - { - return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]); - } - } diff --git a/artbox-ecommerce/models/ProductLang.php b/artbox-ecommerce/models/ProductLang.php deleted file mode 100755 index 6792271..0000000 --- a/artbox-ecommerce/models/ProductLang.php +++ /dev/null @@ -1,127 +0,0 @@ - [ - 'class' => 'common\behaviors\Slug', - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'title' ], - 'required', - ], - [ - [ 'description' ], - 'string', - ], - [ - [ - 'title', - 'alias', - ], - 'string', - 'max' => 255, - ], - [ - [ - 'product_id', - 'language_id', - ], - 'unique', - 'targetAttribute' => [ - 'product_id', - 'language_id', - ], - 'message' => 'The combination of Product ID and Language ID has already been taken.', - ], - [ - [ 'language_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Language::className(), - 'targetAttribute' => [ 'language_id' => 'id' ], - ], - [ - [ 'product_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Product::className(), - 'targetAttribute' => [ 'product_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'product_id' => Yii::t('app', 'Product ID'), - 'language_id' => Yii::t('app', 'Language ID'), - 'title' => Yii::t('app', 'Name'), - 'description' => Yii::t('app', 'Description'), - '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 getProduct() - { - return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); - } - } diff --git a/artbox-ecommerce/models/ProductOption.php b/artbox-ecommerce/models/ProductOption.php deleted file mode 100755 index bb54b21..0000000 --- a/artbox-ecommerce/models/ProductOption.php +++ /dev/null @@ -1,90 +0,0 @@ - true, - 'targetClass' => Product::className(), - 'targetAttribute' => [ 'product_id' => 'id' ], - ], - [ - [ 'option_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => TaxOption::className(), - 'targetAttribute' => [ 'option_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'product_id' => Yii::t('product', 'Product ID'), - 'option_id' => Yii::t('product', 'Option ID'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProduct() - { - return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getOption() - { - return $this->hasOne(TaxOption::className(), [ 'id' => 'option_id' ]); - } - } diff --git a/artbox-ecommerce/models/ProductSearch.php b/artbox-ecommerce/models/ProductSearch.php deleted file mode 100755 index 19e1bef..0000000 --- a/artbox-ecommerce/models/ProductSearch.php +++ /dev/null @@ -1,192 +0,0 @@ - 'Category ID', - 'brand_id' => 'Brand ID', - 'productName' => 'Product name', - 'variantCount' => 'Variant count', - ]; - return array_merge($labels, $new_labels); - } - - /** - * @inheritdoc - */ - public function scenarios() - { - // bypass scenarios() implementation in the parent class - return Model::scenarios(); - } - - /** - * Creates data provider instance with search query applied - * - * @param array $params - * - * @return ActiveDataProvider - */ - public function search($params) - { - $query = Product::find(); - $query->select( - [ - 'product.*', - ] - ); - - $query->joinWith( - [ - 'categories', - 'lang', - ] - ) - ->joinWith( - [ - 'brand' => function ($query) { - /** - * @var ActiveQuery $query - */ - $query->joinWith('lang'); - }, - ] - ) - ->joinWith('variants'); - - $query->groupBy( - [ - 'product.id', - 'product_variant.id', - ] - ); - - $dataProvider = new ActiveDataProvider( - [ - 'query' => $query, - ] - ); - - $dataProvider->setSort( - [ - 'attributes' => [ - 'id', - 'productName' => [ - 'asc' => [ 'product_lang.title' => SORT_ASC ], - 'desc' => [ 'product_lang.title' => SORT_DESC ], - ], - 'brand_id' => [ - 'asc' => [ 'brand_lang.title' => SORT_ASC ], - 'desc' => [ 'brand_lang.title' => SORT_DESC ], - ], - ], - ] - ); - - $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; - } - - if (isset( $this->is_top )) { - $query->andWhere( - [ - 'is_top' => (bool) $this->is_top, - ] - ); - } - if (isset( $this->is_new )) { - $query->andWhere( - [ - 'is_new' => (bool) $this->is_new, - ] - ); - } - if (isset( $this->is_discount )) { - $query->andWhere( - [ - 'is_discount' => (bool) $this->is_discount, - ] - ); - } - $query->andFilterWhere( - [ - 'product.brand_id' => $this->brand_id, - 'product.id' => $this->id, - 'product_category.category_id' => $this->categoryId, - ] - ); - $query->andFilterWhere( - [ - 'like', - 'product_lang.title', - $this->productName, - ] - ); - - return $dataProvider; - } - } diff --git a/artbox-ecommerce/models/ProductStock.php b/artbox-ecommerce/models/ProductStock.php deleted file mode 100755 index 8785cbc..0000000 --- a/artbox-ecommerce/models/ProductStock.php +++ /dev/null @@ -1,138 +0,0 @@ - true, - 'targetClass' => ProductVariant::className(), - 'targetAttribute' => [ 'product_variant_id' => 'id' ], - ], - [ - [ 'stock_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Stock::className(), - 'targetAttribute' => [ 'stock_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'stock_id' => 'Stock ID', - 'quantity' => 'Количество', - 'product_variant_id' => 'Product Variant ID', - 'title' => "Название", - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProduct() - { - return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]) - ->via('variants'); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductVariant() - { - return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]); - } - - /** - * Get Stock title, tries to get from Stock lang - * - * @return string - */ - public function getTitle(): string - { - if (!empty( $this->title )) { - return $this->title; - } elseif (!empty( $this->stock )) { - return $this->stock->lang->title; - } else { - return ''; - } - } - - /** - * Set Stock title, will be saved to Stock table - * - * @param mixed $value - */ - public function setTitle(string $value) - { - $this->title = $value; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getStock() - { - return $this->hasOne(Stock::className(), [ 'id' => 'stock_id' ]); - } - - /** - * @inheritdoc - */ - public static function primaryKey() - { - return [ - "stock_id", - "product_variant_id", - ]; - } - } diff --git a/artbox-ecommerce/models/ProductUnit.php b/artbox-ecommerce/models/ProductUnit.php deleted file mode 100755 index 62d5428..0000000 --- a/artbox-ecommerce/models/ProductUnit.php +++ /dev/null @@ -1,98 +0,0 @@ - [ - 'class' => LanguageBehavior::className(), - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'is_default' ], - 'boolean', - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('product', 'Product Unit ID'), - 'is_default' => Yii::t('product', 'Is Default'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getCategories() - { - return $this->hasMany(Category::className(), [ 'product_unit_id' => 'id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductVariants() - { - return $this->hasMany(ProductVariant::className(), [ 'product_unit_id' => 'id' ]); - } - } diff --git a/artbox-ecommerce/models/ProductUnitLang.php b/artbox-ecommerce/models/ProductUnitLang.php deleted file mode 100755 index c49d76a..0000000 --- a/artbox-ecommerce/models/ProductUnitLang.php +++ /dev/null @@ -1,113 +0,0 @@ - 255, - ], - [ - [ - 'product_unit_id', - 'language_id', - ], - 'unique', - 'targetAttribute' => [ - 'product_unit_id', - 'language_id', - ], - 'message' => 'The combination of Product Unit ID and Language ID has already been taken.', - ], - [ - [ 'language_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Language::className(), - 'targetAttribute' => [ 'language_id' => 'id' ], - ], - [ - [ 'product_unit_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => ProductUnit::className(), - 'targetAttribute' => [ 'product_unit_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'product_unit_id' => Yii::t('app', 'Product Unit ID'), - 'language_id' => Yii::t('app', 'Language ID'), - 'title' => Yii::t('app', 'Name'), - 'short' => Yii::t('app', 'Short'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getLanguage() - { - return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductUnit() - { - return $this->hasOne(ProductUnit::className(), [ 'id' => 'product_unit_id' ]); - } - } diff --git a/artbox-ecommerce/models/ProductUnitSearch.php b/artbox-ecommerce/models/ProductUnitSearch.php deleted file mode 100755 index ac894aa..0000000 --- a/artbox-ecommerce/models/ProductUnitSearch.php +++ /dev/null @@ -1,116 +0,0 @@ - \Yii::t('product', 'Product Unit Name'), - ]; - return array_merge($labels, $new_labels); - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'title' ], - 'safe', - ], - [ - [ 'id' ], - 'integer', - ], - [ - [ 'is_default' ], - 'boolean', - ], - ]; - } - - /** - * @inheritdoc - */ - public function scenarios() - { - // bypass scenarios() implementation in the parent class - return Model::scenarios(); - } - - /** - * Creates data provider instance with search query applied - * - * @param array $params - * - * @return ActiveDataProvider - */ - public function search($params) - { - $query = ProductUnit::find() - ->joinWith('lang'); - - // add conditions that should always apply here - - $dataProvider = new ActiveDataProvider( - [ - 'query' => $query, - 'sort' => [ - 'attributes' => [ - 'id', - 'is_defaut', - 'title' => [ - 'asc' => [ 'product_unit_lang.title' => SORT_ASC ], - 'desc' => [ 'product_unit_lang.title' => SORT_DESC ], - ], - ], - ], - ] - ); - - $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, - 'is_default' => $this->is_default, - ] - ) - ->andFilterWhere( - [ - 'ilike', - 'product_unit_lang.title', - $this->title, - ] - ); - - return $dataProvider; - } - } diff --git a/artbox-ecommerce/models/ProductVariant.php b/artbox-ecommerce/models/ProductVariant.php deleted file mode 100755 index 4505318..0000000 --- a/artbox-ecommerce/models/ProductVariant.php +++ /dev/null @@ -1,418 +0,0 @@ - [ - 'class' => LanguageBehavior::className(), - ], - 'images' => [ - 'class' => SaveMultipleFileBehavior::className(), - 'name' => 'imagesUpload', - 'directory' => 'products', - 'column' => 'image', - 'links' => [ - 'product_id' => 'product_id', - 'id' => 'product_variant_id', - ], - 'model' => ProductImage::className(), - ], - 'multipleImage' => [ - 'class' => MultipleImgBehavior::className(), - 'links' => [ - 'product_variant_id' => 'id', - ], - 'model' => ProductImage::className(), - 'config' => [ - 'caption' => 'image', - 'delete_action' => '/product/variant/delete-image', - 'id' => 'id', - ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ - 'product_id', - 'product_unit_id', - ], - 'required', - ], - [ - [ - 'product_id', - 'product_unit_id', - ], - 'integer', - ], - [ - [ - 'price', - 'price_old', - 'stock', - ], - 'number', - ], - [ - [ - 'sku', - ], - 'string', - 'max' => 255, - ], - [ - [ - 'options', - ], - 'safe', - ], - [ - [ 'product_unit_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => ProductUnit::className(), - 'targetAttribute' => [ 'product_unit_id' => 'id' ], - ], - [ - [ 'product_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Product::className(), - 'targetAttribute' => [ 'product_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('product', 'Product Variant ID'), - 'product_id' => Yii::t('product', 'Product ID'), - 'sku' => Yii::t('product', 'Sku'), - 'price' => Yii::t('product', 'Price'), - 'price_old' => Yii::t('product', 'Price Old'), - 'stock' => Yii::t('product', 'Stock'), - 'product_unit_id' => Yii::t('product', 'Product Unit ID'), - 'stock_caption' => Yii::t('product', 'Stock'), - 'image' => Yii::t('product', 'Image'), - 'images' => Yii::t('product', 'Images'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductUnit() - { - return $this->hasOne(ProductUnit::className(), [ 'id' => 'product_unit_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProduct() - { - return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductStocks() - { - return $this->hasMany(ProductStock::className(), [ 'product_variant_id' => 'id' ]); - } - - /** - * Get qunatity for current ProductVariant - * If $recalculate set to true will recalculate stock via product_stock table - * - * @param bool $recalculate - * - * @return int - */ - public function getQuantity(bool $recalculate = false): int - { - if (!$recalculate) { - return $this->stock; - } else { - $quantity = $this->getProductStocks() - ->sum('quantity'); - if (empty( $quantity )) { - $this->stock = 0; - } else { - $this->stock = (int) $quantity; - } - $this->save(false, [ 'stock' ]); - return $this->stock; - } - } - - /** - * Get ProductStocks query woth preloaded Stocks for current ProductVariant - * **Used in dynamic fields in product variant form** - * - * @return ActiveQuery - */ - public function getVariantStocks() - { - return $this->getProductStocks() - ->joinWith('stock'); - } - - /** - * @return ActiveQuery - */ - public function getStocks() - { - return $this->hasMany(Stock::className(), [ 'id' => 'stock_id' ]) - ->via('productStocks'); - } - - /** - * @return ActiveQuery - */ - public function getOptions() - { - return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) - ->viaTable('product_variant_option', [ 'product_variant_id' => 'id' ]); - } - - /** - * Get TaxOptions with preloaded TaxGroups for current ProductVariant - * - * @return ActiveQuery - */ - public function getFilters() - { - return $this->getOptions() - ->joinWith('taxGroup.lang') - ->joinWith('lang'); - } - - /** - * Get Product title concanated with current ProductVariant title - * - * @return string - */ - public function getFullname(): string - { - return $this->product->lang->title . ' ' . $this->lang->title; - } - - /** - * Set Options to override previous - * - * @param int[] $values - */ - public function setOptions($values) - { - $this->options = $values; - } - - /** - * Get all TaxGroups for current ProductVariant filled with $customOptions that satisfy current ProductVariant - * - * @return TaxGroup[] - */ - public function getProperties() - { - $groups = $options = []; - foreach ($this->getOptions() - ->with('lang') - ->all() as $option) { - /** - * @var TaxOption $option - */ - $options[ $option->tax_group_id ][] = $option; - } - foreach (TaxGroup::find() - ->where([ 'tax_group.id' => array_keys($options) ]) - ->orderBy([ 'sort' => SORT_ASC ]) - ->with('lang') - ->all() as $group) { - /** - * @var TaxGroup $group - */ - if (!empty( $options[ $group->id ] )) { - $group->customOptions = $options[ $group->id ]; - $groups[] = $group; - } - } - return $groups; - } - - /** - * Set stocks to override existing in product_stock table - * - * @param mixed $stocks - */ - public function setStocks($stocks) - { - $this->stocks = (array) $stocks; - } - - /** - * @return ActiveQuery - */ - public function getCategory() - { - return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]) - ->viaTable('product_category', [ 'product_id' => 'product_id' ]); - } - - /** - * @return ActiveQuery - */ - public function getCategories() - { - return $this->hasMany(Category::className(), [ 'id' => 'category_id' ]) - ->viaTable('product_category', [ 'product_id' => 'product_id' ]); - } - - /** - * Get TaxGroups query for current ProductVariant according to level - * * 0 - Product Tax Groups - * * 1 - ProductVariant Tax Groups - * - * @param int $level - * - * @return ActiveQuery - * @throws InvalidParamException - */ - public function getTaxGroupsByLevel(int $level = 0) - { - return $this->product->getTaxGroupsByLevel($level); - } - - public function afterSave($insert, $changedAttributes) - { - parent::afterSave($insert, $changedAttributes); - if (!empty( $this->options )) { - $options = TaxOption::findAll($this->options); - $this->unlinkAll('options', true); - foreach ($options as $option) { - $this->link('options', $option); - } - } - - if (!empty( $this->stocks )) { - ProductStock::deleteAll([ 'product_variant_id' => $this->id ]); - foreach ($this->stocks as $id => $quantity) { - /** - * @var ProductStock $productStock - */ - $productStock = ProductStock::find() - ->where( - [ - 'product_variant_id' => $this->id, - 'stock_id' => $id, - ] - ) - ->one(); - $productStock->quantity = $quantity; - $productStock->save(); - } - } - } - } diff --git a/artbox-ecommerce/models/ProductVariantLang.php b/artbox-ecommerce/models/ProductVariantLang.php deleted file mode 100755 index cb12b4a..0000000 --- a/artbox-ecommerce/models/ProductVariantLang.php +++ /dev/null @@ -1,108 +0,0 @@ - 255, - ], - [ - [ - 'product_variant_id', - 'language_id', - ], - 'unique', - 'targetAttribute' => [ - 'product_variant_id', - 'language_id', - ], - 'message' => 'The combination of Product Variant ID and Language ID has already been taken.', - ], - [ - [ 'language_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Language::className(), - 'targetAttribute' => [ 'language_id' => 'id' ], - ], - [ - [ 'product_variant_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => ProductVariant::className(), - 'targetAttribute' => [ 'product_variant_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'product_variant_id' => Yii::t('app', 'Product Variant ID'), - 'language_id' => Yii::t('app', 'Language ID'), - 'title' => Yii::t('app', 'Name'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getLanguage() - { - return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductVariant() - { - return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]); - } - } diff --git a/artbox-ecommerce/models/ProductVariantOption.php b/artbox-ecommerce/models/ProductVariantOption.php deleted file mode 100755 index da29e6c..0000000 --- a/artbox-ecommerce/models/ProductVariantOption.php +++ /dev/null @@ -1,90 +0,0 @@ - true, - 'targetClass' => ProductVariant::className(), - 'targetAttribute' => [ 'product_variant_id' => 'id' ], - ], - [ - [ 'option_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => TaxOption::className(), - 'targetAttribute' => [ 'option_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'product_variant_id' => 'Product Variant ID', - 'option_id' => 'Option ID', - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductVariant() - { - return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getOption() - { - return $this->hasOne(TaxOption::className(), [ 'id' => 'option_id' ]); - } - } diff --git a/artbox-ecommerce/models/ProductVariantSearch.php b/artbox-ecommerce/models/ProductVariantSearch.php deleted file mode 100755 index 02779dc..0000000 --- a/artbox-ecommerce/models/ProductVariantSearch.php +++ /dev/null @@ -1,137 +0,0 @@ -joinWith('lang'); - - // add conditions that should always apply here - - $dataProvider = new ActiveDataProvider( - [ - 'query' => $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; - } - - $dataProvider->setSort( - [ - 'attributes' => [ - 'id', - 'sku', - 'variantName' => [ - 'asc' => [ 'product_variant_lang.title' => SORT_ASC ], - 'desc' => [ 'product_variant_lang.title' => SORT_DESC ], - ], - 'price', - 'price_old', - 'stock', - ], - ] - ); - - $query->andFilterWhere( - [ - 'price' => $this->price, - 'price_old' => $this->price_old, - 'stock' => $this->stock, - ] - ); - - $query->andFilterWhere( - [ - 'ilike', - 'product_variant_lang.title', - $this->variantName, - ] - ) - ->andFilterWhere( - [ - 'ilike', - 'sku', - $this->sku, - ] - ); - - $query->groupBy( - [ - 'product_variant.id', - 'product_variant_lang.title', - ] - ); - - return $dataProvider; - } - } diff --git a/artbox-ecommerce/models/Stock.php b/artbox-ecommerce/models/Stock.php deleted file mode 100755 index 8eb6c62..0000000 --- a/artbox-ecommerce/models/Stock.php +++ /dev/null @@ -1,93 +0,0 @@ - [ - 'class' => LanguageBehavior::className(), - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('product', 'Stock ID'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getProductStocks() - { - return $this->hasMany(ProductStock::className(), [ 'stock_id' => 'id' ]); - } - - /** - * @return ActiveQuery - */ - public function getProductVariants() - { - return $this->hasMany(ProductVariant::className(), [ 'id' => 'product_variant_id' ]) - ->via('productStocks'); - } - - /** - * @return ActiveQuery - */ - public function getProducts() - { - return $this->hasMany(Product::className(), [ 'id' => 'product_id' ]) - ->via('productVariants'); - } - } diff --git a/artbox-ecommerce/models/StockLang.php b/artbox-ecommerce/models/StockLang.php deleted file mode 100644 index 2024ad3..0000000 --- a/artbox-ecommerce/models/StockLang.php +++ /dev/null @@ -1,110 +0,0 @@ - 255, - ], - [ - [ - 'stock_id', - 'language_id', - ], - 'unique', - 'targetAttribute' => [ - 'stock_id', - 'language_id', - ], - 'message' => 'The combination of Stock ID and Language ID has already been taken.', - ], - [ - [ 'stock_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Stock::className(), - 'targetAttribute' => [ 'stock_id' => 'id' ], - ], - [ - [ 'language_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Language::className(), - 'targetAttribute' => [ 'language_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'stock_id' => Yii::t('app', 'Stock ID'), - 'language_id' => Yii::t('app', 'Language ID'), - 'title' => Yii::t('app', 'Name'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getStock() - { - return $this->hasOne(Stock::className(), [ 'id' => 'stock_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getLanguage() - { - return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); - } - } diff --git a/artbox-ecommerce/models/TaxGroup.php b/artbox-ecommerce/models/TaxGroup.php deleted file mode 100755 index c87a0ac..0000000 --- a/artbox-ecommerce/models/TaxGroup.php +++ /dev/null @@ -1,225 +0,0 @@ - [ - 'class' => LanguageBehavior::className(), - ], - ]; - } - - /** - * @inheritdoc - */ - public static function tableName() - { - return 'tax_group'; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ - 'is_filter', - 'display', - 'is_menu', - ], - 'boolean', - ], - [ - [ - 'level', - 'sort', - ], - 'integer', - ], - [ - [ 'categories' ], - 'safe', - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => 'Tax Group ID', - 'is_filter' => 'Use in filter', - 'sort' => 'Sort', - 'display' => 'Display', - 'is_menu' => 'Отображать в меню', - ]; - } - - /** - * @return ActiveQuery - */ - public function getCategories() - { - return $this->hasMany(Category::className(), [ 'id' => 'category_id' ]) - ->viaTable('tax_group_to_category', [ 'tax_group_id' => 'id' ]); - } - - /** - * Set categories to override tax_group_to_category table data - * - * @param int[] $values - */ - public function setCategories(array $values) - { - $this->categories = $values; - } - - /** - * @inheritdoc - */ - public function afterSave($insert, $changedAttributes) - { - parent::afterSave($insert, $changedAttributes); - $this->unlinkAll('categories', true); - $categories = []; - if (!empty( $this->categories )) { - $categories = Category::findAll($this->categories); - } - foreach ($categories as $category) { - $this->link('categories', $category); - } - } - - /** - * @return ActiveQuery - */ - public function getTaxOptions() - { - return $this->hasMany(TaxOption::className(), [ 'tax_group_id' => 'id' ]) - ->inverseOf('taxGroup'); - } - - /** - * Synonim for getTaxOptions() - * - * @see TaxGroup::getTaxOptions() - * @return \yii\db\ActiveQuery - */ - public function getOptions() - { - return $this->getTaxOptions(); - } - - /** - * Get customOptins that were filled dynamically. - * If $fillDefault is true then fill $customOptions with TaxOptions for current TaxGroup - * - * @param bool $fillDefault - * - * @return TaxOption[] - */ - public function getCustomOptions(bool $fillDefault = false): array - { - if ($fillDefault && empty( $this->custom_options )) { - $this->customOptions = $this->getTaxOptions() - ->with('lang') - ->all(); - } - return $this->customOptions; - } - - /** - * Set customOptions - * - * @param TaxOption[] $value - */ - public function setCustomOptions(array $value) - { - foreach ($value as $item) { - if (!( $item instanceof TaxOption )) { - throw new InvalidValueException('All elements must be instances of ' . TaxOption::className()); - } - } - $this->customOptions = $value; - } - - /** - * Get default lang alias - * - * @return string - */ - public function getAlias() - { - $default_lang = Language::getDefaultLanguage(); - /** - * @var TaxGroupLang $lang - */ - $lang = $this->getLang($default_lang->id) - ->one(); - return $lang->alias; - } - } diff --git a/artbox-ecommerce/models/TaxGroupLang.php b/artbox-ecommerce/models/TaxGroupLang.php deleted file mode 100755 index 3058107..0000000 --- a/artbox-ecommerce/models/TaxGroupLang.php +++ /dev/null @@ -1,128 +0,0 @@ - [ - 'class' => 'common\behaviors\Slug', - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'title' ], - 'required', - ], - [ - [ 'description' ], - 'string', - ], - [ - [ - 'title', - 'alias', - ], - 'string', - 'max' => 255, - ], - [ - [ - 'tax_group_id', - 'language_id', - ], - 'unique', - 'targetAttribute' => [ - 'tax_group_id', - 'language_id', - ], - 'message' => 'The combination of Tax Group ID and Language ID has already been taken.', - ], - [ - [ 'language_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Language::className(), - 'targetAttribute' => [ 'language_id' => 'id' ], - ], - [ - [ 'tax_group_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => TaxGroup::className(), - 'targetAttribute' => [ 'tax_group_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'tax_group_id' => Yii::t('app', 'Tax Group ID'), - 'language_id' => Yii::t('app', 'Language ID'), - 'title' => Yii::t('app', 'Name'), - 'description' => Yii::t('app', 'Description'), - '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 getTaxGroup() - { - return $this->hasOne(TaxGroup::className(), [ 'id' => 'tax_group_id' ]); - } - } diff --git a/artbox-ecommerce/models/TaxGroupSearch.php b/artbox-ecommerce/models/TaxGroupSearch.php deleted file mode 100755 index aa1739d..0000000 --- a/artbox-ecommerce/models/TaxGroupSearch.php +++ /dev/null @@ -1,120 +0,0 @@ -joinWith('lang'); - - $dataProvider = new ActiveDataProvider( - [ - 'query' => $query, - 'sort' => [ - 'attributes' => [ - 'id', - 'is_filter', - 'groupName' => [ - 'asc' => [ 'tax_group_lang.title' => SORT_ASC ], - 'desc' => [ 'tax_group_lang.title' => SORT_DESC ], - ], - ], - ], - ] - ); - - $this->load($params); - - if (!is_null($level)) { - $this->level = $level; - } - - 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; - } - - $query->andFilterWhere( - [ - 'id' => $this->id, - 'is_filter' => $this->is_filter, - 'level' => $this->level, - ] - ) - ->andFilterWhere( - [ - 'ilike', - 'tax_group_lang.title', - $this->groupName, - ] - ); - - return $dataProvider; - } - } diff --git a/artbox-ecommerce/models/TaxGroupToCategory.php b/artbox-ecommerce/models/TaxGroupToCategory.php deleted file mode 100755 index 97e29de..0000000 --- a/artbox-ecommerce/models/TaxGroupToCategory.php +++ /dev/null @@ -1,92 +0,0 @@ - true, - 'targetClass' => Category::className(), - 'targetAttribute' => [ 'category_id' => 'id' ], - ], - [ - [ 'tax_group_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => TaxGroup::className(), - 'targetAttribute' => [ 'tax_group_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'tax_group_to_category_id' => 'Tax Group To Category ID', - 'tax_group_id' => 'Tax Group ID', - 'category_id' => 'Category ID', - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getCategory() - { - return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]); - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getTaxGroup() - { - return $this->hasOne(TaxGroup::className(), [ 'id' => 'tax_group_id' ]); - } - } diff --git a/artbox-ecommerce/models/TaxOption.php b/artbox-ecommerce/models/TaxOption.php deleted file mode 100755 index dbaa865..0000000 --- a/artbox-ecommerce/models/TaxOption.php +++ /dev/null @@ -1,158 +0,0 @@ - SaveImgBehavior::className(), - 'fields' => [ - [ - 'name' => 'image', - 'directory' => 'tax_option', - ], - ], - ], - 'language' => [ - 'class' => LanguageBehavior::className(), - ], - ]; - } - - /** - * @inheritdoc - */ - public static function tableName() - { - return '{{%tax_option}}'; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ - 'tax_group_id', - 'sort', - ], - 'integer', - ], - [ - [ 'tax_group_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => TaxGroup::className(), - 'targetAttribute' => [ 'tax_group_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('app', 'Tax Option ID'), - 'tax_group_id' => Yii::t('app', 'Tax Group ID'), - 'sort' => Yii::t('app', 'Sort'), - 'image' => Yii::t('product', 'Image'), - ]; - } - - /** - * @return \yii\db\ActiveQuery - */ - public function getTaxGroup() - { - return $this->hasOne(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) - ->inverseOf('taxOptions'); - } - - /** - * Synonim for TaxOption::getTaxGroup() - * - * @see TaxOption::getTaxGroup() - * @return \yii\db\ActiveQuery - */ - public function getGroup() - { - return $this->getTaxGroup(); - } - - /** - * @return ActiveQuery - */ - public function getProducts() - { - return $this->hasMany(Product::className(), [ 'id' => 'product_id' ]) - ->viaTable('product_option', [ 'option_id' => 'id' ]); - } - - /** - * @return ActiveQuery - */ - public function getProductVariants() - { - return $this->hasMany(ProductVariant::className(), [ 'id' => 'product_variant_id' ]) - ->viaTable('product_variant_option', [ 'option_id' => 'id' ]); - } - } diff --git a/artbox-ecommerce/models/TaxOptionLang.php b/artbox-ecommerce/models/TaxOptionLang.php deleted file mode 100755 index dd09e88..0000000 --- a/artbox-ecommerce/models/TaxOptionLang.php +++ /dev/null @@ -1,123 +0,0 @@ - [ - 'class' => 'common\behaviors\Slug', - 'inAttribute' => 'value', - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ 'value' ], - 'required', - ], - [ - [ - 'value', - 'alias', - ], - 'string', - 'max' => 255, - ], - [ - [ - 'tax_option_id', - 'language_id', - ], - 'unique', - 'targetAttribute' => [ - 'tax_option_id', - 'language_id', - ], - 'message' => 'The combination of Tax Option ID and Language ID has already been taken.', - ], - [ - [ 'language_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => Language::className(), - 'targetAttribute' => [ 'language_id' => 'id' ], - ], - [ - [ 'tax_option_id' ], - 'exist', - 'skipOnError' => true, - 'targetClass' => TaxOption::className(), - 'targetAttribute' => [ 'tax_option_id' => 'id' ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'tax_option_id' => Yii::t('app', 'Tax Option ID'), - 'language_id' => Yii::t('app', 'Language ID'), - 'value' => Yii::t('app', 'Value'), - '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 getTaxOption() - { - return $this->hasOne(TaxOption::className(), [ 'id' => 'tax_option_id' ]); - } - } diff --git a/artbox-ecommerce/models/TaxOptionSearch.php b/artbox-ecommerce/models/TaxOptionSearch.php deleted file mode 100755 index 8304ddd..0000000 --- a/artbox-ecommerce/models/TaxOptionSearch.php +++ /dev/null @@ -1,101 +0,0 @@ -joinWith('lang'); - - $dataProvider = new ActiveDataProvider( - [ - 'query' => $query, - 'sort' => [ - 'attributes' => [ - 'id', - 'value' => [ - 'asc' => [ 'tax_option_lang.value' => SORT_ASC ], - 'desc' => [ 'tax_option_lang.value' => SORT_DESC ], - ], - ], - ], - ] - ); - - $this->load($params); - - // if (!$this->validate()) { - // return $dataProvider; - // } - - // grid filtering conditions - $query->andFilterWhere( - [ - 'id' => $this->id, - ] - ) - ->andFilterWhere( - [ - 'like', - 'tax_option_lang.value', - $this->value, - ] - ); - - return $dataProvider; - } - } diff --git a/artbox-ecommerce/views/manage/_form.php b/artbox-ecommerce/views/manage/_form.php deleted file mode 100755 index 765e924..0000000 --- a/artbox-ecommerce/views/manage/_form.php +++ /dev/null @@ -1,142 +0,0 @@ - - -
- - [ 'enctype' => 'multipart/form-data' ], - ] - ); ?> - - field($model, 'is_top') - ->checkbox([ 'label' => 'ТОП' ]) ?> - field($model, 'is_new') - ->checkbox([ 'label' => 'Новинка' ]) ?> - field($model, 'is_discount') - ->checkbox([ 'label' => 'Акционный' ]) ?> - - field($model, 'video') - ->textarea(); ?> - - field($model, 'brand_id') - ->dropDownList( - ArrayHelper::map( - Brand::find() - ->with('lang') - ->all(), - 'id', - 'lang.title' - ), - [ - 'prompt' => Yii::t('product', 'Select brand'), - ] - ) ?> - - field($model, 'categories') - ->widget( - Select2::className(), - [ - 'data' => ArtboxTreeHelper::treeMap(ProductHelper::getCategories(), 'id', 'lang.title'), - 'language' => 'ru', - 'options' => [ - 'placeholder' => Yii::t('product', 'Select categories'), - 'multiple' => true, - ], - 'pluginOptions' => [ - 'allowClear' => true, - ], - ] - ) ?> - - field($model, 'imagesUpload[]') - ->widget( - \kartik\file\FileInput::className(), - [ - 'language' => 'ru', - 'options' => [ - 'accept' => 'image/*', - 'multiple' => true, - ], - 'pluginOptions' => [ - 'allowedFileExtensions' => [ - 'jpg', - 'gif', - 'png', - ], - 'initialPreview' => !empty( $model->imagesHTML ) ? $model->imagesHTML : [], - 'initialPreviewConfig' => $model->imagesConfig, - 'overwriteInitial' => false, - 'showRemove' => false, - 'showUpload' => false, - 'uploadAsync' => !empty( $model->id ), - 'previewFileType' => 'image', - ], - ] - ); ?> - - with('lang') - ->all() as $group) { - /** - * @var TaxGroup $group - */ - echo $form->field($model, 'options') - ->checkboxList( - ArrayHelper::map( - $group->getOptions() - ->with('lang') - ->all(), - 'id', - 'lang.value' - ), - [ - 'multiple' => true, - 'unselect' => NULL, - ] - ) - ->label($group->lang->title); - } - } - ?> - -
- - $modelLangs, - 'formView' => '@common/modules/product/views/manage/_form_language', - 'form' => $form, - ] - ) ?> - -
- isNewRecord ? Yii::t('product', 'Create') : Yii::t('product', 'Update'), - [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ] - ) ?> -
- - - -
diff --git a/artbox-ecommerce/views/manage/_form_language.php b/artbox-ecommerce/views/manage/_form_language.php deleted file mode 100755 index 601f2ac..0000000 --- a/artbox-ecommerce/views/manage/_form_language.php +++ /dev/null @@ -1,28 +0,0 @@ - -field($model_lang, '[' . $language->id . ']title') - ->textInput([ 'maxlength' => true ]); ?> -field($model_lang, '[' . $language->id . ']alias') - ->textInput([ 'maxlength' => true ]); ?> -field($model_lang, '[' . $language->id . ']description') - ->widget(CKEditor::className(), [ - 'editorOptions' => ElFinder::ckeditorOptions('elfinder', [ - 'preset' => 'full', - 'inline' => false, - 'filebrowserUploadUrl' => Yii::$app->getUrlManager() - ->createUrl('file/uploader/images-upload'), - ]), - ]) ?> \ No newline at end of file diff --git a/artbox-ecommerce/views/manage/create.php b/artbox-ecommerce/views/manage/create.php deleted file mode 100755 index adb632d..0000000 --- a/artbox-ecommerce/views/manage/create.php +++ /dev/null @@ -1,30 +0,0 @@ -title = Yii::t('product', 'Create Product'); - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Products'), - 'url' => [ 'index' ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
- -

title) ?>

- - render('_form', [ - 'model' => $model, - 'modelLangs' => $modelLangs, - ]) ?> - -
diff --git a/artbox-ecommerce/views/manage/export-process.php b/artbox-ecommerce/views/manage/export-process.php deleted file mode 100755 index 9278200..0000000 --- a/artbox-ecommerce/views/manage/export-process.php +++ /dev/null @@ -1,104 +0,0 @@ - -registerJs("var in_process=true; - var count=1; - var filename = null; - - doExport(0,filename); - - function doExport(from,filename) { - from = typeof(from) != 'undefined' ? from : 0; - - $.ajax({ - method: 'get', - url: '" . Yii::$app->request->baseUrl . '/product/manage/export-process' . "', - data: { - from:from, - filename: filename - }, - dataType: 'json', - success: function(data){ - - var per = Math.round(100*data.from/data.totalsize)+'%'; - $('#progressbar div').css({width: per}); - - if(data != false && !data.end) - { - doExport(data.from,data.filename); - } - else - { - console.log(data.link); - $(progressbar).hide('fast'); - $('#result_link').attr('href', data.link).removeClass('hidden'); - in_process = false; - } - }, - error: function(xhr, status, errorThrown) { - } - }); - }"); -?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Экспорт данных товаров

- - [ - 'value' => 100, - 'label' => '', - ], - 'options' => [ - 'id' => 'progressbar', - ], - ]); ?> -
    - -
    diff --git a/artbox-ecommerce/views/manage/export.php b/artbox-ecommerce/views/manage/export.php deleted file mode 100755 index 432d031..0000000 --- a/artbox-ecommerce/views/manage/export.php +++ /dev/null @@ -1,51 +0,0 @@ - - -
    - false, - 'options' => [ 'enctype' => 'multipart/form-data' ], - ]); ?> - - errors) : ?> -
    - \n", $model->errors); ?> -
    - - - output) : ?> -

    Лог операции

    -
    - \n", $model->output); ?> -
    - - - field($model, 'lang') - ->dropDownList(Language::find() - ->select([ - 'name', - 'id', - ]) - ->where([ 'status' => 1 ]) - ->orderBy([ 'default' => SORT_DESC ]) - ->asArray() - ->indexBy('id') - ->column()) ?> - -
    - 'btn btn-success' ]) ?> -
    - - -
    \ No newline at end of file diff --git a/artbox-ecommerce/views/manage/import-process.php b/artbox-ecommerce/views/manage/import-process.php deleted file mode 100755 index 91199cf..0000000 --- a/artbox-ecommerce/views/manage/import-process.php +++ /dev/null @@ -1,64 +0,0 @@ -registerJs(" -var in_process=false; - var count=1; - - in_process=true; - - doImport(); - - function doImport(from) { - from = typeof(from) != 'undefined' ? from : 0; - console.log('go', from); - $.ajax({ - url: '" . \Yii::$app->request->baseUrl . '/product/manage/' . $method . "', - data: {from:from}, - dataType: 'json', - success: function(data){ - for(var key in data.items) - { - $('ul#process-result').prepend('
  • '+ data.items[key] +'
  • '); - count++; - } - - var per = Math.round(100*data.from/data.totalsize)+'%'; - $('#progressbar div').css({width: per}); - - if(data != false && !data.end) - { - doImport(data.from); - } - else - { - $('ul#process-result').prepend('
  • Импорт цен успешно завершен!
  • '); - progressbar.hide('fast'); - in_process = false; - } - }, - error: function(xhr, status, errorThrown) { - } - }); - } -"); - //?> - -
    -

    Импорт товаров

    - - [ - 'value' => 100, - 'label' => '', - ], - 'options' => [ - 'id' => 'progressbar', - ], - ]); ?> -
      -
      diff --git a/artbox-ecommerce/views/manage/import.php b/artbox-ecommerce/views/manage/import.php deleted file mode 100755 index 4e67ef4..0000000 --- a/artbox-ecommerce/views/manage/import.php +++ /dev/null @@ -1,63 +0,0 @@ - - -
      - false, - 'options' => [ 'enctype' => 'multipart/form-data' ], - ]); ?> - - errors) : ?> -
      - \n", $model->errors); ?> -
      - - - output) : ?> -

      Лог операции

      -
      - \n", $model->output); ?> -
      - - - field($model, 'type') - ->radioList([ - 'products' => Yii::t('product', 'Load products'), - 'prices' => Yii::t('product', 'Load prices'), - ]); ?> - - field($model, 'lang') - ->dropDownList($languages); ?> - - field($model, 'file') - ->fileInput([ 'multiple' => false, ]) ?> - - field($model, 'file')->widget(\kartik\file\FileInput::classname(), [ - 'language' => 'ru', - 'options' => [ - 'multiple' => false, - ], - 'pluginOptions' => [ - 'allowedFileExtensions' => ['csv'], - 'overwriteInitial' => true, - 'showRemove' => false, - 'showUpload' => false, - ], - ])*/ ?> - -
      - 'btn btn-primary' ]) ?> -
      - - -
      diff --git a/artbox-ecommerce/views/manage/index.php b/artbox-ecommerce/views/manage/index.php deleted file mode 100755 index 5d4da9b..0000000 --- a/artbox-ecommerce/views/manage/index.php +++ /dev/null @@ -1,232 +0,0 @@ -title = Yii::t('product', 'Products'); - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - -

      - 'btn btn-success' ]) ?> -

      - $dataProvider, - 'filterModel' => $searchModel, - 'columns' => [ - 'id', - [ - 'attribute' => 'productName', - 'value' => 'lang.title', - ], - [ - 'label' => Yii::t('product', 'Brand'), - 'attribute' => 'brand_id', - 'value' => 'brand.lang.title', - 'filter' => Select2::widget( - [ - 'model' => $searchModel, - 'attribute' => 'brand_id', - 'data' => Brand::find() - ->joinWith('lang') - ->select( - [ - 'brand_lang.title', - 'brand.id', - ] - ) - ->asArray() - ->indexBy('id') - ->column(), - 'language' => 'ru', - 'options' => [ - 'placeholder' => Yii::t('product', 'Select brand'), - 'multiple' => false, - ], - 'pluginOptions' => [ - 'allowClear' => true, - ], - ] - ), - ], - [ - 'label' => Yii::t('product', 'Category'), - 'attribute' => 'categoryId', - 'value' => function ($model) { - /** - * @var Product $model - */ - $categories = []; - foreach ($model->getCategories() - ->with('lang') - ->all() as $category) { - /** - * @var Category $category - */ - $categories[] = $category->lang->title; - } - return implode(", ", $categories); - }, - 'filter' => Select2::widget( - [ - 'model' => $searchModel, - 'attribute' => 'categoryId', - 'data' => ArtboxTreeHelper::treeMap( - ProductHelper::getCategories(), - 'id', - 'lang.title' - ), - 'language' => 'ru', - 'options' => [ - 'placeholder' => Yii::t('product', 'Select category'), - 'multiple' => false, - ], - 'pluginOptions' => [ - 'allowClear' => true, - ], - ] - ), - ], - [ - 'attribute' => 'variantCount', - 'value' => function ($model) { - /** - * @var Product $model - */ - return count($model->variants); - }, - ], - [ - 'class' => 'yii\grid\ActionColumn', - 'template' => '{items} {view} |{is_top} {is_new} {is_discount} | {update} {delete}', - 'buttons' => [ - 'is_top' => function ($url, $model) { - return Html::a( - '', - $url, - [ - 'title' => Yii::t('product', ( $model->is_top ? 'Set not is top' : 'Set is top' )), - ] - ); - }, - 'is_new' => function ($url, $model) { - return Html::a( - '', - $url, - [ - 'title' => Yii::t('product', ( $model->is_new ? 'Set not is new' : 'Set is new' )), - ] - ); - }, - 'is_discount' => function ($url, $model) { - return Html::a( - '', - $url, - [ - 'title' => Yii::t( - 'product', - ( $model->is_discount ? 'Set not is promotion' : 'Set is promotion' ) - ), - ] - ); - }, - 'items' => function ($url, $model) { - return Html::a( - '', - $url, - [ - 'title' => Yii::t('product', 'Variants'), - ] - ); - }, - - ], - 'urlCreator' => function ($action, $model, $key, $index) { - /** - * @var Product $model - */ - switch ($action) { - case 'items': - return \yii\helpers\Url::to( - [ - '/product/variant', - 'product_id' => $model->id, - ] - ); - break; - case 'is_top': - return \yii\helpers\Url::to( - [ - 'manage/is_top', - 'id' => $model->id, - ] - ); - break; - case 'is_new': - return \yii\helpers\Url::to( - [ - 'manage/is_new', - 'id' => $model->id, - ] - ); - break; - case 'is_discount': - return \yii\helpers\Url::to( - [ - 'manage/is-discount', - 'id' => $model->id, - ] - ); - break; - case 'view': - return \yii\helpers\Url::to( - [ - 'manage/view', - 'id' => $model->id, - ] - ); - break; - case 'update': - return \yii\helpers\Url::to( - [ - 'manage/update', - 'id' => $model->id, - ] - ); - break; - case 'delete': - return \yii\helpers\Url::to( - [ - 'manage/delete', - 'id' => $model->id, - ] - ); - break; - default: - return ''; - break; - } - }, - ], - ], - ] - ); ?> -
      diff --git a/artbox-ecommerce/views/manage/update.php b/artbox-ecommerce/views/manage/update.php deleted file mode 100755 index b4c5f9d..0000000 --- a/artbox-ecommerce/views/manage/update.php +++ /dev/null @@ -1,42 +0,0 @@ -title = Yii::t('product', 'Update {modelClass}: ', [ - 'modelClass' => 'Product', - ]) . ' ' . $model->lang->title; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Products'), - 'url' => [ 'index' ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $model->lang->title, - 'url' => [ - 'view', - 'id' => $model->id, - ], - ]; - $this->params[ 'breadcrumbs' ][] = Yii::t('product', 'Update'); -?> -
      - -

      title) ?>

      - - render('_form', [ - 'model' => $model, - 'modelLangs' => $modelLangs, - 'groups' => $groups, - ]) ?> - -
      diff --git a/artbox-ecommerce/views/manage/view.php b/artbox-ecommerce/views/manage/view.php deleted file mode 100755 index 85e8ee7..0000000 --- a/artbox-ecommerce/views/manage/view.php +++ /dev/null @@ -1,140 +0,0 @@ -title = $model->lang->title; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Products'), - 'url' => [ 'index' ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; - $properties_string = ''; - foreach ($properties as $property) { - $property_list = ''; - foreach ($property->options as $option) { - $property_list .= Html::tag('li', $option->lang->value); - } - $properties_string .= Html::tag('p', $property->lang->title) . Html::tag('ul', $property_list); - } - $variants_string = ''; - foreach ($variants as $variant) { - $variants_string .= Html::a( - $variant->lang->title, - [ - '/product/variant/view', - 'id' => $variant->id, - ] - ) . '
      '; - } -?> -
      - -

      title) ?>

      - -

      - $model->id, - ], - [ 'class' => 'btn btn-primary' ] - ) ?> - $model->id, - ], - [ - 'class' => 'btn btn-danger', - 'data' => [ - 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'), - 'method' => 'post', - ], - ] - ) ?> - $model->id, - ], - [ 'class' => 'btn btn-info' ] - ) ?> -

      - - $model, - 'attributes' => [ - 'id', - 'brand.lang.title', - [ - 'label' => \Yii::t('app', 'Categories'), - 'value' => implode('
      ', ArrayHelper::getColumn($categories, 'lang.title')), - 'format' => 'html', - ], - [ - 'attribute' => 'is_top', - 'value' => $model->is_top ? Html::tag( - 'span', - '', - [ 'class' => 'glyphicon glyphicon-ok' ] - ) : Html::tag('span', '', [ 'class' => 'glyphicon glyphicon-remove' ]), - 'format' => 'html', - ], - [ - 'attribute' => 'is_new', - 'value' => $model->is_new ? Html::tag( - 'span', - '', - [ 'class' => 'glyphicon glyphicon-ok' ] - ) : Html::tag('span', '', [ 'class' => 'glyphicon glyphicon-remove' ]), - 'format' => 'html', - ], - [ - 'attribute' => 'is_discount', - 'value' => $model->is_discount ? Html::tag( - 'span', - '', - [ 'class' => 'glyphicon glyphicon-ok' ] - ) : Html::tag('span', '', [ 'class' => 'glyphicon glyphicon-remove' ]), - 'format' => 'html', - ], - [ - 'attribute' => 'video', - 'format' => 'html', - ], - [ - 'label' => \Yii::t('app', 'Properties'), - 'value' => $properties_string, - 'format' => 'html', - ], - [ - 'label' => \Yii::t('app', 'Variants'), - 'value' => $variants_string, - 'format' => 'html', - ], - 'lang.description:html', - 'image.imageUrl:image', - ], - ] - ) ?> - -
      diff --git a/artbox-ecommerce/views/product-unit/_form.php b/artbox-ecommerce/views/product-unit/_form.php deleted file mode 100755 index 98f221d..0000000 --- a/artbox-ecommerce/views/product-unit/_form.php +++ /dev/null @@ -1,37 +0,0 @@ - - -
      - - - - field($model, 'is_default') - ->checkbox() ?> - - $modelLangs, - 'form' => $form, - 'formView' => '@common/modules/product/views/product-unit/_form_language', - ]) ?> - -
      - isNewRecord ? Yii::t('product', 'Create') : Yii::t('product', 'Update'), [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ]) ?> -
      - - - -
      diff --git a/artbox-ecommerce/views/product-unit/_form_language.php b/artbox-ecommerce/views/product-unit/_form_language.php deleted file mode 100755 index bf8d725..0000000 --- a/artbox-ecommerce/views/product-unit/_form_language.php +++ /dev/null @@ -1,17 +0,0 @@ - -field($model_lang, '[' . $language->id . ']title') - ->textInput([ 'maxlength' => true ]); ?> -field($model_lang, '[' . $language->id . ']short') - ->textInput([ 'maxlength' => true ]); ?> \ No newline at end of file diff --git a/artbox-ecommerce/views/product-unit/create.php b/artbox-ecommerce/views/product-unit/create.php deleted file mode 100755 index 20611aa..0000000 --- a/artbox-ecommerce/views/product-unit/create.php +++ /dev/null @@ -1,30 +0,0 @@ -title = Yii::t('product', 'Create Product Unit'); - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Product Units'), - 'url' => [ 'index' ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - - render('_form', [ - 'model' => $model, - 'modelLangs' => $modelLangs, - ]) ?> - -
      diff --git a/artbox-ecommerce/views/product-unit/index.php b/artbox-ecommerce/views/product-unit/index.php deleted file mode 100755 index ac912eb..0000000 --- a/artbox-ecommerce/views/product-unit/index.php +++ /dev/null @@ -1,48 +0,0 @@ -title = Yii::t('product', 'Product Units'); - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - -

      - 'btn btn-success' ]) ?> -

      - $dataProvider, - 'filterModel' => $searchModel, - 'columns' => [ - 'id', - [ - 'attribute' => 'is_default', - 'format' => 'boolean', - 'filter' => [ - \Yii::$app->formatter->asBoolean(false), - \Yii::$app->formatter->asBoolean(true), - ], - ], - [ - 'attribute' => 'title', - 'value' => 'lang.title', - ], - 'lang.short', - [ 'class' => 'yii\grid\ActionColumn' ], - ], - ] - ); ?> -
      diff --git a/artbox-ecommerce/views/product-unit/update.php b/artbox-ecommerce/views/product-unit/update.php deleted file mode 100755 index 8d3692f..0000000 --- a/artbox-ecommerce/views/product-unit/update.php +++ /dev/null @@ -1,39 +0,0 @@ -title = Yii::t('product', 'Update {modelClass}: ', [ - 'modelClass' => 'Product Unit', - ]) . $model->lang->title; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Product Units'), - 'url' => [ 'index' ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $model->lang->title, - 'url' => [ - 'view', - 'id' => $model->id, - ], - ]; - $this->params[ 'breadcrumbs' ][] = Yii::t('product', 'Update'); -?> -
      - -

      title) ?>

      - - render('_form', [ - 'model' => $model, - 'modelLangs' => $modelLangs, - ]) ?> - -
      diff --git a/artbox-ecommerce/views/product-unit/view.php b/artbox-ecommerce/views/product-unit/view.php deleted file mode 100755 index 36894cd..0000000 --- a/artbox-ecommerce/views/product-unit/view.php +++ /dev/null @@ -1,61 +0,0 @@ -title = $model->lang->title; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Product Units'), - 'url' => [ 'index' ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - -

      - $model->id, - ], - [ 'class' => 'btn btn-primary' ] - ) ?> - $model->id, - ], - [ - 'class' => 'btn btn-danger', - 'data' => [ - 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'), - 'method' => 'post', - ], - ] - ) ?> -

      - - $model, - 'attributes' => [ - 'id', - 'is_default:boolean', - 'lang.title', - 'lang.short', - ], - ] - ) ?> - -
      diff --git a/artbox-ecommerce/views/tax-group/_form.php b/artbox-ecommerce/views/tax-group/_form.php deleted file mode 100755 index b1b4f2e..0000000 --- a/artbox-ecommerce/views/tax-group/_form.php +++ /dev/null @@ -1,56 +0,0 @@ - - -
      - - [ 'enctype' => 'multipart/form-data' ] ]); ?> - - field($model, 'categories') - ->dropDownList(ArtboxTreeHelper::treeMap(ProductHelper::getCategories(), 'id', 'lang.title'), [ - 'multiple' => true, - ]) - ->label('Use in the following categories') ?> - - field($model, 'is_filter') - ->checkbox() ?> - - field($model, 'display') - ->checkbox() ?> - - field($model, 'is_menu') - ->checkbox() ?> - - field($model, 'sort') - ->textInput() ?> - - $modelLangs, - 'formView' => '@common/modules/rubrication/views/tax-group/_form_language', - 'form' => $form, - ]); - ?> - -
      - isNewRecord ? Yii::t('rubrication', 'Create') : Yii::t('rubrication', 'Update'), [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ]) ?> -
      - - - -
      diff --git a/artbox-ecommerce/views/tax-group/_form_language.php b/artbox-ecommerce/views/tax-group/_form_language.php deleted file mode 100755 index 5a78eea..0000000 --- a/artbox-ecommerce/views/tax-group/_form_language.php +++ /dev/null @@ -1,19 +0,0 @@ - -field($model_lang, '[' . $language->id . ']title') - ->textInput([ 'maxlength' => true ]); ?> -field($model_lang, '[' . $language->id . ']alias') - ->textInput([ 'maxlength' => true ]); ?> -field($model_lang, '[' . $language->id . ']description') - ->textarea([ 'rows' => 6 ]) ?> \ No newline at end of file diff --git a/artbox-ecommerce/views/tax-group/create.php b/artbox-ecommerce/views/tax-group/create.php deleted file mode 100755 index 3bf2f17..0000000 --- a/artbox-ecommerce/views/tax-group/create.php +++ /dev/null @@ -1,29 +0,0 @@ -title = Yii::t('rubrication', 'Create Tax Group'); - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('rubrication', 'Tax Groups'), - 'url' => [ 'index' ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - - render('_form', [ - 'model' => $model, - 'modelLangs' => $modelLangs, - ]) ?> - -
      diff --git a/artbox-ecommerce/views/tax-group/index.php b/artbox-ecommerce/views/tax-group/index.php deleted file mode 100755 index 4d1af2d..0000000 --- a/artbox-ecommerce/views/tax-group/index.php +++ /dev/null @@ -1,116 +0,0 @@ -title = $level ? Yii::t('rubrication', 'Modification Groups') : Yii::t('rubrication', 'Product Groups'); - $this->params[ 'breadcrumbs' ][] = $this->title; -?> - -
      - -

      title) ?>

      -

      - $level, - ] - ), - [ 'class' => 'btn btn-success' ] - ) ?> -

      - - $dataProvider, - 'filterModel' => $searchModel, - 'columns' => [ - [ 'class' => 'yii\grid\SerialColumn' ], - 'id', - [ - 'attribute' => 'is_filter', - 'format' => 'boolean', - 'filter' => \Yii::$app->formatter->booleanFormat, - ], - [ - 'attribute' => 'groupName', - 'value' => 'lang.title', - ], - [ - 'label' => \Yii::t('rubrication', 'Options count'), - 'value' => function ($model) { - /** - * @var TaxGroup $model - */ - return count($model->options); - }, - ], - [ - 'label' => \Yii::t('rubrication', 'Categories count'), - 'value' => function ($model) { - /** - * @var TaxGroup $model - */ - return count($model->categories); - }, - ], - [ - 'class' => 'yii\grid\ActionColumn', - 'template' => '{update} {options} {delete}', - 'buttons' => [ - 'options' => function ($url, $model) { - return Html::a( - '', - $url, - [ - 'title' => Yii::t('rubrication', 'Options'), - ] - ); - }, - ], - 'urlCreator' => function ($action, $model, $key, $index) use ($level) { - if ($action === 'options') { - $url = '/admin/rubrication/tax-option?group=' . $model->id; - return $url; - } elseif ($action === 'update') { - $url = Url::to( - [ - '/rubrication/tax-group/update', - 'level' => $level, - 'id' => $model->id, - ] - ); - return $url; - } elseif ($action === 'delete') { - $url = Url::to( - [ - '/rubrication/tax-group/delete', - 'level' => $level, - 'id' => $model->id, - ] - ); - return $url; - } - return ''; - }, - ], - ], - ] - ); ?> -
      - diff --git a/artbox-ecommerce/views/tax-group/update.php b/artbox-ecommerce/views/tax-group/update.php deleted file mode 100755 index 454b852..0000000 --- a/artbox-ecommerce/views/tax-group/update.php +++ /dev/null @@ -1,36 +0,0 @@ -title = Yii::t('rubrication', 'Update {modelClass}: ', [ - 'modelClass' => 'Tax Group', - ]) . ' ' . $model->lang->title; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $level ? Yii::t('rubrication', 'Modification Groups') : Yii::t('rubrication', 'Product Groups'), - 'url' => [ - 'index', - 'level' => $level, - ], - ]; - $this->params[ 'breadcrumbs' ][] = Yii::t('rubrication', 'Update'); -?> -
      - -

      title) ?>

      - - render('_form', [ - 'model' => $model, - 'modelLangs' => $modelLangs, - ]) ?> - -
      diff --git a/artbox-ecommerce/views/tax-group/view.php b/artbox-ecommerce/views/tax-group/view.php deleted file mode 100755 index 4594b87..0000000 --- a/artbox-ecommerce/views/tax-group/view.php +++ /dev/null @@ -1,60 +0,0 @@ -title = $model->id; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('rubrication', 'Tax Groups'), - 'url' => [ 'index' ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - -

      - $model->id, - ], - [ 'class' => 'btn btn-primary' ] - ) ?> - $model->id, - ], - [ - 'class' => 'btn btn-danger', - 'data' => [ - 'confirm' => Yii::t('rubrication', 'Are you sure you want to delete this item?'), - 'method' => 'post', - ], - ] - ) ?> - id ], - [ 'class' => 'btn btn-success' ] - ) ?> -

      - - $model, - 'attributes' => [ - 'id', - 'is_filter:boolean', - ], - ] - ) ?> - -
      diff --git a/artbox-ecommerce/views/tax-option/_form.php b/artbox-ecommerce/views/tax-option/_form.php deleted file mode 100755 index b533917..0000000 --- a/artbox-ecommerce/views/tax-option/_form.php +++ /dev/null @@ -1,102 +0,0 @@ - - -
      - - [ 'enctype' => 'multipart/form-data' ] ]); ?> - id )) : ?> - field($model, 'tax_group_id') - ->dropDownList( - ArrayHelper::map( - TaxOption::find() - ->all(), - 'tax_group_id', - 'tax_group_id' - ), - [ - 'prompt' => Yii::t('rubrication', 'Select group'), - ] - ) ?> - - field($model, 'tax_group_id') - ->hiddenInput() - ->label('') ?> - - - field($model, 'image') - ->widget( - \kartik\file\FileInput::className(), - [ - 'language' => 'ru', - 'options' => [ - 'accept' => 'image/*', - 'multiple' => false, - ], - 'pluginOptions' => [ - 'allowedFileExtensions' => [ - 'jpg', - 'gif', - 'png', - ], - 'initialPreview' => !empty( $model->imageUrl ) ? \common\components\artboximage\ArtboxImageHelper::getImage( - $model->imageUrl, - 'list' - ) : '', - 'overwriteInitial' => true, - 'showRemove' => false, - 'showUpload' => false, - 'previewFileType' => 'image', - ], - ] - ) - ->hint( - ( ( $model->tax_group_id == 5 ) ? 'Для корректного отображения на сайте, размер изображения должен быть 262x144 либо соблюдать соотношение сторон примерно 2:1' : '' ) - ); ?> - field($model, 'sort') - ->textInput() ?> - - $modelLangs, - 'formView' => '@common/modules/rubrication/views/tax-option/_form_language', - 'form' => $form, - ] - ); - ?> - -
      - isNewRecord ? Yii::t('rubrication', 'Create') : Yii::t('rubrication', 'Update'), - [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ] - ) ?> - isNewRecord) : ?> - 'create_and_new', - 'class' => 'btn btn-primary', - ] - ) ?> - -
      - - - -
      diff --git a/artbox-ecommerce/views/tax-option/_form_language.php b/artbox-ecommerce/views/tax-option/_form_language.php deleted file mode 100755 index 6f7d22a..0000000 --- a/artbox-ecommerce/views/tax-option/_form_language.php +++ /dev/null @@ -1,17 +0,0 @@ - -field($model_lang, '[' . $language->id . ']value') - ->textInput([ 'maxlength' => true ]); ?> -field($model_lang, '[' . $language->id . ']alias') - ->textInput([ 'maxlength' => true ]); ?> diff --git a/artbox-ecommerce/views/tax-option/create.php b/artbox-ecommerce/views/tax-option/create.php deleted file mode 100755 index f1ba296..0000000 --- a/artbox-ecommerce/views/tax-option/create.php +++ /dev/null @@ -1,49 +0,0 @@ -title = Yii::t('rubrication', 'Create Tax Option'); - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $group->level ? Yii::t('rubrication', 'Modification Groups') : Yii::t('rubrication', 'Product Groups'), - 'url' => [ - 'tax-group/index', - 'level' => $group->level, - ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $group->lang->title, - 'url' => [ - 'tax-group/update', - 'id' => $group->id, - 'level' => $group->level, - ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('rubrication', 'Options for group {group}', [ 'group' => $group->lang->title ]), - 'url' => [ - 'index', - 'group' => $group->id, - 'level' => $group->level, - ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - - render('_form', [ - 'model' => $model, - 'modelLangs' => $modelLangs, - 'group' => $group, - ]) ?> - -
      diff --git a/artbox-ecommerce/views/tax-option/index.php b/artbox-ecommerce/views/tax-option/index.php deleted file mode 100755 index 12de0fe..0000000 --- a/artbox-ecommerce/views/tax-option/index.php +++ /dev/null @@ -1,79 +0,0 @@ -title = Yii::t('rubrication', 'Options for group {group}', [ 'group' => $group->lang->title ]); - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $group->level ? Yii::t('rubrication', 'Modification Groups') : Yii::t( - 'rubrication', - 'Product Groups' - ), - 'url' => [ - 'tax-group/index', - 'level' => $group->level, - ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $group->lang->title, - 'url' => [ - 'tax-group/update', - 'id' => $group->id, - 'level' => $group->level, - ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - -

      - id ], - [ 'class' => 'btn btn-success' ] - ) ?> -

      - - $dataProvider, - 'filterModel' => $searchModel, - 'columns' => [ - [ 'class' => 'yii\grid\SerialColumn' ], - 'id', - [ - 'attribute' => 'value', - 'value' => 'lang.value', - ], - 'imageUrl:image', - [ - 'label' => $group->level ? \Yii::t('rubrication', 'Variants count') : \Yii::t( - 'rubrication', - 'Products count' - ), - 'value' => function ($model) use ($group) { - /** - * @var TaxOption $model - */ - return count($group->level ? $model->productVariants : $model->products); - }, - ], - [ - 'class' => 'yii\grid\ActionColumn', - 'template' => '{update} {delete}', - ], - ], - ] - ); ?> -
      diff --git a/artbox-ecommerce/views/tax-option/update.php b/artbox-ecommerce/views/tax-option/update.php deleted file mode 100755 index ffd5e15..0000000 --- a/artbox-ecommerce/views/tax-option/update.php +++ /dev/null @@ -1,51 +0,0 @@ -title = Yii::t('rubrication', 'Update {modelClass}: ', [ - 'modelClass' => 'Tax Option', - ]) . ' ' . $model->lang->value; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $group->level ? Yii::t('rubrication', 'Modification Groups') : Yii::t('rubrication', 'Product Groups'), - 'url' => [ - 'tax-group/index', - 'level' => $group->level, - ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $group->lang->title, - 'url' => [ - 'tax-group/update', - 'id' => $group->id, - 'level' => $group->level, - ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('rubrication', 'Options for group {group}', [ 'group' => $group->lang->title ]), - 'url' => [ - 'index', - 'group' => $group->id, - 'level' => $group->level, - ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - - render('_form', [ - 'model' => $model, - 'modelLangs' => $modelLangs, - 'group' => $group, - ]) ?> - -
      diff --git a/artbox-ecommerce/views/tax-option/view.php b/artbox-ecommerce/views/tax-option/view.php deleted file mode 100755 index 29960b4..0000000 --- a/artbox-ecommerce/views/tax-option/view.php +++ /dev/null @@ -1,81 +0,0 @@ -title = $model->id; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('rubrication', 'Groups'), - 'url' => [ 'tax-group/index' ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('rubrication', $group->id), - 'url' => [ - 'index', - 'group' => $group->id, - ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('rubrication', Yii::t('rubrication', 'Options of {title}', [ 'title' => $group->id ])), - 'url' => [ - 'index', - 'group' => $group->id, - ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - -

      - $model->id, - ], - [ 'class' => 'btn btn-primary' ] - ) ?> - $model->id, - ], - [ - 'class' => 'btn btn-danger', - 'data' => [ - 'confirm' => Yii::t('rubrication', 'Are you sure you want to delete this item?'), - 'method' => 'post', - ], - ] - ) ?> - id ], - [ 'class' => 'btn btn-success' ] - ) ?> -

      - - $model, - 'attributes' => [ - 'id', - 'group.id', - 'sort', - ], - ] - ) ?> - -
      diff --git a/artbox-ecommerce/views/variant/_form.php b/artbox-ecommerce/views/variant/_form.php deleted file mode 100755 index abbff5f..0000000 --- a/artbox-ecommerce/views/variant/_form.php +++ /dev/null @@ -1,207 +0,0 @@ -registerJs($js, View::POS_END); -?> -
      - - 'dynamic-form', - 'options' => [ 'enctype' => 'multipart/form-data' ], - ] - ); ?> - - field($model, 'product_id') - ->hiddenInput() - ->label(false); ?> - - field($model, 'sku') - ->textarea(); ?> - field($model, 'price') - ->textarea(); ?> - field($model, 'price_old') - ->textarea(); ?> - field($model, 'imagesUpload[]') - ->widget( - \kartik\file\FileInput::className(), - [ - 'language' => 'ru', - 'options' => [ - 'accept' => 'image/*', - 'multiple' => true, - ], - 'pluginOptions' => [ - 'allowedFileExtensions' => [ - 'jpg', - 'gif', - 'png', - ], - 'initialPreview' => !empty( $model->imagesHTML ) ? $model->imagesHTML : [], - 'initialPreviewConfig' => $model->imagesConfig, - 'overwriteInitial' => false, - 'showRemove' => false, - 'showUpload' => false, - 'uploadAsync' => !empty( $model->id ), - 'previewFileType' => 'image', - ], - ] - ); ?> - - $modelLangs, - 'formView' => '@common/modules/product/views/variant/_form_language', - 'form' => $form, - ] - ) ?> - - 'dynamicform_wrapper', - // required: only alphanumeric characters plus "_" [A-Za-z0-9_] - 'widgetBody' => '.container-items', - // required: css class selector - 'widgetItem' => '.item', - // required: css class - 'limit' => 10, - // the maximum times, an element can be added (default 999) - 'min' => 0, - // 0 or 1 (default 1) - 'insertButton' => '.add-item', - // css class - 'deleteButton' => '.remove-item', - // css class - 'model' => $stocks[ 0 ], - 'formId' => 'dynamic-form', - 'formFields' => [ - 'quantity', - 'title', - ], - ] - ); ?> - -
      -
      -

      - Склады - -

      -
      -
      -
      - $stock): ?> -
      -
      - isNewRecord) { - echo Html::activeHiddenInput($stock, "[{$i}]stock_id"); - } - ?> -
      -
      - field($stock, "[{$i}]quantity") - ->textInput([ 'maxlength' => true ]) ?> -
      -
      - field($stock, "[{$i}]title") - ->textInput([ 'maxlength' => true ]) ?> -
      -
      - -
      -
      -
      -
      - -
      -
      -
      - - - field($model, 'product_unit_id') - ->dropDownList( - ArrayHelper::map( - ProductUnit::find() - ->with('lang') - ->all(), - 'id', - 'lang.title' - ) - ) - ->label(Yii::t('product', 'Unit')) ?> - - with('lang') - ->all() as $group) { - /** - * @var TaxGroup $group - */ - echo $form->field($model, 'options') - ->checkboxList( - ArrayHelper::map( - $group->getOptions() - ->with('lang') - ->all(), - 'id', - 'lang.value' - ), - [ - 'multiple' => true, - 'unselect' => null, - ] - ) - ->label($group->lang->title); - } - } ?> - -
      - isNewRecord ? Yii::t('product', 'Create') : Yii::t('product', 'Update'), - [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ] - ) ?> -
      - - - -
      diff --git a/artbox-ecommerce/views/variant/_form_language.php b/artbox-ecommerce/views/variant/_form_language.php deleted file mode 100755 index 39c1482..0000000 --- a/artbox-ecommerce/views/variant/_form_language.php +++ /dev/null @@ -1,15 +0,0 @@ - -field($model_lang, '[' . $language->id . ']title') - ->textInput([ 'maxlength' => true ]); ?> \ No newline at end of file diff --git a/artbox-ecommerce/views/variant/create.php b/artbox-ecommerce/views/variant/create.php deleted file mode 100755 index f432298..0000000 --- a/artbox-ecommerce/views/variant/create.php +++ /dev/null @@ -1,55 +0,0 @@ -title = Yii::t('product', 'Create Variant'); - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Products'), - 'url' => [ '/product/manage/index' ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $product->lang->title, - 'url' => [ - '/product/manage/view', - 'id' => $product->id, - ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Variants'), - 'url' => [ - 'index', - 'product_id' => $product->id, - ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; -?> -
      - -

      title) ?>

      - - render( - '_form', - [ - 'model' => $model, - 'modelLangs' => $modelLangs, - 'groups' => $groups, - 'stocks' => $stocks, - 'product' => $product, - ] - ) ?> - -
      diff --git a/artbox-ecommerce/views/variant/index.php b/artbox-ecommerce/views/variant/index.php deleted file mode 100755 index 2e8cb34..0000000 --- a/artbox-ecommerce/views/variant/index.php +++ /dev/null @@ -1,120 +0,0 @@ -title = Yii::t('product', 'Variants for ') . $product->lang->title; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Products'), - 'url' => [ '/product/manage/index' ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $product->lang->title, - 'url' => [ - '/product/manage/view', - 'id' => $product->id, - ], - ]; - $this->params[ 'breadcrumbs' ][] = \Yii::t('product', 'Variants'); -?> -
      - -

      title) ?>

      - -

      - $product->id, - ] - ), - [ 'class' => 'btn btn-success' ] - ) ?> -

      - $dataProvider, - 'filterModel' => $searchModel, - 'columns' => [ - 'id', - [ - 'attribute' => 'variantName', - 'value' => 'lang.title', - ], - 'sku', - 'price', - 'price_old', - 'stock', - 'image.imageUrl:image', - [ - 'class' => 'yii\grid\ActionColumn', - 'buttons' => [ - 'view' => function ($url, $model) { - return Html::a( - '', - Url::to( - [ - 'view', - 'product_id' => $model->product_id, - 'id' => $model->id, - ] - ), - [ - 'title' => \Yii::t('app', "Просмотр"), - ] - ); - }, - 'update' => function ($url, $model) { - return Html::a( - '', - Url::to( - [ - 'update', - 'product_id' => $model->product_id, - 'id' => $model->id, - ] - ), - [ - 'title' => \Yii::t('app', "Редактировать"), - ] - ); - }, - 'delete' => function ($url, $model) { - - return Html::a( - '', - Url::to( - [ - 'delete', - 'product_id' => $model->product_id, - 'id' => $model->id, - ] - ), - [ - 'title' => Yii::t('yii', 'Delete'), - 'data-confirm' => Yii::t('yii', 'Are you sure to delete this item?'), - 'data-method' => 'post', - ] - ); - - }, - ], - ], - ], - ] - ); ?> -
      diff --git a/artbox-ecommerce/views/variant/update.php b/artbox-ecommerce/views/variant/update.php deleted file mode 100755 index 85c329f..0000000 --- a/artbox-ecommerce/views/variant/update.php +++ /dev/null @@ -1,73 +0,0 @@ -title = Yii::t( - 'product', - 'Update {modelClass}: ', - [ - 'modelClass' => 'Product', - ] - ) . ' ' . $model->lang->title; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Products'), - 'url' => [ '/product/manage/index' ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $model->product->lang->title, - 'url' => [ - '/product/manage/view', - 'id' => $model->product->id, - ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Variants'), - 'url' => Url::to( - [ - 'index', - 'product_id' => $model->product->id, - ] - ), - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', $model->lang->title), - 'url' => Url::to( - [ - 'view', - 'id' => $model->id, - ] - ), - ]; - $this->params[ 'breadcrumbs' ][] = Yii::t('product', 'Update'); -?> -
      - -

      title) ?>

      - - render( - '_form', - [ - 'model' => $model, - 'modelLangs' => $modelLangs, - 'groups' => $groups, - 'stocks' => $stocks, - 'product' => $product, - ] - ) ?> - -
      diff --git a/artbox-ecommerce/views/variant/view.php b/artbox-ecommerce/views/variant/view.php deleted file mode 100755 index 2a707e8..0000000 --- a/artbox-ecommerce/views/variant/view.php +++ /dev/null @@ -1,102 +0,0 @@ -title = $model->lang->title; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Products'), - 'url' => [ 'index' ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => $model->product->lang->title, - 'url' => [ - 'view', - 'id' => $model->product->id, - ], - ]; - $this->params[ 'breadcrumbs' ][] = [ - 'label' => Yii::t('product', 'Variants'), - 'url' => [ '/product/variant?product_id=' . $model->product->id ], - ]; - $this->params[ 'breadcrumbs' ][] = $this->title; - $properties_string = ''; - foreach ($properties as $property) { - $options_string = ''; - foreach ($property->options as $option) { - $options_string .= Html::tag('li', $option->lang->value); - } - $properties_string .= Html::tag('p', $property->lang->title) . Html::tag('ul', $options_string); - } -?> -
      - -

      title) ?>

      - -

      - $model->id, - ], - [ 'class' => 'btn btn-primary' ] - ) ?> - $model->id, - ], - [ - 'class' => 'btn btn-danger', - 'data' => [ - 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'), - 'method' => 'post', - ], - ] - ) ?> -

      - - $model, - 'attributes' => [ - 'id', - 'lang.title', - 'sku', - 'price', - 'price_old', - 'stock', - 'productUnit.lang.title', - [ - 'attribute' => 'product_id', - 'value' => Html::a( - $model->product->fullname, - [ - '/product/manage/view', - 'id' => $model->id, - ] - ), - 'format' => 'html', - ], - 'image.imageUrl:image', - [ - 'label' => \Yii::t('app', 'Properties'), - 'value' => $properties_string, - 'format' => 'html', - ], - ], - ] - ) ?> - -
      diff --git a/artbox-ecommerce/widgets/brandsCarouselWidget.php b/artbox-ecommerce/widgets/brandsCarouselWidget.php deleted file mode 100755 index f9713f1..0000000 --- a/artbox-ecommerce/widgets/brandsCarouselWidget.php +++ /dev/null @@ -1,29 +0,0 @@ -with('lang') - ->all(); - return $this->render( - 'brandsCarousel', - [ - 'brands' => $brands, - ] - ); - } - } - \ No newline at end of file diff --git a/artbox-ecommerce/widgets/lastProducts.php b/artbox-ecommerce/widgets/lastProducts.php deleted file mode 100755 index 14e4495..0000000 --- a/artbox-ecommerce/widgets/lastProducts.php +++ /dev/null @@ -1,28 +0,0 @@ -render( - 'products_block', - [ - 'title' => \Yii::t('product', 'Вы недавно просматривали'), - 'class' => 'last-products', - 'products' => ProductHelper::getLastProducts(true), - ] - ); - } - } - \ No newline at end of file diff --git a/artbox-ecommerce/widgets/similarProducts.php b/artbox-ecommerce/widgets/similarProducts.php deleted file mode 100755 index fbcc712..0000000 --- a/artbox-ecommerce/widgets/similarProducts.php +++ /dev/null @@ -1,41 +0,0 @@ -product, $this->count); - - if (!$this->title) { - $this->title = Yii::t('product', 'Similar products'); - } - - return $this->render( - 'products_block', - [ - 'title' => $this->title, - 'class' => 'similar-products', - 'products' => $products, - ] - ); - } - } - \ No newline at end of file diff --git a/artbox-ecommerce/widgets/specialProducts.php b/artbox-ecommerce/widgets/specialProducts.php deleted file mode 100755 index f8aad91..0000000 --- a/artbox-ecommerce/widgets/specialProducts.php +++ /dev/null @@ -1,51 +0,0 @@ -type, $this->count); - - if (!$this->title) { - switch ($this->type) { - case 'top': - $this->title = Yii::t('product', 'Top products'); - break; - case 'promo': - $this->title = Yii::t('product', 'Promo products'); - break; - case 'new': - $this->title = Yii::t('product', 'New products'); - break; - } - } - - return $this->render( - 'products_block', - [ - 'title' => $this->title, - 'class' => $this->type, - 'products' => $products, - ] - ); - } - } - \ No newline at end of file diff --git a/artbox-ecommerce/widgets/views/brandsCarousel.php b/artbox-ecommerce/widgets/views/brandsCarousel.php deleted file mode 100755 index 845ed10..0000000 --- a/artbox-ecommerce/widgets/views/brandsCarousel.php +++ /dev/null @@ -1,21 +0,0 @@ - - \ No newline at end of file diff --git a/artbox-ecommerce/widgets/views/product_smart.php b/artbox-ecommerce/widgets/views/product_smart.php deleted file mode 100755 index 4db88e7..0000000 --- a/artbox-ecommerce/widgets/views/product_smart.php +++ /dev/null @@ -1,110 +0,0 @@ - -
      -
      -
      - -
      - lang->title, - Url::to( - [ - 'catalog/product', - 'product' => $product->lang->alias, - ] - ), - [ 'class' => 'btn-product-details' ] - ) ?> -
      - -
      -
      - Цена: - - variant->price ?> - грн - -
      -
      -
      -
      Особенности
      -
      -
      -
        - -
      • Бренд: brand->lang->title ?>
      • - - getProperties() as $group): ?> -
      • - lang->title ?> customOptions as $option ) : ?> lang->value ?> -
      • - - - -
      -
      -
      -
      - -
      -
      -
      -
      -
      - \ No newline at end of file diff --git a/artbox-ecommerce/widgets/views/products_block.php b/artbox-ecommerce/widgets/views/products_block.php deleted file mode 100755 index ba647d7..0000000 --- a/artbox-ecommerce/widgets/views/products_block.php +++ /dev/null @@ -1,35 +0,0 @@ - - -
      -
      - -
      - - render('product_smart', [ 'product' => $product ]); ?> - -
      -
      -
      -
      - registerJs($js, View::POS_READY); - ?> - \ No newline at end of file diff --git a/artbox-ecommerce/widgets/views/submenu.php b/artbox-ecommerce/widgets/views/submenu.php deleted file mode 100755 index ccae3ff..0000000 --- a/artbox-ecommerce/widgets/views/submenu.php +++ /dev/null @@ -1,44 +0,0 @@ - - diff --git a/artbox-file/FileUploadAsset.php b/artbox-file/FileUploadAsset.php deleted file mode 100755 index 0c9fdcd..0000000 --- a/artbox-file/FileUploadAsset.php +++ /dev/null @@ -1,43 +0,0 @@ - - * @since 2.0 - */ -class FileUploadAsset extends AssetBundle -{ - - - /** - * @inheritdoc - */ - public function init() - { - parent::init(); - $this->sourcePath = __DIR__.'/assets'; - } - - public $css = [ - 'css/jquery.fileupload.css', - 'css/fileupload/style.css' - ]; - - public $js = [ - 'js/vendor/jquery.ui.widget.js', - 'js/jquery.iframe-transport.js', - 'js/jquery.fileupload.js' - ]; -} diff --git a/artbox-file/Module.php b/artbox-file/Module.php deleted file mode 100755 index 1855e9b..0000000 --- a/artbox-file/Module.php +++ /dev/null @@ -1,15 +0,0 @@ -').prop('href', options.postMessage)[0], - target = loc.protocol + '//' + loc.host, - xhrUpload = options.xhr().upload; - return { - send: function (_, completeCallback) { - counter += 1; - var message = { - id: 'postmessage-transport-' + counter - }, - eventName = 'message.' + message.id; - iframe = $( - '' - ).bind('load', function () { - $.each(names, function (i, name) { - message[name] = options[name]; - }); - message.dataType = message.dataType.replace('postmessage ', ''); - $(window).bind(eventName, function (e) { - e = e.originalEvent; - var data = e.data, - ev; - if (e.origin === target && data.id === message.id) { - if (data.type === 'progress') { - ev = document.createEvent('Event'); - ev.initEvent(data.type, false, true); - $.extend(ev, data); - xhrUpload.dispatchEvent(ev); - } else { - completeCallback( - data.status, - data.statusText, - {postmessage: data.result}, - data.headers - ); - iframe.remove(); - $(window).unbind(eventName); - } - } - }); - iframe[0].contentWindow.postMessage( - message, - target - ); - }).appendTo(document.body); - }, - abort: function () { - if (iframe) { - iframe.remove(); - } - } - }; - } - }); - -})); diff --git a/artbox-file/assets/js/cors/jquery.xdr-transport.js b/artbox-file/assets/js/cors/jquery.xdr-transport.js deleted file mode 100755 index d951827..0000000 --- a/artbox-file/assets/js/cors/jquery.xdr-transport.js +++ /dev/null @@ -1,89 +0,0 @@ -/* - * jQuery XDomainRequest Transport Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2011, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - * - * Based on Julian Aubourg's ajaxHooks xdr.js: - * https://github.com/jaubourg/ajaxHooks/ - */ - -/* global define, require, window, XDomainRequest */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define(['jquery'], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory(require('jquery')); - } else { - // Browser globals: - factory(window.jQuery); - } -}(function ($) { - 'use strict'; - if (window.XDomainRequest && !$.support.cors) { - $.ajaxTransport(function (s) { - if (s.crossDomain && s.async) { - if (s.timeout) { - s.xdrTimeout = s.timeout; - delete s.timeout; - } - var xdr; - return { - send: function (headers, completeCallback) { - var addParamChar = /\?/.test(s.url) ? '&' : '?'; - function callback(status, statusText, responses, responseHeaders) { - xdr.onload = xdr.onerror = xdr.ontimeout = $.noop; - xdr = null; - completeCallback(status, statusText, responses, responseHeaders); - } - xdr = new XDomainRequest(); - // XDomainRequest only supports GET and POST: - if (s.type === 'DELETE') { - s.url = s.url + addParamChar + '_method=DELETE'; - s.type = 'POST'; - } else if (s.type === 'PUT') { - s.url = s.url + addParamChar + '_method=PUT'; - s.type = 'POST'; - } else if (s.type === 'PATCH') { - s.url = s.url + addParamChar + '_method=PATCH'; - s.type = 'POST'; - } - xdr.open(s.type, s.url); - xdr.onload = function () { - callback( - 200, - 'OK', - {text: xdr.responseText}, - 'Content-Type: ' + xdr.contentType - ); - }; - xdr.onerror = function () { - callback(404, 'Not Found'); - }; - if (s.xdrTimeout) { - xdr.ontimeout = function () { - callback(0, 'timeout'); - }; - xdr.timeout = s.xdrTimeout; - } - xdr.send((s.hasContent && s.data) || null); - }, - abort: function () { - if (xdr) { - xdr.onerror = $.noop(); - xdr.abort(); - } - } - }; - } - }); - } -})); diff --git a/artbox-file/assets/js/jquery.fileupload-angular.js b/artbox-file/assets/js/jquery.fileupload-angular.js deleted file mode 100755 index 08bb488..0000000 --- a/artbox-file/assets/js/jquery.fileupload-angular.js +++ /dev/null @@ -1,425 +0,0 @@ -/* - * jQuery File Upload AngularJS Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2013, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* jshint nomen:false */ -/* global define, angular */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define([ - 'jquery', - 'angular', - './jquery.fileupload-image', - './jquery.fileupload-audio', - './jquery.fileupload-video', - './jquery.fileupload-validate' - ], factory); - } else { - factory(); - } -}(function () { - 'use strict'; - - angular.module('blueimp.fileupload', []) - - // The fileUpload service provides configuration options - // for the fileUpload directive and default handlers for - // File Upload events: - .provider('fileUpload', function () { - var scopeEvalAsync = function (expression) { - var scope = angular.element(this) - .fileupload('option', 'scope'); - // Schedule a new $digest cycle if not already inside of one - // and evaluate the given expression: - scope.$evalAsync(expression); - }, - addFileMethods = function (scope, data) { - var files = data.files, - file = files[0]; - angular.forEach(files, function (file, index) { - file._index = index; - file.$state = function () { - return data.state(); - }; - file.$processing = function () { - return data.processing(); - }; - file.$progress = function () { - return data.progress(); - }; - file.$response = function () { - return data.response(); - }; - }); - file.$submit = function () { - if (!file.error) { - return data.submit(); - } - }; - file.$cancel = function () { - return data.abort(); - }; - }, - $config; - $config = this.defaults = { - handleResponse: function (e, data) { - var files = data.result && data.result.files; - if (files) { - data.scope.replace(data.files, files); - } else if (data.errorThrown || - data.textStatus === 'error') { - data.files[0].error = data.errorThrown || - data.textStatus; - } - }, - add: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var scope = data.scope, - filesCopy = []; - angular.forEach(data.files, function (file) { - filesCopy.push(file); - }); - scope.$parent.$applyAsync(function () { - addFileMethods(scope, data); - var method = scope.option('prependFiles') ? - 'unshift' : 'push'; - Array.prototype[method].apply(scope.queue, data.files); - }); - data.process(function () { - return scope.process(data); - }).always(function () { - scope.$parent.$applyAsync(function () { - addFileMethods(scope, data); - scope.replace(filesCopy, data.files); - }); - }).then(function () { - if ((scope.option('autoUpload') || - data.autoUpload) && - data.autoUpload !== false) { - data.submit(); - } - }); - }, - done: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var that = this; - data.scope.$apply(function () { - data.handleResponse.call(that, e, data); - }); - }, - fail: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var that = this, - scope = data.scope; - if (data.errorThrown === 'abort') { - scope.clear(data.files); - return; - } - scope.$apply(function () { - data.handleResponse.call(that, e, data); - }); - }, - stop: scopeEvalAsync, - processstart: scopeEvalAsync, - processstop: scopeEvalAsync, - getNumberOfFiles: function () { - var scope = this.scope; - return scope.queue.length - scope.processing(); - }, - dataType: 'json', - autoUpload: false - }; - this.$get = [ - function () { - return { - defaults: $config - }; - } - ]; - }) - - // Format byte numbers to readable presentations: - .provider('formatFileSizeFilter', function () { - var $config = { - // Byte units following the IEC format - // http://en.wikipedia.org/wiki/Kilobyte - units: [ - {size: 1000000000, suffix: ' GB'}, - {size: 1000000, suffix: ' MB'}, - {size: 1000, suffix: ' KB'} - ] - }; - this.defaults = $config; - this.$get = function () { - return function (bytes) { - if (!angular.isNumber(bytes)) { - return ''; - } - var unit = true, - i = 0, - prefix, - suffix; - while (unit) { - unit = $config.units[i]; - prefix = unit.prefix || ''; - suffix = unit.suffix || ''; - if (i === $config.units.length - 1 || bytes >= unit.size) { - return prefix + (bytes / unit.size).toFixed(2) + suffix; - } - i += 1; - } - }; - }; - }) - - // The FileUploadController initializes the fileupload widget and - // provides scope methods to control the File Upload functionality: - .controller('FileUploadController', [ - '$scope', '$element', '$attrs', '$window', 'fileUpload', - function ($scope, $element, $attrs, $window, fileUpload) { - var uploadMethods = { - progress: function () { - return $element.fileupload('progress'); - }, - active: function () { - return $element.fileupload('active'); - }, - option: function (option, data) { - if (arguments.length === 1) { - return $element.fileupload('option', option); - } - $element.fileupload('option', option, data); - }, - add: function (data) { - return $element.fileupload('add', data); - }, - send: function (data) { - return $element.fileupload('send', data); - }, - process: function (data) { - return $element.fileupload('process', data); - }, - processing: function (data) { - return $element.fileupload('processing', data); - } - }; - $scope.disabled = !$window.jQuery.support.fileInput; - $scope.queue = $scope.queue || []; - $scope.clear = function (files) { - var queue = this.queue, - i = queue.length, - file = files, - length = 1; - if (angular.isArray(files)) { - file = files[0]; - length = files.length; - } - while (i) { - i -= 1; - if (queue[i] === file) { - return queue.splice(i, length); - } - } - }; - $scope.replace = function (oldFiles, newFiles) { - var queue = this.queue, - file = oldFiles[0], - i, - j; - for (i = 0; i < queue.length; i += 1) { - if (queue[i] === file) { - for (j = 0; j < newFiles.length; j += 1) { - queue[i + j] = newFiles[j]; - } - return; - } - } - }; - $scope.applyOnQueue = function (method) { - var list = this.queue.slice(0), - i, - file; - for (i = 0; i < list.length; i += 1) { - file = list[i]; - if (file[method]) { - file[method](); - } - } - }; - $scope.submit = function () { - this.applyOnQueue('$submit'); - }; - $scope.cancel = function () { - this.applyOnQueue('$cancel'); - }; - // Add upload methods to the scope: - angular.extend($scope, uploadMethods); - // The fileupload widget will initialize with - // the options provided via "data-"-parameters, - // as well as those given via options object: - $element.fileupload(angular.extend( - {scope: $scope}, - fileUpload.defaults - )).on('fileuploadadd', function (e, data) { - data.scope = $scope; - }).on('fileuploadfail', function (e, data) { - if (data.errorThrown === 'abort') { - return; - } - if (data.dataType && - data.dataType.indexOf('json') === data.dataType.length - 4) { - try { - data.result = angular.fromJson(data.jqXHR.responseText); - } catch (ignore) {} - } - }).on([ - 'fileuploadadd', - 'fileuploadsubmit', - 'fileuploadsend', - 'fileuploaddone', - 'fileuploadfail', - 'fileuploadalways', - 'fileuploadprogress', - 'fileuploadprogressall', - 'fileuploadstart', - 'fileuploadstop', - 'fileuploadchange', - 'fileuploadpaste', - 'fileuploaddrop', - 'fileuploaddragover', - 'fileuploadchunksend', - 'fileuploadchunkdone', - 'fileuploadchunkfail', - 'fileuploadchunkalways', - 'fileuploadprocessstart', - 'fileuploadprocess', - 'fileuploadprocessdone', - 'fileuploadprocessfail', - 'fileuploadprocessalways', - 'fileuploadprocessstop' - ].join(' '), function (e, data) { - $scope.$parent.$applyAsync(function () { - if ($scope.$emit(e.type, data).defaultPrevented) { - e.preventDefault(); - } - }); - }).on('remove', function () { - // Remove upload methods from the scope, - // when the widget is removed: - var method; - for (method in uploadMethods) { - if (uploadMethods.hasOwnProperty(method)) { - delete $scope[method]; - } - } - }); - // Observe option changes: - $scope.$watch( - $attrs.fileUpload, - function (newOptions) { - if (newOptions) { - $element.fileupload('option', newOptions); - } - } - ); - } - ]) - - // Provide File Upload progress feedback: - .controller('FileUploadProgressController', [ - '$scope', '$attrs', '$parse', - function ($scope, $attrs, $parse) { - var fn = $parse($attrs.fileUploadProgress), - update = function () { - var progress = fn($scope); - if (!progress || !progress.total) { - return; - } - $scope.num = Math.floor( - progress.loaded / progress.total * 100 - ); - }; - update(); - $scope.$watch( - $attrs.fileUploadProgress + '.loaded', - function (newValue, oldValue) { - if (newValue !== oldValue) { - update(); - } - } - ); - } - ]) - - // Display File Upload previews: - .controller('FileUploadPreviewController', [ - '$scope', '$element', '$attrs', - function ($scope, $element, $attrs) { - $scope.$watch( - $attrs.fileUploadPreview + '.preview', - function (preview) { - $element.empty(); - if (preview) { - $element.append(preview); - } - } - ); - } - ]) - - .directive('fileUpload', function () { - return { - controller: 'FileUploadController', - scope: true - }; - }) - - .directive('fileUploadProgress', function () { - return { - controller: 'FileUploadProgressController', - scope: true - }; - }) - - .directive('fileUploadPreview', function () { - return { - controller: 'FileUploadPreviewController' - }; - }) - - // Enhance the HTML5 download attribute to - // allow drag&drop of files to the desktop: - .directive('download', function () { - return function (scope, elm) { - elm.on('dragstart', function (e) { - try { - e.originalEvent.dataTransfer.setData( - 'DownloadURL', - [ - 'application/octet-stream', - elm.prop('download'), - elm.prop('href') - ].join(':') - ); - } catch (ignore) {} - }); - }; - }); - -})); diff --git a/artbox-file/assets/js/jquery.fileupload-audio.js b/artbox-file/assets/js/jquery.fileupload-audio.js deleted file mode 100755 index 971658b..0000000 --- a/artbox-file/assets/js/jquery.fileupload-audio.js +++ /dev/null @@ -1,112 +0,0 @@ -/* - * jQuery File Upload Audio Preview Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2013, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* jshint nomen:false */ -/* global define, require, window, document */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define([ - 'jquery', - 'load-image', - './jquery.fileupload-process' - ], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory( - require('jquery'), - require('load-image') - ); - } else { - // Browser globals: - factory( - window.jQuery, - window.loadImage - ); - } -}(function ($, loadImage) { - 'use strict'; - - // Prepend to the default processQueue: - $.blueimp.fileupload.prototype.options.processQueue.unshift( - { - action: 'loadAudio', - // Use the action as prefix for the "@" options: - prefix: true, - fileTypes: '@', - maxFileSize: '@', - disabled: '@disableAudioPreview' - }, - { - action: 'setAudio', - name: '@audioPreviewName', - disabled: '@disableAudioPreview' - } - ); - - // The File Upload Audio Preview plugin extends the fileupload widget - // with audio preview functionality: - $.widget('blueimp.fileupload', $.blueimp.fileupload, { - - options: { - // The regular expression for the types of audio files to load, - // matched against the file type: - loadAudioFileTypes: /^audio\/.*$/ - }, - - _audioElement: document.createElement('audio'), - - processActions: { - - // Loads the audio file given via data.files and data.index - // as audio element if the browser supports playing it. - // Accepts the options fileTypes (regular expression) - // and maxFileSize (integer) to limit the files to load: - loadAudio: function (data, options) { - if (options.disabled) { - return data; - } - var file = data.files[data.index], - url, - audio; - if (this._audioElement.canPlayType && - this._audioElement.canPlayType(file.type) && - ($.type(options.maxFileSize) !== 'number' || - file.size <= options.maxFileSize) && - (!options.fileTypes || - options.fileTypes.test(file.type))) { - url = loadImage.createObjectURL(file); - if (url) { - audio = this._audioElement.cloneNode(false); - audio.src = url; - audio.controls = true; - data.audio = audio; - return data; - } - } - return data; - }, - - // Sets the audio element as a property of the file object: - setAudio: function (data, options) { - if (data.audio && !options.disabled) { - data.files[data.index][options.name || 'preview'] = data.audio; - } - return data; - } - - } - - }); - -})); diff --git a/artbox-file/assets/js/jquery.fileupload-image.js b/artbox-file/assets/js/jquery.fileupload-image.js deleted file mode 100755 index d445120..0000000 --- a/artbox-file/assets/js/jquery.fileupload-image.js +++ /dev/null @@ -1,321 +0,0 @@ -/* - * jQuery File Upload Image Preview & Resize Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2013, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* jshint nomen:false */ -/* global define, require, window, Blob */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define([ - 'jquery', - 'load-image', - 'load-image-meta', - 'load-image-exif', - 'load-image-ios', - 'canvas-to-blob', - './jquery.fileupload-process' - ], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory( - require('jquery'), - require('load-image') - ); - } else { - // Browser globals: - factory( - window.jQuery, - window.loadImage - ); - } -}(function ($, loadImage) { - 'use strict'; - - // Prepend to the default processQueue: - $.blueimp.fileupload.prototype.options.processQueue.unshift( - { - action: 'loadImageMetaData', - disableImageHead: '@', - disableExif: '@', - disableExifThumbnail: '@', - disableExifSub: '@', - disableExifGps: '@', - disabled: '@disableImageMetaDataLoad' - }, - { - action: 'loadImage', - // Use the action as prefix for the "@" options: - prefix: true, - fileTypes: '@', - maxFileSize: '@', - noRevoke: '@', - disabled: '@disableImageLoad' - }, - { - action: 'resizeImage', - // Use "image" as prefix for the "@" options: - prefix: 'image', - maxWidth: '@', - maxHeight: '@', - minWidth: '@', - minHeight: '@', - crop: '@', - orientation: '@', - forceResize: '@', - disabled: '@disableImageResize' - }, - { - action: 'saveImage', - quality: '@imageQuality', - type: '@imageType', - disabled: '@disableImageResize' - }, - { - action: 'saveImageMetaData', - disabled: '@disableImageMetaDataSave' - }, - { - action: 'resizeImage', - // Use "preview" as prefix for the "@" options: - prefix: 'preview', - maxWidth: '@', - maxHeight: '@', - minWidth: '@', - minHeight: '@', - crop: '@', - orientation: '@', - thumbnail: '@', - canvas: '@', - disabled: '@disableImagePreview' - }, - { - action: 'setImage', - name: '@imagePreviewName', - disabled: '@disableImagePreview' - }, - { - action: 'deleteImageReferences', - disabled: '@disableImageReferencesDeletion' - } - ); - - // The File Upload Resize plugin extends the fileupload widget - // with image resize functionality: - $.widget('blueimp.fileupload', $.blueimp.fileupload, { - - options: { - // The regular expression for the types of images to load: - // matched against the file type: - loadImageFileTypes: /^image\/(gif|jpeg|png|svg\+xml)$/, - // The maximum file size of images to load: - loadImageMaxFileSize: 10000000, // 10MB - // The maximum width of resized images: - imageMaxWidth: 1920, - // The maximum height of resized images: - imageMaxHeight: 1080, - // Defines the image orientation (1-8) or takes the orientation - // value from Exif data if set to true: - imageOrientation: false, - // Define if resized images should be cropped or only scaled: - imageCrop: false, - // Disable the resize image functionality by default: - disableImageResize: true, - // The maximum width of the preview images: - previewMaxWidth: 80, - // The maximum height of the preview images: - previewMaxHeight: 80, - // Defines the preview orientation (1-8) or takes the orientation - // value from Exif data if set to true: - previewOrientation: true, - // Create the preview using the Exif data thumbnail: - previewThumbnail: true, - // Define if preview images should be cropped or only scaled: - previewCrop: false, - // Define if preview images should be resized as canvas elements: - previewCanvas: true - }, - - processActions: { - - // Loads the image given via data.files and data.index - // as img element, if the browser supports the File API. - // Accepts the options fileTypes (regular expression) - // and maxFileSize (integer) to limit the files to load: - loadImage: function (data, options) { - if (options.disabled) { - return data; - } - var that = this, - file = data.files[data.index], - dfd = $.Deferred(); - if (($.type(options.maxFileSize) === 'number' && - file.size > options.maxFileSize) || - (options.fileTypes && - !options.fileTypes.test(file.type)) || - !loadImage( - file, - function (img) { - if (img.src) { - data.img = img; - } - dfd.resolveWith(that, [data]); - }, - options - )) { - return data; - } - return dfd.promise(); - }, - - // Resizes the image given as data.canvas or data.img - // and updates data.canvas or data.img with the resized image. - // Also stores the resized image as preview property. - // Accepts the options maxWidth, maxHeight, minWidth, - // minHeight, canvas and crop: - resizeImage: function (data, options) { - if (options.disabled || !(data.canvas || data.img)) { - return data; - } - options = $.extend({canvas: true}, options); - var that = this, - dfd = $.Deferred(), - img = (options.canvas && data.canvas) || data.img, - resolve = function (newImg) { - if (newImg && (newImg.width !== img.width || - newImg.height !== img.height || - options.forceResize)) { - data[newImg.getContext ? 'canvas' : 'img'] = newImg; - } - data.preview = newImg; - dfd.resolveWith(that, [data]); - }, - thumbnail; - if (data.exif) { - if (options.orientation === true) { - options.orientation = data.exif.get('Orientation'); - } - if (options.thumbnail) { - thumbnail = data.exif.get('Thumbnail'); - if (thumbnail) { - loadImage(thumbnail, resolve, options); - return dfd.promise(); - } - } - // Prevent orienting the same image twice: - if (data.orientation) { - delete options.orientation; - } else { - data.orientation = options.orientation; - } - } - if (img) { - resolve(loadImage.scale(img, options)); - return dfd.promise(); - } - return data; - }, - - // Saves the processed image given as data.canvas - // inplace at data.index of data.files: - saveImage: function (data, options) { - if (!data.canvas || options.disabled) { - return data; - } - var that = this, - file = data.files[data.index], - dfd = $.Deferred(); - if (data.canvas.toBlob) { - data.canvas.toBlob( - function (blob) { - if (!blob.name) { - if (file.type === blob.type) { - blob.name = file.name; - } else if (file.name) { - blob.name = file.name.replace( - /\.\w+$/, - '.' + blob.type.substr(6) - ); - } - } - // Don't restore invalid meta data: - if (file.type !== blob.type) { - delete data.imageHead; - } - // Store the created blob at the position - // of the original file in the files list: - data.files[data.index] = blob; - dfd.resolveWith(that, [data]); - }, - options.type || file.type, - options.quality - ); - } else { - return data; - } - return dfd.promise(); - }, - - loadImageMetaData: function (data, options) { - if (options.disabled) { - return data; - } - var that = this, - dfd = $.Deferred(); - loadImage.parseMetaData(data.files[data.index], function (result) { - $.extend(data, result); - dfd.resolveWith(that, [data]); - }, options); - return dfd.promise(); - }, - - saveImageMetaData: function (data, options) { - if (!(data.imageHead && data.canvas && - data.canvas.toBlob && !options.disabled)) { - return data; - } - var file = data.files[data.index], - blob = new Blob([ - data.imageHead, - // Resized images always have a head size of 20 bytes, - // including the JPEG marker and a minimal JFIF header: - this._blobSlice.call(file, 20) - ], {type: file.type}); - blob.name = file.name; - data.files[data.index] = blob; - return data; - }, - - // Sets the resized version of the image as a property of the - // file object, must be called after "saveImage": - setImage: function (data, options) { - if (data.preview && !options.disabled) { - data.files[data.index][options.name || 'preview'] = data.preview; - } - return data; - }, - - deleteImageReferences: function (data, options) { - if (!options.disabled) { - delete data.img; - delete data.canvas; - delete data.preview; - delete data.imageHead; - } - return data; - } - - } - - }); - -})); diff --git a/artbox-file/assets/js/jquery.fileupload-jquery-ui.js b/artbox-file/assets/js/jquery.fileupload-jquery-ui.js deleted file mode 100755 index a6b2804..0000000 --- a/artbox-file/assets/js/jquery.fileupload-jquery-ui.js +++ /dev/null @@ -1,155 +0,0 @@ -/* - * jQuery File Upload jQuery UI Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2013, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* jshint nomen:false */ -/* global define, require, window */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define(['jquery', './jquery.fileupload-ui'], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory(require('jquery')); - } else { - // Browser globals: - factory(window.jQuery); - } -}(function ($) { - 'use strict'; - - $.widget('blueimp.fileupload', $.blueimp.fileupload, { - - options: { - processdone: function (e, data) { - data.context.find('.start').button('enable'); - }, - progress: function (e, data) { - if (data.context) { - data.context.find('.progress').progressbar( - 'option', - 'value', - parseInt(data.loaded / data.total * 100, 10) - ); - } - }, - progressall: function (e, data) { - var $this = $(this); - $this.find('.fileupload-progress') - .find('.progress').progressbar( - 'option', - 'value', - parseInt(data.loaded / data.total * 100, 10) - ).end() - .find('.progress-extended').each(function () { - $(this).html( - ($this.data('blueimp-fileupload') || - $this.data('fileupload')) - ._renderExtendedProgress(data) - ); - }); - } - }, - - _renderUpload: function (func, files) { - var node = this._super(func, files), - showIconText = $(window).width() > 480; - node.find('.progress').empty().progressbar(); - node.find('.start').button({ - icons: {primary: 'ui-icon-circle-arrow-e'}, - text: showIconText - }); - node.find('.cancel').button({ - icons: {primary: 'ui-icon-cancel'}, - text: showIconText - }); - if (node.hasClass('fade')) { - node.hide(); - } - return node; - }, - - _renderDownload: function (func, files) { - var node = this._super(func, files), - showIconText = $(window).width() > 480; - node.find('.delete').button({ - icons: {primary: 'ui-icon-trash'}, - text: showIconText - }); - if (node.hasClass('fade')) { - node.hide(); - } - return node; - }, - - _startHandler: function (e) { - $(e.currentTarget).button('disable'); - this._super(e); - }, - - _transition: function (node) { - var deferred = $.Deferred(); - if (node.hasClass('fade')) { - node.fadeToggle( - this.options.transitionDuration, - this.options.transitionEasing, - function () { - deferred.resolveWith(node); - } - ); - } else { - deferred.resolveWith(node); - } - return deferred; - }, - - _create: function () { - this._super(); - this.element - .find('.fileupload-buttonbar') - .find('.fileinput-button').each(function () { - var input = $(this).find('input:file').detach(); - $(this) - .button({icons: {primary: 'ui-icon-plusthick'}}) - .append(input); - }) - .end().find('.start') - .button({icons: {primary: 'ui-icon-circle-arrow-e'}}) - .end().find('.cancel') - .button({icons: {primary: 'ui-icon-cancel'}}) - .end().find('.delete') - .button({icons: {primary: 'ui-icon-trash'}}) - .end().find('.progress').progressbar(); - }, - - _destroy: function () { - this.element - .find('.fileupload-buttonbar') - .find('.fileinput-button').each(function () { - var input = $(this).find('input:file').detach(); - $(this) - .button('destroy') - .append(input); - }) - .end().find('.start') - .button('destroy') - .end().find('.cancel') - .button('destroy') - .end().find('.delete') - .button('destroy') - .end().find('.progress').progressbar('destroy'); - this._super(); - } - - }); - -})); diff --git a/artbox-file/assets/js/jquery.fileupload-process.js b/artbox-file/assets/js/jquery.fileupload-process.js deleted file mode 100755 index aa7acec..0000000 --- a/artbox-file/assets/js/jquery.fileupload-process.js +++ /dev/null @@ -1,175 +0,0 @@ -/* - * jQuery File Upload Processing Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2012, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* jshint nomen:false */ -/* global define, require, window */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define([ - 'jquery', - './jquery.fileupload' - ], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory(require('jquery')); - } else { - // Browser globals: - factory( - window.jQuery - ); - } -}(function ($) { - 'use strict'; - - var originalAdd = $.blueimp.fileupload.prototype.options.add; - - // The File Upload Processing plugin extends the fileupload widget - // with file processing functionality: - $.widget('blueimp.fileupload', $.blueimp.fileupload, { - - options: { - // The list of processing actions: - processQueue: [ - /* - { - action: 'log', - type: 'debug' - } - */ - ], - add: function (e, data) { - var $this = $(this); - data.process(function () { - return $this.fileupload('process', data); - }); - originalAdd.call(this, e, data); - } - }, - - processActions: { - /* - log: function (data, options) { - console[options.type]( - 'Processing "' + data.files[data.index].name + '"' - ); - } - */ - }, - - _processFile: function (data, originalData) { - var that = this, - dfd = $.Deferred().resolveWith(that, [data]), - chain = dfd.promise(); - this._trigger('process', null, data); - $.each(data.processQueue, function (i, settings) { - var func = function (data) { - if (originalData.errorThrown) { - return $.Deferred() - .rejectWith(that, [originalData]).promise(); - } - return that.processActions[settings.action].call( - that, - data, - settings - ); - }; - chain = chain.pipe(func, settings.always && func); - }); - chain - .done(function () { - that._trigger('processdone', null, data); - that._trigger('processalways', null, data); - }) - .fail(function () { - that._trigger('processfail', null, data); - that._trigger('processalways', null, data); - }); - return chain; - }, - - // Replaces the settings of each processQueue item that - // are strings starting with an "@", using the remaining - // substring as key for the option map, - // e.g. "@autoUpload" is replaced with options.autoUpload: - _transformProcessQueue: function (options) { - var processQueue = []; - $.each(options.processQueue, function () { - var settings = {}, - action = this.action, - prefix = this.prefix === true ? action : this.prefix; - $.each(this, function (key, value) { - if ($.type(value) === 'string' && - value.charAt(0) === '@') { - settings[key] = options[ - value.slice(1) || (prefix ? prefix + - key.charAt(0).toUpperCase() + key.slice(1) : key) - ]; - } else { - settings[key] = value; - } - - }); - processQueue.push(settings); - }); - options.processQueue = processQueue; - }, - - // Returns the number of files currently in the processsing queue: - processing: function () { - return this._processing; - }, - - // Processes the files given as files property of the data parameter, - // returns a Promise object that allows to bind callbacks: - process: function (data) { - var that = this, - options = $.extend({}, this.options, data); - if (options.processQueue && options.processQueue.length) { - this._transformProcessQueue(options); - if (this._processing === 0) { - this._trigger('processstart'); - } - $.each(data.files, function (index) { - var opts = index ? $.extend({}, options) : options, - func = function () { - if (data.errorThrown) { - return $.Deferred() - .rejectWith(that, [data]).promise(); - } - return that._processFile(opts, data); - }; - opts.index = index; - that._processing += 1; - that._processingQueue = that._processingQueue.pipe(func, func) - .always(function () { - that._processing -= 1; - if (that._processing === 0) { - that._trigger('processstop'); - } - }); - }); - } - return this._processingQueue; - }, - - _create: function () { - this._super(); - this._processing = 0; - this._processingQueue = $.Deferred().resolveWith(this) - .promise(); - } - - }); - -})); diff --git a/artbox-file/assets/js/jquery.fileupload-ui.js b/artbox-file/assets/js/jquery.fileupload-ui.js deleted file mode 100755 index 26eacb3..0000000 --- a/artbox-file/assets/js/jquery.fileupload-ui.js +++ /dev/null @@ -1,710 +0,0 @@ -/* - * jQuery File Upload User Interface Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2010, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* jshint nomen:false */ -/* global define, require, window */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define([ - 'jquery', - 'tmpl', - './jquery.fileupload-image', - './jquery.fileupload-audio', - './jquery.fileupload-video', - './jquery.fileupload-validate' - ], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory( - require('jquery'), - require('tmpl') - ); - } else { - // Browser globals: - factory( - window.jQuery, - window.tmpl - ); - } -}(function ($, tmpl) { - 'use strict'; - - $.blueimp.fileupload.prototype._specialOptions.push( - 'filesContainer', - 'uploadTemplateId', - 'downloadTemplateId' - ); - - // The UI version extends the file upload widget - // and adds complete user interface interaction: - $.widget('blueimp.fileupload', $.blueimp.fileupload, { - - options: { - // By default, files added to the widget are uploaded as soon - // as the user clicks on the start buttons. To enable automatic - // uploads, set the following option to true: - autoUpload: false, - // The ID of the upload template: - uploadTemplateId: 'template-upload', - // The ID of the download template: - downloadTemplateId: 'template-download', - // The container for the list of files. If undefined, it is set to - // an element with class "files" inside of the widget element: - filesContainer: undefined, - // By default, files are appended to the files container. - // Set the following option to true, to prepend files instead: - prependFiles: false, - // The expected data type of the upload response, sets the dataType - // option of the $.ajax upload requests: - dataType: 'json', - - // Error and info messages: - messages: { - unknownError: 'Unknown error' - }, - - // Function returning the current number of files, - // used by the maxNumberOfFiles validation: - getNumberOfFiles: function () { - return this.filesContainer.children() - .not('.processing').length; - }, - - // Callback to retrieve the list of files from the server response: - getFilesFromResponse: function (data) { - if (data.result && $.isArray(data.result.files)) { - return data.result.files; - } - return []; - }, - - // The add callback is invoked as soon as files are added to the fileupload - // widget (via file input selection, drag & drop or add API call). - // See the basic file upload widget for more information: - add: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var $this = $(this), - that = $this.data('blueimp-fileupload') || - $this.data('fileupload'), - options = that.options; - data.context = that._renderUpload(data.files) - .data('data', data) - .addClass('processing'); - options.filesContainer[ - options.prependFiles ? 'prepend' : 'append' - ](data.context); - that._forceReflow(data.context); - that._transition(data.context); - data.process(function () { - return $this.fileupload('process', data); - }).always(function () { - data.context.each(function (index) { - $(this).find('.size').text( - that._formatFileSize(data.files[index].size) - ); - }).removeClass('processing'); - that._renderPreviews(data); - }).done(function () { - data.context.find('.start').prop('disabled', false); - if ((that._trigger('added', e, data) !== false) && - (options.autoUpload || data.autoUpload) && - data.autoUpload !== false) { - data.submit(); - } - }).fail(function () { - if (data.files.error) { - data.context.each(function (index) { - var error = data.files[index].error; - if (error) { - $(this).find('.error').text(error); - } - }); - } - }); - }, - // Callback for the start of each file upload request: - send: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'); - if (data.context && data.dataType && - data.dataType.substr(0, 6) === 'iframe') { - // Iframe Transport does not support progress events. - // In lack of an indeterminate progress bar, we set - // the progress to 100%, showing the full animated bar: - data.context - .find('.progress').addClass( - !$.support.transition && 'progress-animated' - ) - .attr('aria-valuenow', 100) - .children().first().css( - 'width', - '100%' - ); - } - return that._trigger('sent', e, data); - }, - // Callback for successful uploads: - done: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'), - getFilesFromResponse = data.getFilesFromResponse || - that.options.getFilesFromResponse, - files = getFilesFromResponse(data), - template, - deferred; - if (data.context) { - data.context.each(function (index) { - var file = files[index] || - {error: 'Empty file upload result'}; - deferred = that._addFinishedDeferreds(); - that._transition($(this)).done( - function () { - var node = $(this); - template = that._renderDownload([file]) - .replaceAll(node); - that._forceReflow(template); - that._transition(template).done( - function () { - data.context = $(this); - that._trigger('completed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); - } - ); - } - ); - }); - } else { - template = that._renderDownload(files)[ - that.options.prependFiles ? 'prependTo' : 'appendTo' - ](that.options.filesContainer); - that._forceReflow(template); - deferred = that._addFinishedDeferreds(); - that._transition(template).done( - function () { - data.context = $(this); - that._trigger('completed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); - } - ); - } - }, - // Callback for failed (abort or error) uploads: - fail: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'), - template, - deferred; - if (data.context) { - data.context.each(function (index) { - if (data.errorThrown !== 'abort') { - var file = data.files[index]; - file.error = file.error || data.errorThrown || - data.i18n('unknownError'); - deferred = that._addFinishedDeferreds(); - that._transition($(this)).done( - function () { - var node = $(this); - template = that._renderDownload([file]) - .replaceAll(node); - that._forceReflow(template); - that._transition(template).done( - function () { - data.context = $(this); - that._trigger('failed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); - } - ); - } - ); - } else { - deferred = that._addFinishedDeferreds(); - that._transition($(this)).done( - function () { - $(this).remove(); - that._trigger('failed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); - } - ); - } - }); - } else if (data.errorThrown !== 'abort') { - data.context = that._renderUpload(data.files)[ - that.options.prependFiles ? 'prependTo' : 'appendTo' - ](that.options.filesContainer) - .data('data', data); - that._forceReflow(data.context); - deferred = that._addFinishedDeferreds(); - that._transition(data.context).done( - function () { - data.context = $(this); - that._trigger('failed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); - } - ); - } else { - that._trigger('failed', e, data); - that._trigger('finished', e, data); - that._addFinishedDeferreds().resolve(); - } - }, - // Callback for upload progress events: - progress: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var progress = Math.floor(data.loaded / data.total * 100); - if (data.context) { - data.context.each(function () { - $(this).find('.progress') - .attr('aria-valuenow', progress) - .children().first().css( - 'width', - progress + '%' - ); - }); - } - }, - // Callback for global upload progress events: - progressall: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var $this = $(this), - progress = Math.floor(data.loaded / data.total * 100), - globalProgressNode = $this.find('.fileupload-progress'), - extendedProgressNode = globalProgressNode - .find('.progress-extended'); - if (extendedProgressNode.length) { - extendedProgressNode.html( - ($this.data('blueimp-fileupload') || $this.data('fileupload')) - ._renderExtendedProgress(data) - ); - } - globalProgressNode - .find('.progress') - .attr('aria-valuenow', progress) - .children().first().css( - 'width', - progress + '%' - ); - }, - // Callback for uploads start, equivalent to the global ajaxStart event: - start: function (e) { - if (e.isDefaultPrevented()) { - return false; - } - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'); - that._resetFinishedDeferreds(); - that._transition($(this).find('.fileupload-progress')).done( - function () { - that._trigger('started', e); - } - ); - }, - // Callback for uploads stop, equivalent to the global ajaxStop event: - stop: function (e) { - if (e.isDefaultPrevented()) { - return false; - } - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'), - deferred = that._addFinishedDeferreds(); - $.when.apply($, that._getFinishedDeferreds()) - .done(function () { - that._trigger('stopped', e); - }); - that._transition($(this).find('.fileupload-progress')).done( - function () { - $(this).find('.progress') - .attr('aria-valuenow', '0') - .children().first().css('width', '0%'); - $(this).find('.progress-extended').html(' '); - deferred.resolve(); - } - ); - }, - processstart: function (e) { - if (e.isDefaultPrevented()) { - return false; - } - $(this).addClass('fileupload-processing'); - }, - processstop: function (e) { - if (e.isDefaultPrevented()) { - return false; - } - $(this).removeClass('fileupload-processing'); - }, - // Callback for file deletion: - destroy: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'), - removeNode = function () { - that._transition(data.context).done( - function () { - $(this).remove(); - that._trigger('destroyed', e, data); - } - ); - }; - if (data.url) { - data.dataType = data.dataType || that.options.dataType; - $.ajax(data).done(removeNode).fail(function () { - that._trigger('destroyfailed', e, data); - }); - } else { - removeNode(); - } - } - }, - - _resetFinishedDeferreds: function () { - this._finishedUploads = []; - }, - - _addFinishedDeferreds: function (deferred) { - if (!deferred) { - deferred = $.Deferred(); - } - this._finishedUploads.push(deferred); - return deferred; - }, - - _getFinishedDeferreds: function () { - return this._finishedUploads; - }, - - // Link handler, that allows to download files - // by drag & drop of the links to the desktop: - _enableDragToDesktop: function () { - var link = $(this), - url = link.prop('href'), - name = link.prop('download'), - type = 'application/octet-stream'; - link.bind('dragstart', function (e) { - try { - e.originalEvent.dataTransfer.setData( - 'DownloadURL', - [type, name, url].join(':') - ); - } catch (ignore) {} - }); - }, - - _formatFileSize: function (bytes) { - if (typeof bytes !== 'number') { - return ''; - } - if (bytes >= 1000000000) { - return (bytes / 1000000000).toFixed(2) + ' GB'; - } - if (bytes >= 1000000) { - return (bytes / 1000000).toFixed(2) + ' MB'; - } - return (bytes / 1000).toFixed(2) + ' KB'; - }, - - _formatBitrate: function (bits) { - if (typeof bits !== 'number') { - return ''; - } - if (bits >= 1000000000) { - return (bits / 1000000000).toFixed(2) + ' Gbit/s'; - } - if (bits >= 1000000) { - return (bits / 1000000).toFixed(2) + ' Mbit/s'; - } - if (bits >= 1000) { - return (bits / 1000).toFixed(2) + ' kbit/s'; - } - return bits.toFixed(2) + ' bit/s'; - }, - - _formatTime: function (seconds) { - var date = new Date(seconds * 1000), - days = Math.floor(seconds / 86400); - days = days ? days + 'd ' : ''; - return days + - ('0' + date.getUTCHours()).slice(-2) + ':' + - ('0' + date.getUTCMinutes()).slice(-2) + ':' + - ('0' + date.getUTCSeconds()).slice(-2); - }, - - _formatPercentage: function (floatValue) { - return (floatValue * 100).toFixed(2) + ' %'; - }, - - _renderExtendedProgress: function (data) { - return this._formatBitrate(data.bitrate) + ' | ' + - this._formatTime( - (data.total - data.loaded) * 8 / data.bitrate - ) + ' | ' + - this._formatPercentage( - data.loaded / data.total - ) + ' | ' + - this._formatFileSize(data.loaded) + ' / ' + - this._formatFileSize(data.total); - }, - - _renderTemplate: function (func, files) { - if (!func) { - return $(); - } - var result = func({ - files: files, - formatFileSize: this._formatFileSize, - options: this.options - }); - if (result instanceof $) { - return result; - } - return $(this.options.templatesContainer).html(result).children(); - }, - - _renderPreviews: function (data) { - data.context.find('.preview').each(function (index, elm) { - $(elm).append(data.files[index].preview); - }); - }, - - _renderUpload: function (files) { - return this._renderTemplate( - this.options.uploadTemplate, - files - ); - }, - - _renderDownload: function (files) { - return this._renderTemplate( - this.options.downloadTemplate, - files - ).find('a[download]').each(this._enableDragToDesktop).end(); - }, - - _startHandler: function (e) { - e.preventDefault(); - var button = $(e.currentTarget), - template = button.closest('.template-upload'), - data = template.data('data'); - button.prop('disabled', true); - if (data && data.submit) { - data.submit(); - } - }, - - _cancelHandler: function (e) { - e.preventDefault(); - var template = $(e.currentTarget) - .closest('.template-upload,.template-download'), - data = template.data('data') || {}; - data.context = data.context || template; - if (data.abort) { - data.abort(); - } else { - data.errorThrown = 'abort'; - this._trigger('fail', e, data); - } - }, - - _deleteHandler: function (e) { - e.preventDefault(); - var button = $(e.currentTarget); - this._trigger('destroy', e, $.extend({ - context: button.closest('.template-download'), - type: 'DELETE' - }, button.data())); - }, - - _forceReflow: function (node) { - return $.support.transition && node.length && - node[0].offsetWidth; - }, - - _transition: function (node) { - var dfd = $.Deferred(); - if ($.support.transition && node.hasClass('fade') && node.is(':visible')) { - node.bind( - $.support.transition.end, - function (e) { - // Make sure we don't respond to other transitions events - // in the container element, e.g. from button elements: - if (e.target === node[0]) { - node.unbind($.support.transition.end); - dfd.resolveWith(node); - } - } - ).toggleClass('in'); - } else { - node.toggleClass('in'); - dfd.resolveWith(node); - } - return dfd; - }, - - _initButtonBarEventHandlers: function () { - var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'), - filesList = this.options.filesContainer; - this._on(fileUploadButtonBar.find('.start'), { - click: function (e) { - e.preventDefault(); - filesList.find('.start').click(); - } - }); - this._on(fileUploadButtonBar.find('.cancel'), { - click: function (e) { - e.preventDefault(); - filesList.find('.cancel').click(); - } - }); - this._on(fileUploadButtonBar.find('.delete'), { - click: function (e) { - e.preventDefault(); - filesList.find('.toggle:checked') - .closest('.template-download') - .find('.delete').click(); - fileUploadButtonBar.find('.toggle') - .prop('checked', false); - } - }); - this._on(fileUploadButtonBar.find('.toggle'), { - change: function (e) { - filesList.find('.toggle').prop( - 'checked', - $(e.currentTarget).is(':checked') - ); - } - }); - }, - - _destroyButtonBarEventHandlers: function () { - this._off( - this.element.find('.fileupload-buttonbar') - .find('.start, .cancel, .delete'), - 'click' - ); - this._off( - this.element.find('.fileupload-buttonbar .toggle'), - 'change.' - ); - }, - - _initEventHandlers: function () { - this._super(); - this._on(this.options.filesContainer, { - 'click .start': this._startHandler, - 'click .cancel': this._cancelHandler, - 'click .delete': this._deleteHandler - }); - this._initButtonBarEventHandlers(); - }, - - _destroyEventHandlers: function () { - this._destroyButtonBarEventHandlers(); - this._off(this.options.filesContainer, 'click'); - this._super(); - }, - - _enableFileInputButton: function () { - this.element.find('.fileinput-button input') - .prop('disabled', false) - .parent().removeClass('disabled'); - }, - - _disableFileInputButton: function () { - this.element.find('.fileinput-button input') - .prop('disabled', true) - .parent().addClass('disabled'); - }, - - _initTemplates: function () { - var options = this.options; - options.templatesContainer = this.document[0].createElement( - options.filesContainer.prop('nodeName') - ); - if (tmpl) { - if (options.uploadTemplateId) { - options.uploadTemplate = tmpl(options.uploadTemplateId); - } - if (options.downloadTemplateId) { - options.downloadTemplate = tmpl(options.downloadTemplateId); - } - } - }, - - _initFilesContainer: function () { - var options = this.options; - if (options.filesContainer === undefined) { - options.filesContainer = this.element.find('.files'); - } else if (!(options.filesContainer instanceof $)) { - options.filesContainer = $(options.filesContainer); - } - }, - - _initSpecialOptions: function () { - this._super(); - this._initFilesContainer(); - this._initTemplates(); - }, - - _create: function () { - this._super(); - this._resetFinishedDeferreds(); - if (!$.support.fileInput) { - this._disableFileInputButton(); - } - }, - - enable: function () { - var wasDisabled = false; - if (this.options.disabled) { - wasDisabled = true; - } - this._super(); - if (wasDisabled) { - this.element.find('input, button').prop('disabled', false); - this._enableFileInputButton(); - } - }, - - disable: function () { - if (!this.options.disabled) { - this.element.find('input, button').prop('disabled', true); - this._disableFileInputButton(); - } - this._super(); - } - - }); - -})); diff --git a/artbox-file/assets/js/jquery.fileupload-validate.js b/artbox-file/assets/js/jquery.fileupload-validate.js deleted file mode 100755 index b771386..0000000 --- a/artbox-file/assets/js/jquery.fileupload-validate.js +++ /dev/null @@ -1,122 +0,0 @@ -/* - * jQuery File Upload Validation Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2013, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* global define, require, window */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define([ - 'jquery', - './jquery.fileupload-process' - ], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory(require('jquery')); - } else { - // Browser globals: - factory( - window.jQuery - ); - } -}(function ($) { - 'use strict'; - - // Append to the default processQueue: - $.blueimp.fileupload.prototype.options.processQueue.push( - { - action: 'validate', - // Always trigger this action, - // even if the previous action was rejected: - always: true, - // Options taken from the global options map: - acceptFileTypes: '@', - maxFileSize: '@', - minFileSize: '@', - maxNumberOfFiles: '@', - disabled: '@disableValidation' - } - ); - - // The File Upload Validation plugin extends the fileupload widget - // with file validation functionality: - $.widget('blueimp.fileupload', $.blueimp.fileupload, { - - options: { - /* - // The regular expression for allowed file types, matches - // against either file type or file name: - acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, - // The maximum allowed file size in bytes: - maxFileSize: 10000000, // 10 MB - // The minimum allowed file size in bytes: - minFileSize: undefined, // No minimal file size - // The limit of files to be uploaded: - maxNumberOfFiles: 10, - */ - - // Function returning the current number of files, - // has to be overriden for maxNumberOfFiles validation: - getNumberOfFiles: $.noop, - - // Error and info messages: - messages: { - maxNumberOfFiles: 'Maximum number of files exceeded', - acceptFileTypes: 'File type not allowed', - maxFileSize: 'File is too large', - minFileSize: 'File is too small' - } - }, - - processActions: { - - validate: function (data, options) { - if (options.disabled) { - return data; - } - var dfd = $.Deferred(), - settings = this.options, - file = data.files[data.index], - fileSize; - if (options.minFileSize || options.maxFileSize) { - fileSize = file.size; - } - if ($.type(options.maxNumberOfFiles) === 'number' && - (settings.getNumberOfFiles() || 0) + data.files.length > - options.maxNumberOfFiles) { - file.error = settings.i18n('maxNumberOfFiles'); - } else if (options.acceptFileTypes && - !(options.acceptFileTypes.test(file.type) || - options.acceptFileTypes.test(file.name))) { - file.error = settings.i18n('acceptFileTypes'); - } else if (fileSize > options.maxFileSize) { - file.error = settings.i18n('maxFileSize'); - } else if ($.type(fileSize) === 'number' && - fileSize < options.minFileSize) { - file.error = settings.i18n('minFileSize'); - } else { - delete file.error; - } - if (file.error || data.files.error) { - data.files.error = true; - dfd.rejectWith(this, [data]); - } else { - dfd.resolveWith(this, [data]); - } - return dfd.promise(); - } - - } - - }); - -})); diff --git a/artbox-file/assets/js/jquery.fileupload-video.js b/artbox-file/assets/js/jquery.fileupload-video.js deleted file mode 100755 index 1416e26..0000000 --- a/artbox-file/assets/js/jquery.fileupload-video.js +++ /dev/null @@ -1,112 +0,0 @@ -/* - * jQuery File Upload Video Preview Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2013, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* jshint nomen:false */ -/* global define, require, window, document */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define([ - 'jquery', - 'load-image', - './jquery.fileupload-process' - ], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory( - require('jquery'), - require('load-image') - ); - } else { - // Browser globals: - factory( - window.jQuery, - window.loadImage - ); - } -}(function ($, loadImage) { - 'use strict'; - - // Prepend to the default processQueue: - $.blueimp.fileupload.prototype.options.processQueue.unshift( - { - action: 'loadVideo', - // Use the action as prefix for the "@" options: - prefix: true, - fileTypes: '@', - maxFileSize: '@', - disabled: '@disableVideoPreview' - }, - { - action: 'setVideo', - name: '@videoPreviewName', - disabled: '@disableVideoPreview' - } - ); - - // The File Upload Video Preview plugin extends the fileupload widget - // with video preview functionality: - $.widget('blueimp.fileupload', $.blueimp.fileupload, { - - options: { - // The regular expression for the types of video files to load, - // matched against the file type: - loadVideoFileTypes: /^video\/.*$/ - }, - - _videoElement: document.createElement('video'), - - processActions: { - - // Loads the video file given via data.files and data.index - // as video element if the browser supports playing it. - // Accepts the options fileTypes (regular expression) - // and maxFileSize (integer) to limit the files to load: - loadVideo: function (data, options) { - if (options.disabled) { - return data; - } - var file = data.files[data.index], - url, - video; - if (this._videoElement.canPlayType && - this._videoElement.canPlayType(file.type) && - ($.type(options.maxFileSize) !== 'number' || - file.size <= options.maxFileSize) && - (!options.fileTypes || - options.fileTypes.test(file.type))) { - url = loadImage.createObjectURL(file); - if (url) { - video = this._videoElement.cloneNode(false); - video.src = url; - video.controls = true; - data.video = video; - return data; - } - } - return data; - }, - - // Sets the video element as a property of the file object: - setVideo: function (data, options) { - if (data.video && !options.disabled) { - data.files[data.index][options.name || 'preview'] = data.video; - } - return data; - } - - } - - }); - -})); diff --git a/artbox-file/assets/js/jquery.fileupload.js b/artbox-file/assets/js/jquery.fileupload.js deleted file mode 100755 index 68d1c4a..0000000 --- a/artbox-file/assets/js/jquery.fileupload.js +++ /dev/null @@ -1,1477 +0,0 @@ -/* - * jQuery File Upload Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2010, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* jshint nomen:false */ -/* global define, require, window, document, location, Blob, FormData */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define([ - 'jquery', - 'jquery.ui.widget' - ], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory( - require('jquery'), - require('./vendor/jquery.ui.widget') - ); - } else { - // Browser globals: - factory(window.jQuery); - } -}(function ($) { - 'use strict'; - - // Detect file input support, based on - // http://viljamis.com/blog/2012/file-upload-support-on-mobile/ - $.support.fileInput = !(new RegExp( - // Handle devices which give false positives for the feature detection: - '(Android (1\\.[0156]|2\\.[01]))' + - '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' + - '|(w(eb)?OSBrowser)|(webOS)' + - '|(Kindle/(1\\.0|2\\.[05]|3\\.0))' - ).test(window.navigator.userAgent) || - // Feature detection for all other devices: - $('').prop('disabled')); - - // The FileReader API is not actually used, but works as feature detection, - // as some Safari versions (5?) support XHR file uploads via the FormData API, - // but not non-multipart XHR file uploads. - // window.XMLHttpRequestUpload is not available on IE10, so we check for - // window.ProgressEvent instead to detect XHR2 file upload capability: - $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader); - $.support.xhrFormDataFileUpload = !!window.FormData; - - // Detect support for Blob slicing (required for chunked uploads): - $.support.blobSlice = window.Blob && (Blob.prototype.slice || - Blob.prototype.webkitSlice || Blob.prototype.mozSlice); - - // Helper function to create drag handlers for dragover/dragenter/dragleave: - function getDragHandler(type) { - var isDragOver = type === 'dragover'; - return function (e) { - e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; - var dataTransfer = e.dataTransfer; - if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 && - this._trigger( - type, - $.Event(type, {delegatedEvent: e}) - ) !== false) { - e.preventDefault(); - if (isDragOver) { - dataTransfer.dropEffect = 'copy'; - } - } - }; - } - - // The fileupload widget listens for change events on file input fields defined - // via fileInput setting and paste or drop events of the given dropZone. - // In addition to the default jQuery Widget methods, the fileupload widget - // exposes the "add" and "send" methods, to add or directly send files using - // the fileupload API. - // By default, files added via file input selection, paste, drag & drop or - // "add" method are uploaded immediately, but it is possible to override - // the "add" callback option to queue file uploads. - $.widget('blueimp.fileupload', { - - options: { - // The drop target element(s), by the default the complete document. - // Set to null to disable drag & drop support: - dropZone: $(document), - // The paste target element(s), by the default undefined. - // Set to a DOM node or jQuery object to enable file pasting: - pasteZone: undefined, - // The file input field(s), that are listened to for change events. - // If undefined, it is set to the file input fields inside - // of the widget element on plugin initialization. - // Set to null to disable the change listener. - fileInput: undefined, - // By default, the file input field is replaced with a clone after - // each input field change event. This is required for iframe transport - // queues and allows change events to be fired for the same file - // selection, but can be disabled by setting the following option to false: - replaceFileInput: true, - // The parameter name for the file form data (the request argument name). - // If undefined or empty, the name property of the file input field is - // used, or "files[]" if the file input name property is also empty, - // can be a string or an array of strings: - paramName: undefined, - // By default, each file of a selection is uploaded using an individual - // request for XHR type uploads. Set to false to upload file - // selections in one request each: - singleFileUploads: true, - // To limit the number of files uploaded with one XHR request, - // set the following option to an integer greater than 0: - limitMultiFileUploads: undefined, - // The following option limits the number of files uploaded with one - // XHR request to keep the request size under or equal to the defined - // limit in bytes: - limitMultiFileUploadSize: undefined, - // Multipart file uploads add a number of bytes to each uploaded file, - // therefore the following option adds an overhead for each file used - // in the limitMultiFileUploadSize configuration: - limitMultiFileUploadSizeOverhead: 512, - // Set the following option to true to issue all file upload requests - // in a sequential order: - sequentialUploads: false, - // To limit the number of concurrent uploads, - // set the following option to an integer greater than 0: - limitConcurrentUploads: undefined, - // Set the following option to true to force iframe transport uploads: - forceIframeTransport: false, - // Set the following option to the location of a redirect url on the - // origin server, for cross-domain iframe transport uploads: - redirect: undefined, - // The parameter name for the redirect url, sent as part of the form - // data and set to 'redirect' if this option is empty: - redirectParamName: undefined, - // Set the following option to the location of a postMessage window, - // to enable postMessage transport uploads: - postMessage: undefined, - // By default, XHR file uploads are sent as multipart/form-data. - // The iframe transport is always using multipart/form-data. - // Set to false to enable non-multipart XHR uploads: - multipart: true, - // To upload large files in smaller chunks, set the following option - // to a preferred maximum chunk size. If set to 0, null or undefined, - // or the browser does not support the required Blob API, files will - // be uploaded as a whole. - maxChunkSize: undefined, - // When a non-multipart upload or a chunked multipart upload has been - // aborted, this option can be used to resume the upload by setting - // it to the size of the already uploaded bytes. This option is most - // useful when modifying the options object inside of the "add" or - // "send" callbacks, as the options are cloned for each file upload. - uploadedBytes: undefined, - // By default, failed (abort or error) file uploads are removed from the - // global progress calculation. Set the following option to false to - // prevent recalculating the global progress data: - recalculateProgress: true, - // Interval in milliseconds to calculate and trigger progress events: - progressInterval: 100, - // Interval in milliseconds to calculate progress bitrate: - bitrateInterval: 500, - // By default, uploads are started automatically when adding files: - autoUpload: true, - - // Error and info messages: - messages: { - uploadedBytes: 'Uploaded bytes exceed file size' - }, - - // Translation function, gets the message key to be translated - // and an object with context specific data as arguments: - i18n: function (message, context) { - message = this.messages[message] || message.toString(); - if (context) { - $.each(context, function (key, value) { - message = message.replace('{' + key + '}', value); - }); - } - return message; - }, - - // Additional form data to be sent along with the file uploads can be set - // using this option, which accepts an array of objects with name and - // value properties, a function returning such an array, a FormData - // object (for XHR file uploads), or a simple object. - // The form of the first fileInput is given as parameter to the function: - formData: function (form) { - return form.serializeArray(); - }, - - // The add callback is invoked as soon as files are added to the fileupload - // widget (via file input selection, drag & drop, paste or add API call). - // If the singleFileUploads option is enabled, this callback will be - // called once for each file in the selection for XHR file uploads, else - // once for each file selection. - // - // The upload starts when the submit method is invoked on the data parameter. - // The data object contains a files property holding the added files - // and allows you to override plugin options as well as define ajax settings. - // - // Listeners for this callback can also be bound the following way: - // .bind('fileuploadadd', func); - // - // data.submit() returns a Promise object and allows to attach additional - // handlers using jQuery's Deferred callbacks: - // data.submit().done(func).fail(func).always(func); - add: function (e, data) { - if (e.isDefaultPrevented()) { - return false; - } - if (data.autoUpload || (data.autoUpload !== false && - $(this).fileupload('option', 'autoUpload'))) { - data.process().done(function () { - data.submit(); - }); - } - }, - - // Other callbacks: - - // Callback for the submit event of each file upload: - // submit: function (e, data) {}, // .bind('fileuploadsubmit', func); - - // Callback for the start of each file upload request: - // send: function (e, data) {}, // .bind('fileuploadsend', func); - - // Callback for successful uploads: - // done: function (e, data) {}, // .bind('fileuploaddone', func); - - // Callback for failed (abort or error) uploads: - // fail: function (e, data) {}, // .bind('fileuploadfail', func); - - // Callback for completed (success, abort or error) requests: - // always: function (e, data) {}, // .bind('fileuploadalways', func); - - // Callback for upload progress events: - // progress: function (e, data) {}, // .bind('fileuploadprogress', func); - - // Callback for global upload progress events: - // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func); - - // Callback for uploads start, equivalent to the global ajaxStart event: - // start: function (e) {}, // .bind('fileuploadstart', func); - - // Callback for uploads stop, equivalent to the global ajaxStop event: - // stop: function (e) {}, // .bind('fileuploadstop', func); - - // Callback for change events of the fileInput(s): - // change: function (e, data) {}, // .bind('fileuploadchange', func); - - // Callback for paste events to the pasteZone(s): - // paste: function (e, data) {}, // .bind('fileuploadpaste', func); - - // Callback for drop events of the dropZone(s): - // drop: function (e, data) {}, // .bind('fileuploaddrop', func); - - // Callback for dragover events of the dropZone(s): - // dragover: function (e) {}, // .bind('fileuploaddragover', func); - - // Callback for the start of each chunk upload request: - // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func); - - // Callback for successful chunk uploads: - // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func); - - // Callback for failed (abort or error) chunk uploads: - // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func); - - // Callback for completed (success, abort or error) chunk upload requests: - // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func); - - // The plugin options are used as settings object for the ajax calls. - // The following are jQuery ajax settings required for the file uploads: - processData: false, - contentType: false, - cache: false, - timeout: 0 - }, - - // A list of options that require reinitializing event listeners and/or - // special initialization code: - _specialOptions: [ - 'fileInput', - 'dropZone', - 'pasteZone', - 'multipart', - 'forceIframeTransport' - ], - - _blobSlice: $.support.blobSlice && function () { - var slice = this.slice || this.webkitSlice || this.mozSlice; - return slice.apply(this, arguments); - }, - - _BitrateTimer: function () { - this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime()); - this.loaded = 0; - this.bitrate = 0; - this.getBitrate = function (now, loaded, interval) { - var timeDiff = now - this.timestamp; - if (!this.bitrate || !interval || timeDiff > interval) { - this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8; - this.loaded = loaded; - this.timestamp = now; - } - return this.bitrate; - }; - }, - - _isXHRUpload: function (options) { - return !options.forceIframeTransport && - ((!options.multipart && $.support.xhrFileUpload) || - $.support.xhrFormDataFileUpload); - }, - - _getFormData: function (options) { - var formData; - if ($.type(options.formData) === 'function') { - return options.formData(options.form); - } - if ($.isArray(options.formData)) { - return options.formData; - } - if ($.type(options.formData) === 'object') { - formData = []; - $.each(options.formData, function (name, value) { - formData.push({name: name, value: value}); - }); - return formData; - } - return []; - }, - - _getTotal: function (files) { - var total = 0; - $.each(files, function (index, file) { - total += file.size || 1; - }); - return total; - }, - - _initProgressObject: function (obj) { - var progress = { - loaded: 0, - total: 0, - bitrate: 0 - }; - if (obj._progress) { - $.extend(obj._progress, progress); - } else { - obj._progress = progress; - } - }, - - _initResponseObject: function (obj) { - var prop; - if (obj._response) { - for (prop in obj._response) { - if (obj._response.hasOwnProperty(prop)) { - delete obj._response[prop]; - } - } - } else { - obj._response = {}; - } - }, - - _onProgress: function (e, data) { - if (e.lengthComputable) { - var now = ((Date.now) ? Date.now() : (new Date()).getTime()), - loaded; - if (data._time && data.progressInterval && - (now - data._time < data.progressInterval) && - e.loaded !== e.total) { - return; - } - data._time = now; - loaded = Math.floor( - e.loaded / e.total * (data.chunkSize || data._progress.total) - ) + (data.uploadedBytes || 0); - // Add the difference from the previously loaded state - // to the global loaded counter: - this._progress.loaded += (loaded - data._progress.loaded); - this._progress.bitrate = this._bitrateTimer.getBitrate( - now, - this._progress.loaded, - data.bitrateInterval - ); - data._progress.loaded = data.loaded = loaded; - data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate( - now, - loaded, - data.bitrateInterval - ); - // Trigger a custom progress event with a total data property set - // to the file size(s) of the current upload and a loaded data - // property calculated accordingly: - this._trigger( - 'progress', - $.Event('progress', {delegatedEvent: e}), - data - ); - // Trigger a global progress event for all current file uploads, - // including ajax calls queued for sequential file uploads: - this._trigger( - 'progressall', - $.Event('progressall', {delegatedEvent: e}), - this._progress - ); - } - }, - - _initProgressListener: function (options) { - var that = this, - xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr(); - // Accesss to the native XHR object is required to add event listeners - // for the upload progress event: - if (xhr.upload) { - $(xhr.upload).bind('progress', function (e) { - var oe = e.originalEvent; - // Make sure the progress event properties get copied over: - e.lengthComputable = oe.lengthComputable; - e.loaded = oe.loaded; - e.total = oe.total; - that._onProgress(e, options); - }); - options.xhr = function () { - return xhr; - }; - } - }, - - _isInstanceOf: function (type, obj) { - // Cross-frame instanceof check - return Object.prototype.toString.call(obj) === '[object ' + type + ']'; - }, - - _initXHRData: function (options) { - var that = this, - formData, - file = options.files[0], - // Ignore non-multipart setting if not supported: - multipart = options.multipart || !$.support.xhrFileUpload, - paramName = $.type(options.paramName) === 'array' ? - options.paramName[0] : options.paramName; - options.headers = $.extend({}, options.headers); - if (options.contentRange) { - options.headers['Content-Range'] = options.contentRange; - } - if (!multipart || options.blob || !this._isInstanceOf('File', file)) { - options.headers['Content-Disposition'] = 'attachment; filename="' + - encodeURI(file.name) + '"'; - } - if (!multipart) { - options.contentType = file.type || 'application/octet-stream'; - options.data = options.blob || file; - } else if ($.support.xhrFormDataFileUpload) { - if (options.postMessage) { - // window.postMessage does not allow sending FormData - // objects, so we just add the File/Blob objects to - // the formData array and let the postMessage window - // create the FormData object out of this array: - formData = this._getFormData(options); - if (options.blob) { - formData.push({ - name: paramName, - value: options.blob - }); - } else { - $.each(options.files, function (index, file) { - formData.push({ - name: ($.type(options.paramName) === 'array' && - options.paramName[index]) || paramName, - value: file - }); - }); - } - } else { - if (that._isInstanceOf('FormData', options.formData)) { - formData = options.formData; - } else { - formData = new FormData(); - $.each(this._getFormData(options), function (index, field) { - formData.append(field.name, field.value); - }); - } - if (options.blob) { - formData.append(paramName, options.blob, file.name); - } else { - $.each(options.files, function (index, file) { - // This check allows the tests to run with - // dummy objects: - if (that._isInstanceOf('File', file) || - that._isInstanceOf('Blob', file)) { - formData.append( - ($.type(options.paramName) === 'array' && - options.paramName[index]) || paramName, - file, - file.uploadName || file.name - ); - } - }); - } - } - options.data = formData; - } - // Blob reference is not needed anymore, free memory: - options.blob = null; - }, - - _initIframeSettings: function (options) { - var targetHost = $('').prop('href', options.url).prop('host'); - // Setting the dataType to iframe enables the iframe transport: - options.dataType = 'iframe ' + (options.dataType || ''); - // The iframe transport accepts a serialized array as form data: - options.formData = this._getFormData(options); - // Add redirect url to form data on cross-domain uploads: - if (options.redirect && targetHost && targetHost !== location.host) { - options.formData.push({ - name: options.redirectParamName || 'redirect', - value: options.redirect - }); - } - }, - - _initDataSettings: function (options) { - if (this._isXHRUpload(options)) { - if (!this._chunkedUpload(options, true)) { - if (!options.data) { - this._initXHRData(options); - } - this._initProgressListener(options); - } - if (options.postMessage) { - // Setting the dataType to postmessage enables the - // postMessage transport: - options.dataType = 'postmessage ' + (options.dataType || ''); - } - } else { - this._initIframeSettings(options); - } - }, - - _getParamName: function (options) { - var fileInput = $(options.fileInput), - paramName = options.paramName; - if (!paramName) { - paramName = []; - fileInput.each(function () { - var input = $(this), - name = input.prop('name') || 'files[]', - i = (input.prop('files') || [1]).length; - while (i) { - paramName.push(name); - i -= 1; - } - }); - if (!paramName.length) { - paramName = [fileInput.prop('name') || 'files[]']; - } - } else if (!$.isArray(paramName)) { - paramName = [paramName]; - } - return paramName; - }, - - _initFormSettings: function (options) { - // Retrieve missing options from the input field and the - // associated form, if available: - if (!options.form || !options.form.length) { - options.form = $(options.fileInput.prop('form')); - // If the given file input doesn't have an associated form, - // use the default widget file input's form: - if (!options.form.length) { - options.form = $(this.options.fileInput.prop('form')); - } - } - options.paramName = this._getParamName(options); - if (!options.url) { - options.url = options.form.prop('action') || location.href; - } - // The HTTP request method must be "POST" or "PUT": - options.type = (options.type || - ($.type(options.form.prop('method')) === 'string' && - options.form.prop('method')) || '' - ).toUpperCase(); - if (options.type !== 'POST' && options.type !== 'PUT' && - options.type !== 'PATCH') { - options.type = 'POST'; - } - if (!options.formAcceptCharset) { - options.formAcceptCharset = options.form.attr('accept-charset'); - } - }, - - _getAJAXSettings: function (data) { - var options = $.extend({}, this.options, data); - this._initFormSettings(options); - this._initDataSettings(options); - return options; - }, - - // jQuery 1.6 doesn't provide .state(), - // while jQuery 1.8+ removed .isRejected() and .isResolved(): - _getDeferredState: function (deferred) { - if (deferred.state) { - return deferred.state(); - } - if (deferred.isResolved()) { - return 'resolved'; - } - if (deferred.isRejected()) { - return 'rejected'; - } - return 'pending'; - }, - - // Maps jqXHR callbacks to the equivalent - // methods of the given Promise object: - _enhancePromise: function (promise) { - promise.success = promise.done; - promise.error = promise.fail; - promise.complete = promise.always; - return promise; - }, - - // Creates and returns a Promise object enhanced with - // the jqXHR methods abort, success, error and complete: - _getXHRPromise: function (resolveOrReject, context, args) { - var dfd = $.Deferred(), - promise = dfd.promise(); - context = context || this.options.context || promise; - if (resolveOrReject === true) { - dfd.resolveWith(context, args); - } else if (resolveOrReject === false) { - dfd.rejectWith(context, args); - } - promise.abort = dfd.promise; - return this._enhancePromise(promise); - }, - - // Adds convenience methods to the data callback argument: - _addConvenienceMethods: function (e, data) { - var that = this, - getPromise = function (args) { - return $.Deferred().resolveWith(that, args).promise(); - }; - data.process = function (resolveFunc, rejectFunc) { - if (resolveFunc || rejectFunc) { - data._processQueue = this._processQueue = - (this._processQueue || getPromise([this])).pipe( - function () { - if (data.errorThrown) { - return $.Deferred() - .rejectWith(that, [data]).promise(); - } - return getPromise(arguments); - } - ).pipe(resolveFunc, rejectFunc); - } - return this._processQueue || getPromise([this]); - }; - data.submit = function () { - if (this.state() !== 'pending') { - data.jqXHR = this.jqXHR = - (that._trigger( - 'submit', - $.Event('submit', {delegatedEvent: e}), - this - ) !== false) && that._onSend(e, this); - } - return this.jqXHR || that._getXHRPromise(); - }; - data.abort = function () { - if (this.jqXHR) { - return this.jqXHR.abort(); - } - this.errorThrown = 'abort'; - that._trigger('fail', null, this); - return that._getXHRPromise(false); - }; - data.state = function () { - if (this.jqXHR) { - return that._getDeferredState(this.jqXHR); - } - if (this._processQueue) { - return that._getDeferredState(this._processQueue); - } - }; - data.processing = function () { - return !this.jqXHR && this._processQueue && that - ._getDeferredState(this._processQueue) === 'pending'; - }; - data.progress = function () { - return this._progress; - }; - data.response = function () { - return this._response; - }; - }, - - // Parses the Range header from the server response - // and returns the uploaded bytes: - _getUploadedBytes: function (jqXHR) { - var range = jqXHR.getResponseHeader('Range'), - parts = range && range.split('-'), - upperBytesPos = parts && parts.length > 1 && - parseInt(parts[1], 10); - return upperBytesPos && upperBytesPos + 1; - }, - - // Uploads a file in multiple, sequential requests - // by splitting the file up in multiple blob chunks. - // If the second parameter is true, only tests if the file - // should be uploaded in chunks, but does not invoke any - // upload requests: - _chunkedUpload: function (options, testOnly) { - options.uploadedBytes = options.uploadedBytes || 0; - var that = this, - file = options.files[0], - fs = file.size, - ub = options.uploadedBytes, - mcs = options.maxChunkSize || fs, - slice = this._blobSlice, - dfd = $.Deferred(), - promise = dfd.promise(), - jqXHR, - upload; - if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) || - options.data) { - return false; - } - if (testOnly) { - return true; - } - if (ub >= fs) { - file.error = options.i18n('uploadedBytes'); - return this._getXHRPromise( - false, - options.context, - [null, 'error', file.error] - ); - } - // The chunk upload method: - upload = function () { - // Clone the options object for each chunk upload: - var o = $.extend({}, options), - currentLoaded = o._progress.loaded; - o.blob = slice.call( - file, - ub, - ub + mcs, - file.type - ); - // Store the current chunk size, as the blob itself - // will be dereferenced after data processing: - o.chunkSize = o.blob.size; - // Expose the chunk bytes position range: - o.contentRange = 'bytes ' + ub + '-' + - (ub + o.chunkSize - 1) + '/' + fs; - // Process the upload data (the blob and potential form data): - that._initXHRData(o); - // Add progress listeners for this chunk upload: - that._initProgressListener(o); - jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) || - that._getXHRPromise(false, o.context)) - .done(function (result, textStatus, jqXHR) { - ub = that._getUploadedBytes(jqXHR) || - (ub + o.chunkSize); - // Create a progress event if no final progress event - // with loaded equaling total has been triggered - // for this chunk: - if (currentLoaded + o.chunkSize - o._progress.loaded) { - that._onProgress($.Event('progress', { - lengthComputable: true, - loaded: ub - o.uploadedBytes, - total: ub - o.uploadedBytes - }), o); - } - options.uploadedBytes = o.uploadedBytes = ub; - o.result = result; - o.textStatus = textStatus; - o.jqXHR = jqXHR; - that._trigger('chunkdone', null, o); - that._trigger('chunkalways', null, o); - if (ub < fs) { - // File upload not yet complete, - // continue with the next chunk: - upload(); - } else { - dfd.resolveWith( - o.context, - [result, textStatus, jqXHR] - ); - } - }) - .fail(function (jqXHR, textStatus, errorThrown) { - o.jqXHR = jqXHR; - o.textStatus = textStatus; - o.errorThrown = errorThrown; - that._trigger('chunkfail', null, o); - that._trigger('chunkalways', null, o); - dfd.rejectWith( - o.context, - [jqXHR, textStatus, errorThrown] - ); - }); - }; - this._enhancePromise(promise); - promise.abort = function () { - return jqXHR.abort(); - }; - upload(); - return promise; - }, - - _beforeSend: function (e, data) { - if (this._active === 0) { - // the start callback is triggered when an upload starts - // and no other uploads are currently running, - // equivalent to the global ajaxStart event: - this._trigger('start'); - // Set timer for global bitrate progress calculation: - this._bitrateTimer = new this._BitrateTimer(); - // Reset the global progress values: - this._progress.loaded = this._progress.total = 0; - this._progress.bitrate = 0; - } - // Make sure the container objects for the .response() and - // .progress() methods on the data object are available - // and reset to their initial state: - this._initResponseObject(data); - this._initProgressObject(data); - data._progress.loaded = data.loaded = data.uploadedBytes || 0; - data._progress.total = data.total = this._getTotal(data.files) || 1; - data._progress.bitrate = data.bitrate = 0; - this._active += 1; - // Initialize the global progress values: - this._progress.loaded += data.loaded; - this._progress.total += data.total; - }, - - _onDone: function (result, textStatus, jqXHR, options) { - var total = options._progress.total, - response = options._response; - if (options._progress.loaded < total) { - // Create a progress event if no final progress event - // with loaded equaling total has been triggered: - this._onProgress($.Event('progress', { - lengthComputable: true, - loaded: total, - total: total - }), options); - } - response.result = options.result = result; - response.textStatus = options.textStatus = textStatus; - response.jqXHR = options.jqXHR = jqXHR; - this._trigger('done', null, options); - }, - - _onFail: function (jqXHR, textStatus, errorThrown, options) { - var response = options._response; - if (options.recalculateProgress) { - // Remove the failed (error or abort) file upload from - // the global progress calculation: - this._progress.loaded -= options._progress.loaded; - this._progress.total -= options._progress.total; - } - response.jqXHR = options.jqXHR = jqXHR; - response.textStatus = options.textStatus = textStatus; - response.errorThrown = options.errorThrown = errorThrown; - this._trigger('fail', null, options); - }, - - _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) { - // jqXHRorResult, textStatus and jqXHRorError are added to the - // options object via done and fail callbacks - this._trigger('always', null, options); - }, - - _onSend: function (e, data) { - if (!data.submit) { - this._addConvenienceMethods(e, data); - } - var that = this, - jqXHR, - aborted, - slot, - pipe, - options = that._getAJAXSettings(data), - send = function () { - that._sending += 1; - // Set timer for bitrate progress calculation: - options._bitrateTimer = new that._BitrateTimer(); - jqXHR = jqXHR || ( - ((aborted || that._trigger( - 'send', - $.Event('send', {delegatedEvent: e}), - options - ) === false) && - that._getXHRPromise(false, options.context, aborted)) || - that._chunkedUpload(options) || $.ajax(options) - ).done(function (result, textStatus, jqXHR) { - that._onDone(result, textStatus, jqXHR, options); - }).fail(function (jqXHR, textStatus, errorThrown) { - that._onFail(jqXHR, textStatus, errorThrown, options); - }).always(function (jqXHRorResult, textStatus, jqXHRorError) { - that._onAlways( - jqXHRorResult, - textStatus, - jqXHRorError, - options - ); - that._sending -= 1; - that._active -= 1; - if (options.limitConcurrentUploads && - options.limitConcurrentUploads > that._sending) { - // Start the next queued upload, - // that has not been aborted: - var nextSlot = that._slots.shift(); - while (nextSlot) { - if (that._getDeferredState(nextSlot) === 'pending') { - nextSlot.resolve(); - break; - } - nextSlot = that._slots.shift(); - } - } - if (that._active === 0) { - // The stop callback is triggered when all uploads have - // been completed, equivalent to the global ajaxStop event: - that._trigger('stop'); - } - }); - return jqXHR; - }; - this._beforeSend(e, options); - if (this.options.sequentialUploads || - (this.options.limitConcurrentUploads && - this.options.limitConcurrentUploads <= this._sending)) { - if (this.options.limitConcurrentUploads > 1) { - slot = $.Deferred(); - this._slots.push(slot); - pipe = slot.pipe(send); - } else { - this._sequence = this._sequence.pipe(send, send); - pipe = this._sequence; - } - // Return the piped Promise object, enhanced with an abort method, - // which is delegated to the jqXHR object of the current upload, - // and jqXHR callbacks mapped to the equivalent Promise methods: - pipe.abort = function () { - aborted = [undefined, 'abort', 'abort']; - if (!jqXHR) { - if (slot) { - slot.rejectWith(options.context, aborted); - } - return send(); - } - return jqXHR.abort(); - }; - return this._enhancePromise(pipe); - } - return send(); - }, - - _onAdd: function (e, data) { - var that = this, - result = true, - options = $.extend({}, this.options, data), - files = data.files, - filesLength = files.length, - limit = options.limitMultiFileUploads, - limitSize = options.limitMultiFileUploadSize, - overhead = options.limitMultiFileUploadSizeOverhead, - batchSize = 0, - paramName = this._getParamName(options), - paramNameSet, - paramNameSlice, - fileSet, - i, - j = 0; - if (!filesLength) { - return false; - } - if (limitSize && files[0].size === undefined) { - limitSize = undefined; - } - if (!(options.singleFileUploads || limit || limitSize) || - !this._isXHRUpload(options)) { - fileSet = [files]; - paramNameSet = [paramName]; - } else if (!(options.singleFileUploads || limitSize) && limit) { - fileSet = []; - paramNameSet = []; - for (i = 0; i < filesLength; i += limit) { - fileSet.push(files.slice(i, i + limit)); - paramNameSlice = paramName.slice(i, i + limit); - if (!paramNameSlice.length) { - paramNameSlice = paramName; - } - paramNameSet.push(paramNameSlice); - } - } else if (!options.singleFileUploads && limitSize) { - fileSet = []; - paramNameSet = []; - for (i = 0; i < filesLength; i = i + 1) { - batchSize += files[i].size + overhead; - if (i + 1 === filesLength || - ((batchSize + files[i + 1].size + overhead) > limitSize) || - (limit && i + 1 - j >= limit)) { - fileSet.push(files.slice(j, i + 1)); - paramNameSlice = paramName.slice(j, i + 1); - if (!paramNameSlice.length) { - paramNameSlice = paramName; - } - paramNameSet.push(paramNameSlice); - j = i + 1; - batchSize = 0; - } - } - } else { - paramNameSet = paramName; - } - data.originalFiles = files; - $.each(fileSet || files, function (index, element) { - var newData = $.extend({}, data); - newData.files = fileSet ? element : [element]; - newData.paramName = paramNameSet[index]; - that._initResponseObject(newData); - that._initProgressObject(newData); - that._addConvenienceMethods(e, newData); - result = that._trigger( - 'add', - $.Event('add', {delegatedEvent: e}), - newData - ); - return result; - }); - return result; - }, - - _replaceFileInput: function (data) { - var input = data.fileInput, - inputClone = input.clone(true), - restoreFocus = input.is(document.activeElement); - // Add a reference for the new cloned file input to the data argument: - data.fileInputClone = inputClone; - $('
      ').append(inputClone)[0].reset(); - // Detaching allows to insert the fileInput on another form - // without loosing the file input value: - input.after(inputClone).detach(); - // If the fileInput had focus before it was detached, - // restore focus to the inputClone. - if (restoreFocus) { - inputClone.focus(); - } - // Avoid memory leaks with the detached file input: - $.cleanData(input.unbind('remove')); - // Replace the original file input element in the fileInput - // elements set with the clone, which has been copied including - // event handlers: - this.options.fileInput = this.options.fileInput.map(function (i, el) { - if (el === input[0]) { - return inputClone[0]; - } - return el; - }); - // If the widget has been initialized on the file input itself, - // override this.element with the file input clone: - if (input[0] === this.element[0]) { - this.element = inputClone; - } - }, - - _handleFileTreeEntry: function (entry, path) { - var that = this, - dfd = $.Deferred(), - errorHandler = function (e) { - if (e && !e.entry) { - e.entry = entry; - } - // Since $.when returns immediately if one - // Deferred is rejected, we use resolve instead. - // This allows valid files and invalid items - // to be returned together in one set: - dfd.resolve([e]); - }, - successHandler = function (entries) { - that._handleFileTreeEntries( - entries, - path + entry.name + '/' - ).done(function (files) { - dfd.resolve(files); - }).fail(errorHandler); - }, - readEntries = function () { - dirReader.readEntries(function (results) { - if (!results.length) { - successHandler(entries); - } else { - entries = entries.concat(results); - readEntries(); - } - }, errorHandler); - }, - dirReader, entries = []; - path = path || ''; - if (entry.isFile) { - if (entry._file) { - // Workaround for Chrome bug #149735 - entry._file.relativePath = path; - dfd.resolve(entry._file); - } else { - entry.file(function (file) { - file.relativePath = path; - dfd.resolve(file); - }, errorHandler); - } - } else if (entry.isDirectory) { - dirReader = entry.createReader(); - readEntries(); - } else { - // Return an empy list for file system items - // other than files or directories: - dfd.resolve([]); - } - return dfd.promise(); - }, - - _handleFileTreeEntries: function (entries, path) { - var that = this; - return $.when.apply( - $, - $.map(entries, function (entry) { - return that._handleFileTreeEntry(entry, path); - }) - ).pipe(function () { - return Array.prototype.concat.apply( - [], - arguments - ); - }); - }, - - _getDroppedFiles: function (dataTransfer) { - dataTransfer = dataTransfer || {}; - var items = dataTransfer.items; - if (items && items.length && (items[0].webkitGetAsEntry || - items[0].getAsEntry)) { - return this._handleFileTreeEntries( - $.map(items, function (item) { - var entry; - if (item.webkitGetAsEntry) { - entry = item.webkitGetAsEntry(); - if (entry) { - // Workaround for Chrome bug #149735: - entry._file = item.getAsFile(); - } - return entry; - } - return item.getAsEntry(); - }) - ); - } - return $.Deferred().resolve( - $.makeArray(dataTransfer.files) - ).promise(); - }, - - _getSingleFileInputFiles: function (fileInput) { - fileInput = $(fileInput); - var entries = fileInput.prop('webkitEntries') || - fileInput.prop('entries'), - files, - value; - if (entries && entries.length) { - return this._handleFileTreeEntries(entries); - } - files = $.makeArray(fileInput.prop('files')); - if (!files.length) { - value = fileInput.prop('value'); - if (!value) { - return $.Deferred().resolve([]).promise(); - } - // If the files property is not available, the browser does not - // support the File API and we add a pseudo File object with - // the input value as name with path information removed: - files = [{name: value.replace(/^.*\\/, '')}]; - } else if (files[0].name === undefined && files[0].fileName) { - // File normalization for Safari 4 and Firefox 3: - $.each(files, function (index, file) { - file.name = file.fileName; - file.size = file.fileSize; - }); - } - return $.Deferred().resolve(files).promise(); - }, - - _getFileInputFiles: function (fileInput) { - if (!(fileInput instanceof $) || fileInput.length === 1) { - return this._getSingleFileInputFiles(fileInput); - } - return $.when.apply( - $, - $.map(fileInput, this._getSingleFileInputFiles) - ).pipe(function () { - return Array.prototype.concat.apply( - [], - arguments - ); - }); - }, - - _onChange: function (e) { - var that = this, - data = { - fileInput: $(e.target), - form: $(e.target.form) - }; - this._getFileInputFiles(data.fileInput).always(function (files) { - data.files = files; - if (that.options.replaceFileInput) { - that._replaceFileInput(data); - } - if (that._trigger( - 'change', - $.Event('change', {delegatedEvent: e}), - data - ) !== false) { - that._onAdd(e, data); - } - }); - }, - - _onPaste: function (e) { - var items = e.originalEvent && e.originalEvent.clipboardData && - e.originalEvent.clipboardData.items, - data = {files: []}; - if (items && items.length) { - $.each(items, function (index, item) { - var file = item.getAsFile && item.getAsFile(); - if (file) { - data.files.push(file); - } - }); - if (this._trigger( - 'paste', - $.Event('paste', {delegatedEvent: e}), - data - ) !== false) { - this._onAdd(e, data); - } - } - }, - - _onDrop: function (e) { - e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; - var that = this, - dataTransfer = e.dataTransfer, - data = {}; - if (dataTransfer && dataTransfer.files && dataTransfer.files.length) { - e.preventDefault(); - this._getDroppedFiles(dataTransfer).always(function (files) { - data.files = files; - if (that._trigger( - 'drop', - $.Event('drop', {delegatedEvent: e}), - data - ) !== false) { - that._onAdd(e, data); - } - }); - } - }, - - _onDragOver: getDragHandler('dragover'), - - _onDragEnter: getDragHandler('dragenter'), - - _onDragLeave: getDragHandler('dragleave'), - - _initEventHandlers: function () { - if (this._isXHRUpload(this.options)) { - this._on(this.options.dropZone, { - dragover: this._onDragOver, - drop: this._onDrop, - // event.preventDefault() on dragenter is required for IE10+: - dragenter: this._onDragEnter, - // dragleave is not required, but added for completeness: - dragleave: this._onDragLeave - }); - this._on(this.options.pasteZone, { - paste: this._onPaste - }); - } - if ($.support.fileInput) { - this._on(this.options.fileInput, { - change: this._onChange - }); - } - }, - - _destroyEventHandlers: function () { - this._off(this.options.dropZone, 'dragenter dragleave dragover drop'); - this._off(this.options.pasteZone, 'paste'); - this._off(this.options.fileInput, 'change'); - }, - - _setOption: function (key, value) { - var reinit = $.inArray(key, this._specialOptions) !== -1; - if (reinit) { - this._destroyEventHandlers(); - } - this._super(key, value); - if (reinit) { - this._initSpecialOptions(); - this._initEventHandlers(); - } - }, - - _initSpecialOptions: function () { - var options = this.options; - if (options.fileInput === undefined) { - options.fileInput = this.element.is('input[type="file"]') ? - this.element : this.element.find('input[type="file"]'); - } else if (!(options.fileInput instanceof $)) { - options.fileInput = $(options.fileInput); - } - if (!(options.dropZone instanceof $)) { - options.dropZone = $(options.dropZone); - } - if (!(options.pasteZone instanceof $)) { - options.pasteZone = $(options.pasteZone); - } - }, - - _getRegExp: function (str) { - var parts = str.split('/'), - modifiers = parts.pop(); - parts.shift(); - return new RegExp(parts.join('/'), modifiers); - }, - - _isRegExpOption: function (key, value) { - return key !== 'url' && $.type(value) === 'string' && - /^\/.*\/[igm]{0,3}$/.test(value); - }, - - _initDataAttributes: function () { - var that = this, - options = this.options, - data = this.element.data(); - // Initialize options set via HTML5 data-attributes: - $.each( - this.element[0].attributes, - function (index, attr) { - var key = attr.name.toLowerCase(), - value; - if (/^data-/.test(key)) { - // Convert hyphen-ated key to camelCase: - key = key.slice(5).replace(/-[a-z]/g, function (str) { - return str.charAt(1).toUpperCase(); - }); - value = data[key]; - if (that._isRegExpOption(key, value)) { - value = that._getRegExp(value); - } - options[key] = value; - } - } - ); - }, - - _create: function () { - this._initDataAttributes(); - this._initSpecialOptions(); - this._slots = []; - this._sequence = this._getXHRPromise(true); - this._sending = this._active = 0; - this._initProgressObject(this); - this._initEventHandlers(); - }, - - // This method is exposed to the widget API and allows to query - // the number of active uploads: - active: function () { - return this._active; - }, - - // This method is exposed to the widget API and allows to query - // the widget upload progress. - // It returns an object with loaded, total and bitrate properties - // for the running uploads: - progress: function () { - return this._progress; - }, - - // This method is exposed to the widget API and allows adding files - // using the fileupload API. The data parameter accepts an object which - // must have a files property and can contain additional options: - // .fileupload('add', {files: filesList}); - add: function (data) { - var that = this; - if (!data || this.options.disabled) { - return; - } - if (data.fileInput && !data.files) { - this._getFileInputFiles(data.fileInput).always(function (files) { - data.files = files; - that._onAdd(null, data); - }); - } else { - data.files = $.makeArray(data.files); - this._onAdd(null, data); - } - }, - - // This method is exposed to the widget API and allows sending files - // using the fileupload API. The data parameter accepts an object which - // must have a files or fileInput property and can contain additional options: - // .fileupload('send', {files: filesList}); - // The method returns a Promise object for the file upload call. - send: function (data) { - if (data && !this.options.disabled) { - if (data.fileInput && !data.files) { - var that = this, - dfd = $.Deferred(), - promise = dfd.promise(), - jqXHR, - aborted; - promise.abort = function () { - aborted = true; - if (jqXHR) { - return jqXHR.abort(); - } - dfd.reject(null, 'abort', 'abort'); - return promise; - }; - this._getFileInputFiles(data.fileInput).always( - function (files) { - if (aborted) { - return; - } - if (!files.length) { - dfd.reject(); - return; - } - data.files = files; - jqXHR = that._onSend(null, data); - jqXHR.then( - function (result, textStatus, jqXHR) { - dfd.resolve(result, textStatus, jqXHR); - }, - function (jqXHR, textStatus, errorThrown) { - dfd.reject(jqXHR, textStatus, errorThrown); - } - ); - } - ); - return this._enhancePromise(promise); - } - data.files = $.makeArray(data.files); - if (data.files.length) { - return this._onSend(null, data); - } - } - return this._getXHRPromise(false, data && data.context); - } - - }); - -})); diff --git a/artbox-file/assets/js/jquery.iframe-transport.js b/artbox-file/assets/js/jquery.iframe-transport.js deleted file mode 100755 index b63a16f..0000000 --- a/artbox-file/assets/js/jquery.iframe-transport.js +++ /dev/null @@ -1,217 +0,0 @@ -/* - * jQuery Iframe Transport Plugin - * https://github.com/blueimp/jQuery-File-Upload - * - * Copyright 2011, Sebastian Tschan - * https://blueimp.net - * - * Licensed under the MIT license: - * http://www.opensource.org/licenses/MIT - */ - -/* global define, require, window, document */ - -(function (factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // Register as an anonymous AMD module: - define(['jquery'], factory); - } else if (typeof exports === 'object') { - // Node/CommonJS: - factory(require('jquery')); - } else { - // Browser globals: - factory(window.jQuery); - } -}(function ($) { - 'use strict'; - - // Helper variable to create unique names for the transport iframes: - var counter = 0; - - // The iframe transport accepts four additional options: - // options.fileInput: a jQuery collection of file input fields - // options.paramName: the parameter name for the file form data, - // overrides the name property of the file input field(s), - // can be a string or an array of strings. - // options.formData: an array of objects with name and value properties, - // equivalent to the return data of .serializeArray(), e.g.: - // [{name: 'a', value: 1}, {name: 'b', value: 2}] - // options.initialIframeSrc: the URL of the initial iframe src, - // by default set to "javascript:false;" - $.ajaxTransport('iframe', function (options) { - if (options.async) { - // javascript:false as initial iframe src - // prevents warning popups on HTTPS in IE6: - /*jshint scripturl: true */ - var initialIframeSrc = options.initialIframeSrc || 'javascript:false;', - /*jshint scripturl: false */ - form, - iframe, - addParamChar; - return { - send: function (_, completeCallback) { - form = $('
      '); - form.attr('accept-charset', options.formAcceptCharset); - addParamChar = /\?/.test(options.url) ? '&' : '?'; - // XDomainRequest only supports GET and POST: - if (options.type === 'DELETE') { - options.url = options.url + addParamChar + '_method=DELETE'; - options.type = 'POST'; - } else if (options.type === 'PUT') { - options.url = options.url + addParamChar + '_method=PUT'; - options.type = 'POST'; - } else if (options.type === 'PATCH') { - options.url = options.url + addParamChar + '_method=PATCH'; - options.type = 'POST'; - } - // IE versions below IE8 cannot set the name property of - // elements that have already been added to the DOM, - // so we set the name along with the iframe HTML markup: - counter += 1; - iframe = $( - '' - ).bind('load', function () { - var fileInputClones, - paramNames = $.isArray(options.paramName) ? - options.paramName : [options.paramName]; - iframe - .unbind('load') - .bind('load', function () { - var response; - // Wrap in a try/catch block to catch exceptions thrown - // when trying to access cross-domain iframe contents: - try { - response = iframe.contents(); - // Google Chrome and Firefox do not throw an - // exception when calling iframe.contents() on - // cross-domain requests, so we unify the response: - if (!response.length || !response[0].firstChild) { - throw new Error(); - } - } catch (e) { - response = undefined; - } - // The complete callback returns the - // iframe content document as response object: - completeCallback( - 200, - 'success', - {'iframe': response} - ); - // Fix for IE endless progress bar activity bug - // (happens on form submits to iframe targets): - $('') - .appendTo(form); - window.setTimeout(function () { - // Removing the form in a setTimeout call - // allows Chrome's developer tools to display - // the response result - form.remove(); - }, 0); - }); - form - .prop('target', iframe.prop('name')) - .prop('action', options.url) - .prop('method', options.type); - if (options.formData) { - $.each(options.formData, function (index, field) { - $('') - .prop('name', field.name) - .val(field.value) - .appendTo(form); - }); - } - if (options.fileInput && options.fileInput.length && - options.type === 'POST') { - fileInputClones = options.fileInput.clone(); - // Insert a clone for each file input field: - options.fileInput.after(function (index) { - return fileInputClones[index]; - }); - if (options.paramName) { - options.fileInput.each(function (index) { - $(this).prop( - 'name', - paramNames[index] || options.paramName - ); - }); - } - // Appending the file input fields to the hidden form - // removes them from their original location: - form - .append(options.fileInput) - .prop('enctype', 'multipart/form-data') - // enctype must be set as encoding for IE: - .prop('encoding', 'multipart/form-data'); - // Remove the HTML5 form attribute from the input(s): - options.fileInput.removeAttr('form'); - } - form.submit(); - // Insert the file input fields at their original location - // by replacing the clones with the originals: - if (fileInputClones && fileInputClones.length) { - options.fileInput.each(function (index, input) { - var clone = $(fileInputClones[index]); - // Restore the original name and form properties: - $(input) - .prop('name', clone.prop('name')) - .attr('form', clone.attr('form')); - clone.replaceWith(input); - }); - } - }); - form.append(iframe).appendTo(document.body); - }, - abort: function () { - if (iframe) { - // javascript:false as iframe src aborts the request - // and prevents warning popups on HTTPS in IE6. - // concat is used to avoid the "Script URL" JSLint error: - iframe - .unbind('load') - .prop('src', initialIframeSrc); - } - if (form) { - form.remove(); - } - } - }; - } - }); - - // The iframe transport returns the iframe content document as response. - // The following adds converters from iframe to text, json, html, xml - // and script. - // Please note that the Content-Type for JSON responses has to be text/plain - // or text/html, if the browser doesn't include application/json in the - // Accept header, else IE will show a download dialog. - // The Content-Type for XML responses on the other hand has to be always - // application/xml or text/xml, so IE properly parses the XML response. - // See also - // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation - $.ajaxSetup({ - converters: { - 'iframe text': function (iframe) { - return iframe && $(iframe[0].body).text(); - }, - 'iframe json': function (iframe) { - return iframe && $.parseJSON($(iframe[0].body).text()); - }, - 'iframe html': function (iframe) { - return iframe && $(iframe[0].body).html(); - }, - 'iframe xml': function (iframe) { - var xmlDoc = iframe && iframe[0]; - return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc : - $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) || - $(xmlDoc.body).html()); - }, - 'iframe script': function (iframe) { - return iframe && $.globalEval($(iframe[0].body).text()); - } - } - }); - -})); diff --git a/artbox-file/assets/js/vendor/jquery.ui.widget.js b/artbox-file/assets/js/vendor/jquery.ui.widget.js deleted file mode 100755 index 113c1f0..0000000 --- a/artbox-file/assets/js/vendor/jquery.ui.widget.js +++ /dev/null @@ -1,572 +0,0 @@ -/*! jQuery UI - v1.11.4+CommonJS - 2015-08-28 -* http://jqueryui.com -* Includes: widget.js -* Copyright 2015 jQuery Foundation and other contributors; Licensed MIT */ - -(function( factory ) { - if ( typeof define === "function" && define.amd ) { - - // AMD. Register as an anonymous module. - define([ "jquery" ], factory ); - - } else if ( typeof exports === "object" ) { - - // Node/CommonJS - factory( require( "jquery" ) ); - - } else { - - // Browser globals - factory( jQuery ); - } -}(function( $ ) { -/*! - * jQuery UI Widget 1.11.4 - * http://jqueryui.com - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license. - * http://jquery.org/license - * - * http://api.jqueryui.com/jQuery.widget/ - */ - - -var widget_uuid = 0, - widget_slice = Array.prototype.slice; - -$.cleanData = (function( orig ) { - return function( elems ) { - var events, elem, i; - for ( i = 0; (elem = elems[i]) != null; i++ ) { - try { - - // Only trigger remove when necessary to save time - events = $._data( elem, "events" ); - if ( events && events.remove ) { - $( elem ).triggerHandler( "remove" ); - } - - // http://bugs.jquery.com/ticket/8235 - } catch ( e ) {} - } - orig( elems ); - }; -})( $.cleanData ); - -$.widget = function( name, base, prototype ) { - var fullName, existingConstructor, constructor, basePrototype, - // proxiedPrototype allows the provided prototype to remain unmodified - // so that it can be used as a mixin for multiple widgets (#8876) - proxiedPrototype = {}, - namespace = name.split( "." )[ 0 ]; - - name = name.split( "." )[ 1 ]; - fullName = namespace + "-" + name; - - if ( !prototype ) { - prototype = base; - base = $.Widget; - } - - // create selector for plugin - $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { - return !!$.data( elem, fullName ); - }; - - $[ namespace ] = $[ namespace ] || {}; - existingConstructor = $[ namespace ][ name ]; - constructor = $[ namespace ][ name ] = function( options, element ) { - // allow instantiation without "new" keyword - if ( !this._createWidget ) { - return new constructor( options, element ); - } - - // allow instantiation without initializing for simple inheritance - // must use "new" keyword (the code above always passes args) - if ( arguments.length ) { - this._createWidget( options, element ); - } - }; - // extend with the existing constructor to carry over any static properties - $.extend( constructor, existingConstructor, { - version: prototype.version, - // copy the object used to create the prototype in case we need to - // redefine the widget later - _proto: $.extend( {}, prototype ), - // track widgets that inherit from this widget in case this widget is - // redefined after a widget inherits from it - _childConstructors: [] - }); - - basePrototype = new base(); - // we need to make the options hash a property directly on the new instance - // otherwise we'll modify the options hash on the prototype that we're - // inheriting from - basePrototype.options = $.widget.extend( {}, basePrototype.options ); - $.each( prototype, function( prop, value ) { - if ( !$.isFunction( value ) ) { - proxiedPrototype[ prop ] = value; - return; - } - proxiedPrototype[ prop ] = (function() { - var _super = function() { - return base.prototype[ prop ].apply( this, arguments ); - }, - _superApply = function( args ) { - return base.prototype[ prop ].apply( this, args ); - }; - return function() { - var __super = this._super, - __superApply = this._superApply, - returnValue; - - this._super = _super; - this._superApply = _superApply; - - returnValue = value.apply( this, arguments ); - - this._super = __super; - this._superApply = __superApply; - - return returnValue; - }; - })(); - }); - constructor.prototype = $.widget.extend( basePrototype, { - // TODO: remove support for widgetEventPrefix - // always use the name + a colon as the prefix, e.g., draggable:start - // don't prefix for widgets that aren't DOM-based - widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name - }, proxiedPrototype, { - constructor: constructor, - namespace: namespace, - widgetName: name, - widgetFullName: fullName - }); - - // If this widget is being redefined then we need to find all widgets that - // are inheriting from it and redefine all of them so that they inherit from - // the new version of this widget. We're essentially trying to replace one - // level in the prototype chain. - if ( existingConstructor ) { - $.each( existingConstructor._childConstructors, function( i, child ) { - var childPrototype = child.prototype; - - // redefine the child widget using the same prototype that was - // originally used, but inherit from the new version of the base - $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); - }); - // remove the list of existing child constructors from the old constructor - // so the old child constructors can be garbage collected - delete existingConstructor._childConstructors; - } else { - base._childConstructors.push( constructor ); - } - - $.widget.bridge( name, constructor ); - - return constructor; -}; - -$.widget.extend = function( target ) { - var input = widget_slice.call( arguments, 1 ), - inputIndex = 0, - inputLength = input.length, - key, - value; - for ( ; inputIndex < inputLength; inputIndex++ ) { - for ( key in input[ inputIndex ] ) { - value = input[ inputIndex ][ key ]; - if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { - // Clone objects - if ( $.isPlainObject( value ) ) { - target[ key ] = $.isPlainObject( target[ key ] ) ? - $.widget.extend( {}, target[ key ], value ) : - // Don't extend strings, arrays, etc. with objects - $.widget.extend( {}, value ); - // Copy everything else by reference - } else { - target[ key ] = value; - } - } - } - } - return target; -}; - -$.widget.bridge = function( name, object ) { - var fullName = object.prototype.widgetFullName || name; - $.fn[ name ] = function( options ) { - var isMethodCall = typeof options === "string", - args = widget_slice.call( arguments, 1 ), - returnValue = this; - - if ( isMethodCall ) { - this.each(function() { - var methodValue, - instance = $.data( this, fullName ); - if ( options === "instance" ) { - returnValue = instance; - return false; - } - if ( !instance ) { - return $.error( "cannot call methods on " + name + " prior to initialization; " + - "attempted to call method '" + options + "'" ); - } - if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { - return $.error( "no such method '" + options + "' for " + name + " widget instance" ); - } - methodValue = instance[ options ].apply( instance, args ); - if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue && methodValue.jquery ? - returnValue.pushStack( methodValue.get() ) : - methodValue; - return false; - } - }); - } else { - - // Allow multiple hashes to be passed on init - if ( args.length ) { - options = $.widget.extend.apply( null, [ options ].concat(args) ); - } - - this.each(function() { - var instance = $.data( this, fullName ); - if ( instance ) { - instance.option( options || {} ); - if ( instance._init ) { - instance._init(); - } - } else { - $.data( this, fullName, new object( options, this ) ); - } - }); - } - - return returnValue; - }; -}; - -$.Widget = function( /* options, element */ ) {}; -$.Widget._childConstructors = []; - -$.Widget.prototype = { - widgetName: "widget", - widgetEventPrefix: "", - defaultElement: "
      ", - options: { - disabled: false, - - // callbacks - create: null - }, - _createWidget: function( options, element ) { - element = $( element || this.defaultElement || this )[ 0 ]; - this.element = $( element ); - this.uuid = widget_uuid++; - this.eventNamespace = "." + this.widgetName + this.uuid; - - this.bindings = $(); - this.hoverable = $(); - this.focusable = $(); - - if ( element !== this ) { - $.data( element, this.widgetFullName, this ); - this._on( true, this.element, { - remove: function( event ) { - if ( event.target === element ) { - this.destroy(); - } - } - }); - this.document = $( element.style ? - // element within the document - element.ownerDocument : - // element is window or document - element.document || element ); - this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); - } - - this.options = $.widget.extend( {}, - this.options, - this._getCreateOptions(), - options ); - - this._create(); - this._trigger( "create", null, this._getCreateEventData() ); - this._init(); - }, - _getCreateOptions: $.noop, - _getCreateEventData: $.noop, - _create: $.noop, - _init: $.noop, - - destroy: function() { - this._destroy(); - // we can probably remove the unbind calls in 2.0 - // all event bindings should go through this._on() - this.element - .unbind( this.eventNamespace ) - .removeData( this.widgetFullName ) - // support: jquery <1.6.3 - // http://bugs.jquery.com/ticket/9413 - .removeData( $.camelCase( this.widgetFullName ) ); - this.widget() - .unbind( this.eventNamespace ) - .removeAttr( "aria-disabled" ) - .removeClass( - this.widgetFullName + "-disabled " + - "ui-state-disabled" ); - - // clean up events and states - this.bindings.unbind( this.eventNamespace ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - }, - _destroy: $.noop, - - widget: function() { - return this.element; - }, - - option: function( key, value ) { - var options = key, - parts, - curOption, - i; - - if ( arguments.length === 0 ) { - // don't return a reference to the internal hash - return $.widget.extend( {}, this.options ); - } - - if ( typeof key === "string" ) { - // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } - options = {}; - parts = key.split( "." ); - key = parts.shift(); - if ( parts.length ) { - curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); - for ( i = 0; i < parts.length - 1; i++ ) { - curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; - curOption = curOption[ parts[ i ] ]; - } - key = parts.pop(); - if ( arguments.length === 1 ) { - return curOption[ key ] === undefined ? null : curOption[ key ]; - } - curOption[ key ] = value; - } else { - if ( arguments.length === 1 ) { - return this.options[ key ] === undefined ? null : this.options[ key ]; - } - options[ key ] = value; - } - } - - this._setOptions( options ); - - return this; - }, - _setOptions: function( options ) { - var key; - - for ( key in options ) { - this._setOption( key, options[ key ] ); - } - - return this; - }, - _setOption: function( key, value ) { - this.options[ key ] = value; - - if ( key === "disabled" ) { - this.widget() - .toggleClass( this.widgetFullName + "-disabled", !!value ); - - // If the widget is becoming disabled, then nothing is interactive - if ( value ) { - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); - } - } - - return this; - }, - - enable: function() { - return this._setOptions({ disabled: false }); - }, - disable: function() { - return this._setOptions({ disabled: true }); - }, - - _on: function( suppressDisabledCheck, element, handlers ) { - var delegateElement, - instance = this; - - // no suppressDisabledCheck flag, shuffle arguments - if ( typeof suppressDisabledCheck !== "boolean" ) { - handlers = element; - element = suppressDisabledCheck; - suppressDisabledCheck = false; - } - - // no element argument, shuffle and use this.element - if ( !handlers ) { - handlers = element; - element = this.element; - delegateElement = this.widget(); - } else { - element = delegateElement = $( element ); - this.bindings = this.bindings.add( element ); - } - - $.each( handlers, function( event, handler ) { - function handlerProxy() { - // allow widgets to customize the disabled handling - // - disabled as an array instead of boolean - // - disabled class as method for disabling individual parts - if ( !suppressDisabledCheck && - ( instance.options.disabled === true || - $( this ).hasClass( "ui-state-disabled" ) ) ) { - return; - } - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - - // copy the guid so direct unbinding works - if ( typeof handler !== "string" ) { - handlerProxy.guid = handler.guid = - handler.guid || handlerProxy.guid || $.guid++; - } - - var match = event.match( /^([\w:-]*)\s*(.*)$/ ), - eventName = match[1] + instance.eventNamespace, - selector = match[2]; - if ( selector ) { - delegateElement.delegate( selector, eventName, handlerProxy ); - } else { - element.bind( eventName, handlerProxy ); - } - }); - }, - - _off: function( element, eventName ) { - eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + - this.eventNamespace; - element.unbind( eventName ).undelegate( eventName ); - - // Clear the stack to avoid memory leaks (#10056) - this.bindings = $( this.bindings.not( element ).get() ); - this.focusable = $( this.focusable.not( element ).get() ); - this.hoverable = $( this.hoverable.not( element ).get() ); - }, - - _delay: function( handler, delay ) { - function handlerProxy() { - return ( typeof handler === "string" ? instance[ handler ] : handler ) - .apply( instance, arguments ); - } - var instance = this; - return setTimeout( handlerProxy, delay || 0 ); - }, - - _hoverable: function( element ) { - this.hoverable = this.hoverable.add( element ); - this._on( element, { - mouseenter: function( event ) { - $( event.currentTarget ).addClass( "ui-state-hover" ); - }, - mouseleave: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-hover" ); - } - }); - }, - - _focusable: function( element ) { - this.focusable = this.focusable.add( element ); - this._on( element, { - focusin: function( event ) { - $( event.currentTarget ).addClass( "ui-state-focus" ); - }, - focusout: function( event ) { - $( event.currentTarget ).removeClass( "ui-state-focus" ); - } - }); - }, - - _trigger: function( type, event, data ) { - var prop, orig, - callback = this.options[ type ]; - - data = data || {}; - event = $.Event( event ); - event.type = ( type === this.widgetEventPrefix ? - type : - this.widgetEventPrefix + type ).toLowerCase(); - // the original event may come from any element - // so we need to reset the target on the new event - event.target = this.element[ 0 ]; - - // copy original event properties over to the new event - orig = event.originalEvent; - if ( orig ) { - for ( prop in orig ) { - if ( !( prop in event ) ) { - event[ prop ] = orig[ prop ]; - } - } - } - - this.element.trigger( event, data ); - return !( $.isFunction( callback ) && - callback.apply( this.element[0], [ event ].concat( data ) ) === false || - event.isDefaultPrevented() ); - } -}; - -$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { - $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { - if ( typeof options === "string" ) { - options = { effect: options }; - } - var hasOptions, - effectName = !options ? - method : - options === true || typeof options === "number" ? - defaultEffect : - options.effect || defaultEffect; - options = options || {}; - if ( typeof options === "number" ) { - options = { duration: options }; - } - hasOptions = !$.isEmptyObject( options ); - options.complete = callback; - if ( options.delay ) { - element.delay( options.delay ); - } - if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { - element[ method ]( options ); - } else if ( effectName !== method && element[ effectName ] ) { - element[ effectName ]( options.duration, options.easing, callback ); - } else { - element.queue(function( next ) { - $( this )[ method ](); - if ( callback ) { - callback.call( element[ 0 ] ); - } - next(); - }); - } - }; -}); - -var widget = $.widget; - - - -})); diff --git a/artbox-file/composer.json b/artbox-file/composer.json deleted file mode 100644 index f80512b..0000000 --- a/artbox-file/composer.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "artweb/artbox-file", - "description": "Yii2 light-weight CMS", - "license": "BSD-3-Clause", - "require": { - "php": ">=7.0", - "yiisoft/yii2": "*", - "developeruz/yii2-db-rbac": "*" - }, - "autoload": { - "psr-4": { - "artweb\\artbox\\flie\\": "" - } - } -} \ No newline at end of file diff --git a/artbox-file/config.php b/artbox-file/config.php deleted file mode 100755 index c1d2298..0000000 --- a/artbox-file/config.php +++ /dev/null @@ -1,3 +0,0 @@ -$w){ - return true; - }else if($height >$h) { - return true; - } - return false; - } - - - private function getUserPath(){ - if(isset(Yii::$app->user->id)){ - return 'user_'.Yii::$app->user->id; - }else { - return 'guest'; - } - } - - private function resizeImg($w, $h, $imageAlias,$imageAliasSave){ - $img = Image::getImagine()->open(Yii::getAlias($imageAlias)); - - $size = $img->getSize(); - - $width = $size->getWidth(); - $height = $size->getHeight(); - - $e_width = $w/$h; - $e_height = $h/$w; - - $e1_width = $width/$height; - $e1_height = $height/$width; - - - - if($e_width<$e1_width){ - - $new_width = $width*($e_width/$e1_width); - - $y = 0; - $x = $width/ 2-($new_width/2); - $width = $new_width; - - }else { - - $new_height = $height*($e_height/$e1_height); - $x = 0; - $y = $height/2-($new_height/2); - $height = $new_height; - } - - - - - Image::crop($imageAlias, $width, $height,[$x,$y]) - ->save(Yii::getAlias($imageAliasSave), ['quality' => - 100]); - - - $imagine = new Imagine(); - $imagine->open($imageAliasSave) - ->resize(new Box($w, $h)) - ->save($imageAliasSave, array('flatten' => false)); - - - - } - - private function deleteImages($old_img){ - - if(!empty($old_img) && file_exists($_SERVER['DOCUMENT_ROOT'].$old_img)){ - - $rootDir = explode("/", $old_img); - - $row = $_SERVER['DOCUMENT_ROOT'].'/'.$rootDir[1].'/'.$rootDir[2].'/'.$rootDir[3].'/'; - - $allFiles = scandir($row); - - $allFiles = array_slice($allFiles, 2); - - foreach($allFiles as $oldFile){ - - unlink($row.$oldFile); - - } - - } - } - - public function actionDeleteImage(){ - - $this->enableCsrfValidation = false; - - $request = Yii::$app->request->post(); - - if($request){ - if ($request['old_img']) { - $this->deleteImages($request['old_img']); - } - if(isset($request['action']) && $request['action']=='save'){ - $object = str_replace('-', '\\',$request['model']); - $model = new $object; - $model = $model->findOne($request['id']); - $model->$request['field'] = $request['new_url']; - $model->save(); - } - } - - } - - - public function actionDownloadPhoto() - { - - $model = new ImageSizerForm(); - - $request = Yii::$app->request->post(); - - if ($request) { - - $model->multi = isset($request['multi'])? 1 : 0; - - $model->file = UploadedFile::getInstance($model, 'file'); - - $md5_file = md5_file($model->file->tempName).rand(1, 1000); - - $imgDir = Yii::getAlias('@storage/'.$this->getUserPath().'/'.$md5_file.'/'); - - $imageOrigAlias = Yii::getAlias($imgDir.'original'.'.'.$model->file->extension); - - if(!is_dir($imgDir)) { - mkdir($imgDir, 0755, true); - } - - $model->file->saveAs($imageOrigAlias); - - - if(isset($request['size'] )){ - - $request['size'] = ArrayHelper::toArray(json_decode($request['size'])); - - foreach($request['size'] as $size){ - if($size['width'] && $size['height']){ - - $imageAlias = Yii::getAlias($imgDir.$size['width'].'x'.$size['height'].'.'.$model->file->extension); - - $imageLink = '/storage/'.$this->getUserPath().'/'.$md5_file.'/'.$size['width'].'x'.$size['height'].'.'.$model->file->extension; - - $this->resizeImg($size['width'], $size['height'], $imageOrigAlias,$imageAlias); - - } - } - - } else { - - $imageLink = '/storage/'.$this->getUserPath().'/'.$md5_file.'/'.'original'.'.'.$model->file->extension; - - } - - - if($model->multi){ - $view = $this->renderPartial('/_gallery_item', [ - 'item' => ['image'=>$imageLink], - 'field'=>$request['field'] - ]); - return json_encode(['link'=>$imageLink, - 'view' =>$view, - - ]); - - - } else { - $view = $this->renderPartial('/_one_item', [ - 'item' => ['image'=>$imageLink], - 'field'=>$request['field'] - ]); - return json_encode(['link'=>$imageLink, - 'view' =>$view, - ]); - } - - - } - } - - - public function getex($filename) { - $array = explode(".", $filename); - return array_pop($array); - } - - - public function actionImagesUpload(){ - - if($_FILES['upload']) - { - if (($_FILES['upload'] == "none") OR (empty($_FILES['upload']['name'])) ) - { - $message = "Вы не выбрали файл"; - } - else if ($_FILES['upload']["size"] == 0 OR $_FILES['upload']["size"] > 2050000) - { - $message = "Размер файла не соответствует нормам"; - } - else if (($_FILES['upload']["type"] != "image/jpeg") AND ($_FILES['upload']["type"] != "image/jpeg") AND ($_FILES['upload']["type"] != "image/png") AND ($_FILES['upload']['type'] != 'image/gif')) - { - $message = "Допускается загрузка только картинок JPG и PNG."; - } - else if (!is_uploaded_file($_FILES['upload']["tmp_name"])) - { - $message = "Что-то пошло не так. Попытайтесь загрузить файл ещё раз."; - } - else{ - $filename = $_FILES['upload']['name']; - $name =$_FILES['upload']['name'].'.'.$this->getex($filename); - - $path = "../../storage/".$this->getUserPath()."/images/"; - if(!is_dir($path)) { - mkdir($path, 0755, true); - } - - - - move_uploaded_file($_FILES['upload']['tmp_name'], $path.$name); - - $full_path = '/storage/'.$this->getUserPath().'/images/'.$name; - - $message = "Файл ".$_FILES['upload']['name']." загружен"; - - - } - $callback = $_REQUEST['CKEditorFuncNum']; - echo ''; - } - } - - -} \ No newline at end of file diff --git a/artbox-file/models/ImageSizerForm.php b/artbox-file/models/ImageSizerForm.php deleted file mode 100755 index 99bf71e..0000000 --- a/artbox-file/models/ImageSizerForm.php +++ /dev/null @@ -1,38 +0,0 @@ - 255], - [['model', 'form',], 'string'], - [['file','img','price_list'], 'file'], - ]; - } -} \ No newline at end of file diff --git a/artbox-file/views/_gallery_item.php b/artbox-file/views/_gallery_item.php deleted file mode 100755 index 9aea75e..0000000 --- a/artbox-file/views/_gallery_item.php +++ /dev/null @@ -1,9 +0,0 @@ - - -
      - - -
      \ No newline at end of file diff --git a/artbox-file/views/_one_item.php b/artbox-file/views/_one_item.php deleted file mode 100755 index fed9ba9..0000000 --- a/artbox-file/views/_one_item.php +++ /dev/null @@ -1,17 +0,0 @@ - - -
      - -
      - - -
      - - 'thumbnail']) ?> - -
      - diff --git a/artbox-file/widgets/ImageUploader.php b/artbox-file/widgets/ImageUploader.php deleted file mode 100755 index c04f7a9..0000000 --- a/artbox-file/widgets/ImageUploader.php +++ /dev/null @@ -1,63 +0,0 @@ -render('image_sizer', - [ - 'model'=>$this->model, - 'size' => $this->size, - 'field' => $this->field, - 'height' => $this->height, - 'width' => $this->width, - 'multi' => $this->multi, - 'name' => $this->name, - 'remover' => $this->remover - ]); - - } - - public function getGallery(){ - if($this->gallery){ - $array = explode(",", $this->gallery); - if(count($array) > 1){ - array_pop($array); - } - return $array; - } else { - return array(); - } - - } - -} \ No newline at end of file diff --git a/artbox-file/widgets/views/image_sizer.php b/artbox-file/widgets/views/image_sizer.php deleted file mode 100755 index ee16e6e..0000000 --- a/artbox-file/widgets/views/image_sizer.php +++ /dev/null @@ -1,203 +0,0 @@ -tableSchema->primaryKey[0]; - -?> - - -
      - - "{$field}_picture_link"]) ?> - - - - - - -
      -
      - - $field): ?> - - - - $field):?> - $field),'#',['class'=>'thumbnail']) ?> - -
      -
      - -
      -
      - - - - - $field, 'data-url'=>Yii::$app->getUrlManager()->createUrl('file/uploader/download-photo')]);?> - -
      - - - - - - - - - - - - - - - - - - $field, 'data-url'=>Yii::$app->getUrlManager()->createUrl('file/uploader/download-photo'), 'multiple'=> 'multiple' ]);?> - - - "{$field}_picture_link"]) ?> - - - - -
      - context->getGallery() as $image){ - echo $this->render('@common/modules/file/views/_gallery_item', [ 'item' => ['image'=>$image]]); - } - ?> -
      - - - diff --git a/artbox-language/Module.php b/artbox-language/Module.php deleted file mode 100755 index 96903e3..0000000 --- a/artbox-language/Module.php +++ /dev/null @@ -1,12 +0,0 @@ - 'beforeSave', - ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeSave', - ActiveRecord::EVENT_AFTER_INSERT => 'afterSave', - ActiveRecord::EVENT_AFTER_UPDATE => 'afterSave', - ]; - } - - /** - * Get $owner primary key to link language model - * @return string - */ - public function getOwnerKey():string - { - if(!empty( $this->ownerKey )) { - return $this->ownerKey; - } else { - return $this->owner->primaryKey()[ 0 ]; - } - } - - /** - * Set which attribute to use as $owner primary key to link language model - * - * @param string $value - */ - public function setOwnerKey(string $value) - { - $this->ownerKey = $value; - } - - /** - * Get language model attribute that is used as foreign key to $owner - * @return string - */ - public function getLangKey():string - { - if(!empty( $this->langKey )) { - return $this->langKey; - } else { - $owner = $this->owner; - return $owner::getTableSchema()->name . '_id'; - } - } - - /** - * Set which attribute to use as language model foreign key to $owner - * - * @param $value - */ - public function setLangKey(string $value) - { - $this->langKey = $value; - } - - /** - * Additional checks to attach this behavior - * - * @param ActiveRecord $owner - * - * @throws InvalidConfigException - */ - public function attach($owner) - { - if(empty( $this->objectLang )) { - $this->objectLang = $owner::className() . 'Lang'; - } elseif(!is_string($this->objectLang)) { - throw new InvalidConfigException('Object lang must be fully classified namespaced classname'); - } - try { - $this->objectLang = \Yii::createObject($this->objectLang); - } catch(\ReflectionException $exception) { - throw new InvalidConfigException('Object lang must be fully classified namespaced classname'); - } - if(( !$owner instanceof ActiveRecord ) || ( !$this->objectLang instanceof ActiveRecord )) { - throw new InvalidConfigException('Object lang must be fully classified namespaced classname'); - } - parent::attach($owner); - } - - /** - * Get query to get all language models for $owner indexed by language_id - * @return ActiveQuery - */ - public function getLangs() - { - $objectLang = $this->objectLang; - $owner = $this->owner; - return $owner->hasMany($objectLang::className(), [ $this->getLangKey() => $this->getOwnerKey() ]) - ->indexBy('language_id'); - } - - /** - * Get query to get language model for $owner for language_id, default to - * Language::getCurrent() - * - * @param int $language_id - * - * @return ActiveQuery - */ - public function getLang(int $language_id = NULL) - { - if(empty( $language_id )) { - $language_id = Language::getCurrent()->id; - } - $objectLang = $this->objectLang; - $table_name = $objectLang::getTableSchema()->name; - $owner = $this->owner; - return $owner->hasOne($objectLang::className(), [ $this->getLangKey() => $this->getOwnerKey() ]) - ->where([ $table_name . '.language_id' => $language_id ]); - } - - /** - * Generate language models for $owner for active languages. If $owner not new and language - * models already inserted, models will be filled with them. - * @return void - */ - public function generateLangs() - { - $owner = $this->owner; - $languages = Language::find() - ->where([ 'status' => true ]) - ->orderBy([ 'id' => SORT_ASC ]) - ->asArray() - ->column(); - $objectLang = $this->objectLang; - $owner_key = $this->getOwnerKey(); - $langs = []; - if(!$owner->isNewRecord) { - $langs = $this->getLangs() - ->andFilterWhere([ 'language_id' => $languages ]) - ->orderBy([ 'language_id' => SORT_ASC ]) - ->all(); - } - foreach($languages as $language) { - if(!array_key_exists($language, $langs)) { - $langs[ $language ] = \Yii::createObject([ - 'class' => $objectLang::className(), - 'language_id' => $language, - $this->getLangKey() => ( $owner->isNewRecord ? NULL : $owner->$owner_key ), - ]); - } - } - $this->modelLangs = $langs; - } - - /** - * Load language models with post data. - * - * @param Request $request - */ - public function loadLangs(Request $request) - { - foreach($request->post($this->objectLang->formName(), []) as $lang => $value) { - if(!empty( $this->modelLangs[ $lang ] )) { - $this->modelLangs[ $lang ]->attributes = $value; - $this->modelLangs[ $lang ]->language_id = $lang; - } - } - } - - /** - * Link language models with $owner by setting language model language key to owner key of - * owner - * @return bool If $owner is new record then return false else true - */ - public function linkLangs() - { - $owner = $this->owner; - // if($owner->isNewRecord) { - // return false; - // } - $lang_key = $this->getLangKey(); - $owner_key = $this->getOwnerKey(); - $modelLangs = $this->modelLangs; - foreach($modelLangs as $model_lang) { - $model_lang->$lang_key = $owner->$owner_key; - } - return true; - } - - /** - * Try to save all language models to the db. Validation function is run for all models. - * @return bool Whether all models are valid - */ - public function saveLangs() - { - $success = true; - $modelLangs = $this->modelLangs; - foreach($modelLangs as $model_lang) { - if($model_lang->save() === false) { - $success = false; - } - } - return $success; - } - - public function beforeSave($event) - { - /** - * @var ActiveRecord $owner - */ - $owner = $this->owner; - $db = $owner::getDb(); - $this->transaction = $db->beginTransaction(); - if($owner->hasAttribute('remote_id') && empty( $owner->remote_id )) { - $owner->remote_id = strval(microtime(true) * 10000); - } - } - - public function afterSave($event) - { - if(!empty( $this->modelLangs )) { - if($this->linkLangs() && $this->saveLangs()) { - $this->transaction->commit(); - $this->transactionStatus = true; - } else { - $this->transaction->rollBack(); - $this->transactionStatus = false; - } - } else { - $this->transaction->commit(); - $this->transactionStatus = true; - } - } - - /** - * @return bool - */ - public function getTransactionStatus():bool - { - return $this->transactionStatus; - } - } \ No newline at end of file diff --git a/artbox-language/components/LanguageRequest.php b/artbox-language/components/LanguageRequest.php deleted file mode 100755 index 9e8fe02..0000000 --- a/artbox-language/components/LanguageRequest.php +++ /dev/null @@ -1,75 +0,0 @@ -languageUrl === NULL) { - $this->languageUrl = $this->getUrl(); - - $url_list = explode('/', $this->languageUrl); - - $language_url = isset( $url_list[ 1 ] ) ? $url_list[ 1 ] : NULL; - Language::setCurrent($language_url); - - if($language_url !== NULL && $language_url === Language::getCurrent()->url && strpos($this->languageUrl, Language::getCurrent()->url) === 1) { - $this->languageUrl = substr($this->languageUrl, strlen(Language::getCurrent()->url) + 1); - } - } - - return $this->languageUrl; - } - - protected function resolvePathInfo() - { - $pathInfo = $this->getLanguageUrl(); - - if(( $pos = strpos($pathInfo, '?') ) !== false) { - $pathInfo = substr($pathInfo, 0, $pos); - } - - $pathInfo = urldecode($pathInfo); - - if(!preg_match('%^(?: - [\x09\x0A\x0D\x20-\x7E] - | [\xC2-\xDF][\x80-\xBF] - | \xE0[\xA0-\xBF][\x80-\xBF] - | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} - | \xED[\x80-\x9F][\x80-\xBF] - | \xF0[\x90-\xBF][\x80-\xBF]{2} - | [\xF1-\xF3][\x80-\xBF]{3} - | \xF4[\x80-\x8F][\x80-\xBF]{2} - )*$%xs', $pathInfo) - ) { - $pathInfo = utf8_encode($pathInfo); - } - - $scriptUrl = $this->getScriptUrl(); - $baseUrl = $this->getBaseUrl(); - - if(strpos($pathInfo, $scriptUrl) === 0) { - $pathInfo = substr($pathInfo, strlen($scriptUrl)); - } elseif($baseUrl === '' || strpos($pathInfo, $baseUrl) === 0) { - $pathInfo = substr($pathInfo, strlen($baseUrl)); - } elseif(isset( $_SERVER[ 'PHP_SELF' ] ) && strpos($_SERVER[ 'PHP_SELF' ], $scriptUrl) === 0) { - $pathInfo = substr($_SERVER[ 'PHP_SELF' ], strlen($scriptUrl)); - } else { - throw new InvalidConfigException('Unable to determine the path info of the current request.'); - } - - if($pathInfo === '/') { - $pathInfo = substr($pathInfo, 1); - } - - return (string) $pathInfo; - } - } \ No newline at end of file diff --git a/artbox-language/components/LanguageUrlManager.php b/artbox-language/components/LanguageUrlManager.php deleted file mode 100755 index 9d1e6f0..0000000 --- a/artbox-language/components/LanguageUrlManager.php +++ /dev/null @@ -1,35 +0,0 @@ -url; - } else { - return '/' . $language->url . $url; - } - } - } \ No newline at end of file diff --git a/artbox-language/composer.json b/artbox-language/composer.json deleted file mode 100644 index 69d6345..0000000 --- a/artbox-language/composer.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "artweb/artbox-language", - "description": "Yii2 light-weight CMS", - "license": "BSD-3-Clause", - "require": { - "php": ">=7.0", - "yiisoft/yii2": "*", - "developeruz/yii2-db-rbac": "*" - }, - "autoload": { - "psr-4": { - "artweb\\artbox\\language\\": "" - } - } -} \ No newline at end of file diff --git a/artbox-language/migrations/m160829_104745_create_table_language.php b/artbox-language/migrations/m160829_104745_create_table_language.php deleted file mode 100755 index 051d84e..0000000 --- a/artbox-language/migrations/m160829_104745_create_table_language.php +++ /dev/null @@ -1,35 +0,0 @@ -createTable( - '{{%language}}', - [ - 'id' => $this->primaryKey(), - 'url' => $this->string() - ->notNull(), - 'local' => $this->string() - ->notNull(), - 'name' => $this->string() - ->notNull(), - 'default' => $this->boolean() - ->notNull() - ->defaultValue(false), - 'created_at' => $this->integer() - ->notNull(), - 'updated_at' => $this->integer() - ->notNull(), - ] - ); - } - - public function down() - { - $this->dropTable('{{%language}}'); - } - } diff --git a/artbox-language/migrations/m160829_105345_add_default_languages.php b/artbox-language/migrations/m160829_105345_add_default_languages.php deleted file mode 100755 index d6d3a67..0000000 --- a/artbox-language/migrations/m160829_105345_add_default_languages.php +++ /dev/null @@ -1,56 +0,0 @@ -batchInsert( - '{{%language}}', - [ - 'id', - 'url', - 'local', - 'name', - 'default', - 'created_at', - 'updated_at', - ], - [ - [ - 1, - 'en', - 'en-EN', - 'English', - 0, - time(), - time(), - ], - [ - 2, - 'ru', - 'ru-RU', - 'Русский', - 1, - time(), - time(), - ], - ] - ); - } - - public function down() - { - $this->delete( - '{{%language}}', - [ - 'id' => [ - 1, - 2, - ], - ] - ); - } - } diff --git a/artbox-language/migrations/m160901_140639_add_ukrainian_language.php b/artbox-language/migrations/m160901_140639_add_ukrainian_language.php deleted file mode 100755 index e3c40a5..0000000 --- a/artbox-language/migrations/m160901_140639_add_ukrainian_language.php +++ /dev/null @@ -1,39 +0,0 @@ -batchInsert( - '{{%language}}', - [ - 'id', - 'url', - 'local', - 'name', - 'default', - 'created_at', - 'updated_at', - ], - [ - [ - 3, - 'ua', - 'ua-UA', - 'Українська', - 0, - time(), - time(), - ], - ] - ); - } - - public function down() - { - $this->delete('{{%language}}', [ 'id' => [ 3 ] ]); - } - } diff --git a/artbox-language/migrations/m160927_124151_add_status_column.php b/artbox-language/migrations/m160927_124151_add_status_column.php deleted file mode 100755 index ca47fb7..0000000 --- a/artbox-language/migrations/m160927_124151_add_status_column.php +++ /dev/null @@ -1,19 +0,0 @@ -addColumn('language', 'status', $this->boolean() - ->notNull() - ->defaultValue(false)); - } - - public function down() - { - $this->dropColumn('language', 'status'); - } - } diff --git a/artbox-language/models/Language.php b/artbox-language/models/Language.php deleted file mode 100755 index 640f48e..0000000 --- a/artbox-language/models/Language.php +++ /dev/null @@ -1,177 +0,0 @@ - [ - 'class' => 'yii\behaviors\TimestampBehavior', - 'attributes' => [ - ActiveRecord::EVENT_BEFORE_INSERT => [ - 'created_at', - 'updated_at', - ], - ActiveRecord::EVENT_BEFORE_UPDATE => [ - 'updated_at', - ], - ], - ], - ]; - } - - /** - * @inheritdoc - */ - public function rules() - { - return [ - [ - [ - 'url', - 'local', - 'name', - 'created_at', - 'updated_at', - ], - 'required', - ], - [ - [ 'default' ], - 'boolean', - ], - [ - [ - 'created_at', - 'updated_at', - ], - 'integer', - ], - [ - [ - 'url', - 'local', - 'name', - ], - 'string', - 'max' => 255, - ], - ]; - } - - /** - * @inheritdoc - */ - public function attributeLabels() - { - return [ - 'id' => Yii::t('app', 'Language ID'), - 'url' => Yii::t('app', 'Url'), - 'local' => Yii::t('app', 'Local'), - 'name' => Yii::t('app', 'Name'), - 'default' => Yii::t('app', 'Default'), - 'created_at' => Yii::t('app', 'Date Create'), - 'updated_at' => Yii::t('app', 'Date Update'), - ]; - } - - /** - * Get current language - * - * @return null|Language - */ - public static function getCurrent() - { - if (self::$current === null) { - self::$current = self::getDefaultLanguage(); - } - return self::$current; - } - - /** - * Set current language by Url param - * - * @param null|string $url Language url param - */ - public static function setCurrent($url = null) - { - $language = self::getLanguageByUrl($url); - self::$current = ( $language === null ) ? self::getDefaultLanguage() : $language; - Yii::$app->language = self::$current->local; - } - - /** - * Get default language - * - * @return null|Language - */ - public static function getDefaultLanguage() - { - /** - * @var null|Language $language - */ - $language = self::find() - ->where([ 'default' => true ]) - ->one(); - return $language; - } - - /** - * Get language by Url param - * - * @param null|string $url Language url param - * - * @return null|Language - */ - public static function getLanguageByUrl($url = null) - { - if ($url === null) { - return null; - } else { - /** - * @var null|Language $language - */ - $language = self::find() - ->where([ 'url' => $url ]) - ->one(); - if ($language === null) { - return null; - } else { - return $language; - } - } - } - } diff --git a/artbox-language/readme.txt b/artbox-language/readme.txt deleted file mode 100755 index b1682b6..0000000 --- a/artbox-language/readme.txt +++ /dev/null @@ -1,90 +0,0 @@ -Как включить мультиязычность на сайте: -1. Запускаем миграцию: php yii migrate --migrationPath=common/modules/language/migrations -2. Добавляем в файл конфигурации: -'urlManager' => [ - 'enablePrettyUrl' => true, - 'showScriptName' => false, - 'class'=>'artweb\artbox\language\components\LanguageUrlManager', - 'rules'=>[ - '/' => 'site/index', - '//*'=>'/', - ] -], -3. Добавляем в файл конфигурации: -'request' => [ - 'class' => 'artweb\artbox\language\components\LanguageRequest' -], -4. Добавляем в файл конфигурации: -'language'=>'ru-RU', -'i18n' => [ - 'translations' => [ - '*' => [ - 'class' => 'yii\i18n\PhpMessageSource', - 'basePath' => '@frontend/messages', - 'sourceLanguage' => 'en', - 'fileMap' => [ - ], - ], - ], -], -5. Переводы писать в файл frontend\messages\{language}\app.php, где {language} - нужный язык, например ru. -6. Для вывода на странице сообщения с переводом используем функцию: Yii::t('app', {message}, $params = [], $language = null), - где {message} - нужное сообщение, $params - массив параметров, $language - нужный язык (по умолчанию используется текущий язык). -7. В наличие также виджет переключения языка: LanguagePicker::widget() - - -Как использовать мультиязычность для Active Record: -1. Создаем для таблицы {table} таблицу с языками {table_lang}. -2. Создаем для класса {Table} класс с языками {TableLang}. -3. Подключаеи для класса {Table} поведение LanguageBehavior: -public function behaviors() { - return [ - 'language' => [ - 'class' => LanguageBehavior::className(), - 'objectLang' => {TableLang}::className() // optional, default to {TableLang}::className() - 'ownerKey' => {Table}->id //optional, default to {Table}->primaryKey()[0] - 'langKey' => {TableLang}->table_id //optional, default to {Table}->tableName().'_id' - ], - ]; -} -3.1. PHPDoc для {Table}: - * * From language behavior * - * @property {TableLang} $lang - * @property {TableLang}[] $langs - * @property {TableLang} $objectLang - * @property string $ownerKey - * @property string $langKey - * @property {TableLang}[] $modelLangs - * @property bool $transactionStatus - * @method string getOwnerKey() - * @method void setOwnerKey(string $value) - * @method string getLangKey() - * @method void setLangKey(string $value) - * @method ActiveQuery getLangs() - * @method ActiveQuery getLang( integer $language_id ) - * @method {TableLang}[] generateLangs() - * @method void loadLangs(Request $request) - * @method bool linkLangs() - * @method bool saveLangs() - * @method bool getTransactionStatus() - * * End language behavior * -3.2. Убрать language behavior с наследуемых таблиц от {Table} ({TableSearch}...) -4. Доступные полезные методы: - {Table}->getLangs() - получить все текущие {TableLang} для {Table} проиндексированные по language_id - {Table}->getLang($language_id = NULL) - получить {TableLang} для определенного языка (default: текущий язык) для {Table} - {Table}->generateLangs() - получить массив {TableLang} под каждый язык, включая существующие записи, для {Table} - {Table}->loadLangs($request) - заполнить массив {TableLang} данными с POST - {Table}->linkLangs() - связать каждый элемент массива {TableLang} с текущей {Table} - {Table}->saveLangs() - провалидировать и сохранить каждый элемент массива {TableLang} -5. Добавить поля в форму (к примеру через Bootstrap Tabs). - В наличии: - LanguageForm::widget([ - 'modelLangs' => {TableLang}[], - 'formView' => string, - 'form' => ActiveForm, - ]); -6. Обрабатывать данные в контроллере. - 1. После создания/поиска {Table} создаем/находим языковые модели {Table}->generateLangs() - 2. При POST запросе загружаем данные в языковые модели {Table}->loadLangs(Request $request) - 3. После сохранения, если транзанкция успешна, то свойство {Table}->transactionStatus будет true, иначе возникла ошибка в какой то модели. -7. Получать данные на публичной части сайта через {Table}->lang. diff --git a/artbox-language/widgets/LanguageForm.php b/artbox-language/widgets/LanguageForm.php deleted file mode 100755 index 874b255..0000000 --- a/artbox-language/widgets/LanguageForm.php +++ /dev/null @@ -1,78 +0,0 @@ -formView === NULL) { - throw new InvalidConfigException('Form view must be set'); - } - if(empty( $this->modelLangs ) || !is_array($this->modelLangs)) { - throw new InvalidConfigException('Language models must be passed'); - } - if(empty( $this->getForm() )) { - throw new InvalidConfigException('Form model must be set'); - } - $this->languages = Language::find() - ->where([ 'status' => true ]) - ->orderBy([ 'default' => SORT_DESC ]) - ->indexBy('id') - ->all(); - } - - public function run() - { - return $this->render('language_form_frame', [ - 'languages' => $this->languages, - 'form_view' => $this->formView, - 'modelLangs' => $this->modelLangs, - 'form' => $this->getForm(), - 'idPrefix' => $this->idPrefix, - ]); - } - - public function getForm(): ActiveForm - { - return $this->form; - } - - public function setForm(ActiveForm $value) - { - $this->form = $value; - } - } \ No newline at end of file diff --git a/artbox-language/widgets/LanguagePicker.php b/artbox-language/widgets/LanguagePicker.php deleted file mode 100755 index eefa288..0000000 --- a/artbox-language/widgets/LanguagePicker.php +++ /dev/null @@ -1,29 +0,0 @@ -render('view', [ - 'current' => Language::getCurrent(), - 'languages' => Language::find() - ->where([ - '!=', - 'id', - Language::getCurrent()->id, - ]) - ->all(), - ]); - } - } \ No newline at end of file diff --git a/artbox-language/widgets/views/language_form_frame.php b/artbox-language/widgets/views/language_form_frame.php deleted file mode 100755 index d3b96e5..0000000 --- a/artbox-language/widgets/views/language_form_frame.php +++ /dev/null @@ -1,69 +0,0 @@ - -
      - 1) { - ?> - -
      - $model_lang) { - if(!array_key_exists($lang, $languages)) { - continue; - } - echo Html::tag('div', $this->render($form_view, [ - 'model_lang' => $model_lang, - 'language' => $languages[ $lang ], - 'form' => $form, - ]), [ - 'class' => 'tab-pane' . ( $first ? ' active' : '' ), - 'id' => $idPrefix . '_' . $lang, - ]); - $first = false; - } - ?> -
      - id ] )) { - echo $this->render($form_view, [ - 'model_lang' => $modelLangs[ $language->id ], - 'language' => $language, - 'form' => $form, - ]); - } - } - ?> -
      diff --git a/artbox-language/widgets/views/view.php b/artbox-language/widgets/views/view.php deleted file mode 100755 index 3d0b450..0000000 --- a/artbox-language/widgets/views/view.php +++ /dev/null @@ -1,35 +0,0 @@ - -
      - - name; - ?> - - -
        - -
      • - getRequest(); - echo Html::a($language->name, '/' . $language->url . $request->getLanguageUrl()); - ?> -
      • - -
      -
      diff --git a/artbox-seo/composer.json b/artbox-seo/composer.json deleted file mode 100644 index 15813b5..0000000 --- a/artbox-seo/composer.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "artweb/artbox-seo", - "description": "Yii2 light-weight CMS", - "license": "BSD-3-Clause", - "require": { - "php": ">=7.0", - "yiisoft/yii2": "*", - "developeruz/yii2-db-rbac": "*" - }, - "autoload": { - "psr-4": { - "artweb\\artbox\\seo\\": "" - } - } -} \ No newline at end of file diff --git a/behaviors/FilterBehavior.php b/behaviors/FilterBehavior.php new file mode 100755 index 0000000..6bd7aed --- /dev/null +++ b/behaviors/FilterBehavior.php @@ -0,0 +1,26 @@ +owner; + return $owner->hasMany(TaxOption::className(), [ 'tax_option_id' => 'option_id' ]) + ->viaTable(ProductOption::tableName(), [ 'product_id' => $owner->getTableSchema()->primaryKey[ 0 ] ]) + ->joinWith('taxGroup') + ->all(); + } + + } \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a5453b2 --- /dev/null +++ b/composer.json @@ -0,0 +1,15 @@ +{ + "name": "artweb/artbox-ecommerce", + "description": "Yii2 light-weight CMS", + "license": "BSD-3-Clause", + "require": { + "php": ">=7.0", + "yiisoft/yii2": "*", + "developeruz/yii2-db-rbac": "*" + }, + "autoload": { + "psr-4": { + "artweb\\artbox\\ecommerce\\": "" + } + } +} \ No newline at end of file diff --git a/config.php b/config.php new file mode 100755 index 0000000..3e57f5e --- /dev/null +++ b/config.php @@ -0,0 +1,8 @@ + [ + 'category_group' => 1, + 'brand_group' => 2, + ], + ]; \ No newline at end of file diff --git a/controllers/ManageController.php b/controllers/ManageController.php new file mode 100755 index 0000000..22637d0 --- /dev/null +++ b/controllers/ManageController.php @@ -0,0 +1,407 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => [ 'POST' ], + ], + ], + ]; + } + + /** + * Lists all Product models. + * + * @return mixed + */ + public function actionIndex() + { + $searchModel = new ProductSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render( + 'index', + [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ] + ); + } + + /** + * Displays a single Product model. + * + * @param integer $id + * + * @return mixed + */ + public function actionView($id) + { + $model = $this->findModel($id); + $categories = $model->getCategories() + ->with('lang') + ->all(); + $variants = $model->getVariants() + ->with('lang') + ->all(); + $properties = $model->getProperties(); + return $this->render( + 'view', + [ + 'model' => $this->findModel($id), + 'categories' => $categories, + 'variants' => $variants, + 'properties' => $properties, + ] + ); + } + + /** + * Creates a new Product model. + * If creation is successful, the browser will be redirected to the 'view' page. + * + * @return mixed + */ + public function actionCreate() + { + $model = new Product(); + $model->generateLangs(); + if ($model->load(Yii::$app->request->post())) { + $model->loadLangs(\Yii::$app->request); + if ($model->save() && $model->transactionStatus) { + return $this->redirect( + [ + 'view', + 'id' => $model->id, + ] + ); + } + } + return $this->render( + 'create', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + ] + ); + } + + /** + * Updates an existing Product model. + * If update is successful, the browser will be redirected to the 'view' page. + * + * @param integer $id + * + * @return mixed + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + $model->generateLangs(); + if ($model->load(Yii::$app->request->post())) { + $model->loadLangs(\Yii::$app->request); + if ($model->save() && $model->transactionStatus) { + return $this->redirect( + [ + 'view', + 'id' => $model->id, + ] + ); + } + } + /** + * @var ActiveQuery $groups + */ + $groups = $model->getTaxGroupsByLevel(0); + return $this->render( + 'update', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + 'groups' => $groups, + ] + ); + } + + /** + * Deletes an existing Product model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * + * @param integer $id + * + * @return mixed + */ + public function actionDelete($id) + { + $this->findModel($id) + ->delete(); + return $this->redirect([ 'index' ]); + } + + /** + * Deletes an existing ProductImage model. + * + * @param int $id + */ + public function actionDeleteImage($id) + { + $image = ProductImage::findOne($id); + + if ($image) { + $image->delete(); + } + + print '1'; + exit; + } + + /** + * Toggle product top status + * + * @param int $id Product ID + * + * @return \yii\web\Response + */ + public function actionIsTop($id) + { + $model = $this->findModel($id); + + $model->is_top = intval(empty( $model->is_top )); + + $model->save(false, [ 'is_top' ]); + + return $this->redirect([ 'index' ]); + } + + /** + * Toggle product new status + * + * @param int $id Product ID + * + * @return \yii\web\Response + */ + public function actionIsNew($id) + { + $model = $this->findModel($id); + + $model->is_new = intval(empty( $model->is_new )); + + $model->save(false, [ 'is_new' ]); + + return $this->redirect([ 'index' ]); + } + + /** + * Toggle product discount status + * + * @param int $id Product ID + * + * @return \yii\web\Response + */ + public function actionIsDiscount($id) + { + $model = $this->findModel($id); + + $model->is_discount = intval(empty( $model->is_discount )); + + $model->save(false, [ 'is_discount' ]); + + return $this->redirect([ 'index' ]); + } + + /** + * Perform product import + * + * @return string + */ + public function actionImport() + { + $model = new Import(); + + $languages = Language::find() + ->select( + [ + 'name', + 'id', + ] + ) + ->where([ 'status' => 1 ]) + ->orderBy([ 'default' => SORT_DESC ]) + ->asArray() + ->indexBy('id') + ->column(); + + if ($model->load(Yii::$app->request->post())) { + \Yii::$app->session->set('export_lang', $model->lang); + $file = UploadedFile::getInstances($model, 'file'); + $method = 'go' . ucfirst($model->type); + $target = Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFile' . ucfirst($model->type)); + if (empty( $file )) { + $model->errors[] = 'File not upload'; + } elseif ($method == 'goPrices' && $file[ 0 ]->name != 'file_1.csv') { + $model->errors[] = 'File need "file_1.csv"'; + } elseif ($method == 'goProducts' && $file[ 0 ]->name == 'file_1.csv') { + $model->errors[] = 'File can not "file_1.csv"'; + } elseif ($model->validate() && $file[ 0 ]->saveAs($target)) { + // PROCESS PAGE + return $this->render( + 'import-process', + [ + 'model' => $model, + 'method' => $model->type, + 'target' => $target, + ] + ); + } else { + $model->errors[] = 'File can not be upload or other error'; + } + } + + return $this->render( + 'import', + [ + 'model' => $model, + 'languages' => $languages, + ] + ); + } + + /** + * Import products via AJAX + * + * @return array + * @throws \HttpRequestException + */ + public function actionProducts() + { + $from = Yii::$app->request->get('from', 0); + + $model = new Import(); + + if (Yii::$app->request->isAjax) { + Yii::$app->response->format = Response::FORMAT_JSON; + return $model->goProducts($from, 1); + } else { + throw new \HttpRequestException('Must be AJAX'); + } + } + + /** + * Import prices via AJAX + * + * @return array + * @throws \HttpRequestException + */ + public function actionPrices() + { + $from = Yii::$app->request->get('from', 0); + + $model = new Import(); + + if (Yii::$app->request->isAjax) { + Yii::$app->response->format = Response::FORMAT_JSON; + return $model->goPrices($from, 10); + } else { + throw new \HttpRequestException('Must be AJAX'); + } + } + + /** + * Export proccess via AJAX + * + * @param int $from + * @param string $filename + * + * @return array + * @throws \HttpRequestException + */ + public function actionExportProcess($from, $filename) + { + + $model = new Export(); + if (Yii::$app->request->isAjax) { + Yii::$app->response->format = Response::FORMAT_JSON; + return $model->process($filename, $from); + } else { + throw new \HttpRequestException('Must be AJAX'); + } + } + + /** + * Perform export + * + * @return string + */ + public function actionExport() + { + $model = new Export(); + + if ($model->load(Yii::$app->request->post())) { + \Yii::$app->session->set('export_lang', $model->lang); + return $this->render( + 'export-process', + [ + 'model' => $model, + 'method' => 'export', + ] + ); + } + + return $this->render( + 'export', + [ + 'model' => $model, + ] + ); + } + + /** + * Finds the Product model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * + * @param integer $id + * + * @return Product the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (( $model = Product::find() + ->where([ 'id' => $id ]) + ->with('lang') + ->one() ) !== null + ) { + return $model; + } else { + throw new NotFoundHttpException('The requested page does not exist.'); + } + } + } diff --git a/controllers/ProductUnitController.php b/controllers/ProductUnitController.php new file mode 100755 index 0000000..61f8313 --- /dev/null +++ b/controllers/ProductUnitController.php @@ -0,0 +1,168 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => [ 'POST' ], + ], + ], + ]; + } + + /** + * Lists all ProductUnit models. + * + * @return mixed + */ + public function actionIndex() + { + $searchModel = new ProductUnitSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render( + 'index', + [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ] + ); + } + + /** + * Displays a single ProductUnit model. + * + * @param integer $id + * + * @return mixed + */ + public function actionView($id) + { + return $this->render( + 'view', + [ + 'model' => $this->findModel($id), + ] + ); + } + + /** + * Creates a new ProductUnit model. + * If creation is successful, the browser will be redirected to the 'view' page. + * + * @return mixed + */ + public function actionCreate() + { + $model = new ProductUnit(); + $model->generateLangs(); + if ($model->load(Yii::$app->request->post())) { + $model->loadLangs(\Yii::$app->request); + if ($model->save() && $model->transactionStatus) { + return $this->redirect( + [ + 'view', + 'id' => $model->id, + ] + ); + } + } + return $this->render( + 'create', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + ] + ); + } + + /** + * Updates an existing ProductUnit model. + * If update is successful, the browser will be redirected to the 'view' page. + * + * @param integer $id + * + * @return mixed + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + $model->generateLangs(); + if ($model->load(Yii::$app->request->post())) { + $model->loadLangs(\Yii::$app->request); + if ($model->save() && $model->transactionStatus) { + return $this->redirect( + [ + 'view', + 'id' => $model->id, + ] + ); + } + } + return $this->render( + 'update', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + ] + ); + } + + /** + * Deletes an existing ProductUnit model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * + * @param integer $id + * + * @return mixed + */ + public function actionDelete($id) + { + $this->findModel($id) + ->delete(); + + return $this->redirect([ 'index' ]); + } + + /** + * Finds the ProductUnit model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * + * @param integer $id + * + * @return ProductUnit the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (( $model = ProductUnit::find() + ->where([ 'id' => $id ]) + ->with('lang') + ->one() ) !== null + ) { + return $model; + } else { + throw new NotFoundHttpException('The requested page does not exist.'); + } + } + } diff --git a/controllers/TaxGroupController.php b/controllers/TaxGroupController.php new file mode 100755 index 0000000..58ec043 --- /dev/null +++ b/controllers/TaxGroupController.php @@ -0,0 +1,172 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => [ 'POST' ], + ], + ], + ]; + } + + /** + * Lists all TaxGroup models. + * + * @param $level integer + * + * @return mixed + */ + public function actionIndex($level) + { + $searchModel = new TaxGroupSearch(); + $dataProvider = $searchModel->search(\Yii::$app->request->queryParams, $level); + /** + * @var ActiveQuery $query + */ + $query = $dataProvider->query; + $query->with('options') + ->with('categories'); + + return $this->render( + 'index', + [ + 'dataProvider' => $dataProvider, + 'searchModel' => $searchModel, + 'level' => $level, + ] + ); + } + + /** + * Creates a new TaxGroup model. + * If creation is successful, the browser will be redirected to the 'view' page. + * + * @param $level integer + * + * @return mixed + */ + public function actionCreate($level) + { + $model = new TaxGroup(); + $model->generateLangs(); + if ($model->load(Yii::$app->request->post()) && $model->validate()) { + $model->loadLangs(\Yii::$app->request); + $model->level = $level; + if ($model->save() && $model->transactionStatus) { + return $this->redirect( + [ + 'index', + 'level' => $level, + ] + ); + } + } + return $this->render( + 'create', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + ] + ); + } + + /** + * Updates an existing TaxGroup model. + * If update is successful, the browser will be redirected to the 'view' page. + * + * @param $level integer + * @param integer $id + * + * @return mixed + */ + public function actionUpdate($level, $id) + { + $model = $this->findModel($id); + $model->generateLangs(); + if ($model->load(Yii::$app->request->post())) { + $model->loadLangs(\Yii::$app->request); + if ($model->save() && $model->transactionStatus) { + return $this->redirect( + [ + 'index', + 'level' => $level, + ] + ); + } + } + return $this->render( + 'update', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + 'level' => $level, + ] + ); + } + + /** + * Deletes an existing TaxGroup model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * + * @param $level integer + * @param integer $id + * + * @return mixed + */ + public function actionDelete($level, $id) + { + $this->findModel($id) + ->delete(); + return $this->redirect( + [ + 'index', + 'level' => $level, + ] + ); + } + + /** + * Finds the TaxGroup model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * + * @param integer $id + * + * @return TaxGroup the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (( $model = TaxGroup::find() + ->with('lang') + ->where([ 'id' => $id ]) + ->one() ) !== null + ) { + return $model; + } else { + throw new NotFoundHttpException('The requested page does not exist.'); + } + } + } diff --git a/controllers/TaxOptionController.php b/controllers/TaxOptionController.php new file mode 100755 index 0000000..cd00b1f --- /dev/null +++ b/controllers/TaxOptionController.php @@ -0,0 +1,198 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => [ 'POST' ], + ], + ], + ]; + } + + /** + * Lists all TaxOption models. + * + * @return mixed + */ + public function actionIndex() + { + $group = $this->findGroup(Yii::$app->request->queryParams[ 'group' ]); + $searchModel = new TaxOptionSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + /** + * @var ActiveQuery $query + */ + $query = $dataProvider->query; + $query->andWhere([ 'tax_group_id' => $group->id ]); + if ($group->level) { + $query->with('productVariants'); + } else { + $query->with('products'); + } + return $this->render( + 'index', + [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + 'group' => $group, + ] + ); + } + + /** + * Creates a new TaxOption model. + * If creation is successful, the browser will be redirected to the 'view' page. + * + * @return mixed + */ + public function actionCreate() + { + $group = $this->findGroup(Yii::$app->request->queryParams[ 'group' ]); + $model = new TaxOption( + [ + 'tax_group_id' => $group->id, + ] + ); + $model->generateLangs(); + if ($model->load(Yii::$app->request->post())) { + $model->loadLangs(\Yii::$app->request); + if ($model->save() && $model->transactionStatus) { + return is_null(Yii::$app->request->post('create_and_new')) ? $this->redirect( + [ + 'index', + 'group' => $group->id, + ] + ) : $this->redirect(array_merge([ 'create' ], Yii::$app->request->queryParams)); + } + } + return $this->render( + 'create', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + 'group' => $group, + ] + ); + } + + /** + * Updates an existing TaxOption model. + * If update is successful, the browser will be redirected to the 'view' page. + * + * @param string $id + * + * @return mixed + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + $group = $this->findGroup($model->tax_group_id); + $model->generateLangs(); + if ($model->load(Yii::$app->request->post())) { + $model->loadLangs(\Yii::$app->request); + if ($model->save() && $model->transactionStatus) { + return $this->redirect( + [ + 'index', + 'group' => $group->id, + ] + ); + } + } + return $this->render( + 'update', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + 'group' => $group, + ] + ); + } + + /** + * Deletes an existing TaxOption model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * + * @param string $id + * + * @return mixed + */ + public function actionDelete($id) + { + $model = $this->findModel($id); + $group_id = $model->tax_group_id; + + $model->delete(); + + return $this->redirect( + [ + 'index', + 'group' => $group_id, + ] + ); + } + + /** + * Finds the TaxOption model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * + * @param string $id + * + * @return TaxOption the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (( $model = TaxOption::find() + ->with('lang') + ->where([ 'id' => $id ]) + ->one() ) !== null + ) { + return $model; + } else { + throw new NotFoundHttpException('The requested page does not exist.'); + } + } + + /** + * @param int $id + * + * @return null|TaxGroup + * @throws NotFoundHttpException + */ + protected function findGroup($id) + { + if (( $model = TaxGroup::find() + ->with('lang') + ->where([ 'id' => $id ]) + ->one() ) !== null + ) { + return $model; + } else { + throw new NotFoundHttpException('The requested page does not exist.'); + } + } + } diff --git a/controllers/VariantController.php b/controllers/VariantController.php new file mode 100755 index 0000000..4d326e3 --- /dev/null +++ b/controllers/VariantController.php @@ -0,0 +1,315 @@ + [ + 'class' => VerbFilter::className(), + 'actions' => [ + 'delete' => [ 'POST' ], + ], + ], + ]; + } + + /** + * Lists all ProductVariant models. + * + * @param int $product_id + * + * @return mixed + */ + public function actionIndex($product_id) + { + $product = $this->findProduct($product_id); + $searchModel = new ProductVariantSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + /** + * @var ActiveQuery $query + */ + $query = $dataProvider->query; + $query->with('image') + ->andWhere([ 'product_id' => $product->id ]); + + return $this->render( + 'index', + [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + 'product' => $product, + ] + ); + } + + /** + * Displays a single ProductVariant model. + * + * @param integer $id + * + * @return mixed + */ + public function actionView($id) + { + $model = $this->findModel($id); + $properties = $model->getProperties(); + return $this->render( + 'view', + [ + 'model' => $model, + 'properties' => $properties, + ] + ); + } + + /** + * Creates a new ProductVariant model. + * If creation is successful, the browser will be redirected to the 'view' page. + * + * @param int $product_id + * + * @return mixed + */ + public function actionCreate($product_id) + { + $product = $this->findProduct($product_id); + $model = new ProductVariant(); + $model->product_id = $product->id; + $model->generateLangs(); + if ($model->load(Yii::$app->request->post())) { + $model->loadLangs(\Yii::$app->request); + if ($model->save() && $model->transactionStatus) { + $model->stock = $this->saveStocks($model, Yii::$app->request->post('ProductStock', [])); + if ($model->save(true, [ 'stock' ]) && $model->transactionStatus) { + return $this->redirect( + [ + 'index', + 'product_id' => $product->id, + ] + ); + } + } + } + $groups = $model->getTaxGroupsByLevel(1); + return $this->render( + 'create', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + 'groups' => $groups, + 'stocks' => [ new ProductStock() ], + 'product' => $product, + ] + ); + } + + /** + * Updates an existing ProductVariant model. + * If update is successful, the browser will be redirected to the 'view' page. + * + * @param integer $product_id + * @param integer $id + * + * @return mixed + */ + public function actionUpdate($product_id, $id) + { + $product = $this->findProduct($product_id); + $model = $this->findModel($id); + $model->generateLangs(); + if ($model->load(Yii::$app->request->post())) { + $model->loadLangs(\Yii::$app->request); + if ($model->save() && $model->transactionStatus) { + $model->stock = $this->saveStocks($model, Yii::$app->request->post('ProductStock', [])); + if ($model->save(true, [ 'stock' ]) && $model->transactionStatus) { + return $this->redirect( + [ + 'index', + 'product_id' => $product_id, + ] + ); + } + } + } + $groups = $model->getTaxGroupsByLevel(1); + return $this->render( + 'update', + [ + 'model' => $model, + 'modelLangs' => $model->modelLangs, + 'groups' => $groups, + 'stocks' => ( !empty( $model->variantStocks ) ) ? $model->variantStocks : [ new ProductStock ], + 'product' => $product, + ] + ); + } + + /** + * Deletes an existing ProductVariant model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * + * @param integer $product_id + * @param integer $id + * + * @return mixed + */ + public function actionDelete($product_id, $id) + { + + $this->findModel($id) + ->delete(); + + return $this->redirect( + [ + 'index', + 'product_id' => $product_id, + ] + ); + } + + /** + * Deletes an existing ProductImage model. + * + * @param $id + */ + public function actionDeleteImage($id) + { + $image = ProductImage::findOne($id); + + if ($image) { + $image->delete(); + } + + print '1'; + exit; + } + + /** + * Save ProductStocks for ProductVariant and return total count of products. + * + * @param \artweb\artbox\ecommerce\models\ProductVariant $productVariant + * @param array|null $productStocks + * + * @return int + */ + protected function saveStocks(ProductVariant $productVariant, array $productStocks = null) + { + $total_quantity = 0; + if (!empty( $productStocks )) { + $productVariant->unlinkAll('stocks', true); + $sorted_array = []; + foreach ($productStocks as $productStock) { + if (!empty( $productStock[ 'title' ] ) && !empty( $productStock[ 'quantity' ] )) { + if (!empty( $sorted_array[ $productStock[ 'title' ] ] )) { + $sorted_array[ $productStock[ 'title' ] ] += $productStock[ 'quantity' ]; + } else { + $sorted_array[ $productStock[ 'title' ] ] = $productStock[ 'quantity' ]; + } + } + } + $productStocks = $sorted_array; + $stock_names = array_keys($productStocks); + $stocks = Stock::find() + ->joinWith('lang') + ->where([ 'stock_lang.title' => $stock_names ]) + ->indexBy(function($row) { + /** + * @var Stock $row + */ + return $row->lang->title; + }) + ->all(); + foreach ($productStocks as $stockName => $quantity) { + $quantity = (int) $quantity; + if (!array_key_exists($stockName, $stocks)) { + $stock = new Stock(); + $stock->generateLangs(); + foreach ($stock->modelLangs as $modelLang) { + $modelLang->title = $stockName; + } + if (!$stock->save() || !$stock->transactionStatus) { + continue; + } + } else { + $stock = $stocks[ $stockName ]; + } + $psModel = new ProductStock( + [ + 'product_variant_id' => $productVariant->id, + 'stock_id' => $stock->id, + 'quantity' => $quantity, + ] + ); + if ($psModel->save()) { + $total_quantity += $quantity; + } + } + } else { + $productVariant->unlinkAll('stocks', true); + } + return $total_quantity; + } + + /** + * Finds the ProductVariant model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * + * @param integer $id + * + * @return ProductVariant the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (( $model = ProductVariant::find() + ->where([ 'id' => $id ]) + ->with('lang') + ->one() ) !== null + ) { + return $model; + } else { + throw new NotFoundHttpException('The requested page does not exist.'); + } + } + + /** + * @param int $product_id + * + * @return Product + * @throws NotFoundHttpException + */ + protected function findProduct($product_id) + { + if (( $model = Product::find() + ->with('lang') + ->where([ 'id' => $product_id ]) + ->one() ) !== null + ) { + return $model; + } else { + throw new NotFoundHttpException('The requested page does not exist.'); + } + } + } diff --git a/helpers/FilterHelper.php b/helpers/FilterHelper.php new file mode 100755 index 0000000..5113145 --- /dev/null +++ b/helpers/FilterHelper.php @@ -0,0 +1,313 @@ +joinWith('lang') + ->where([ 'is_filter' => 'TRUE' ]) + ->all(), + 'lang.alias' + ); + } else { + return static::$optionsList; + } + + } + + /** + * Return custom filter-option link + * + * @param array $filter + * @param string $key + * @param mixed $value + * @param bool $remove + * + * @return array + */ + public static function getFilterForOption(array $filter, string $key, $value, bool $remove = false) + { + + $optionsTemplate = self::optionsTemplate(); + array_unshift($optionsTemplate, "special", "brands"); + + $result = $filter; + + if (is_array($value)) { + foreach ($value as $value_key => $value_items) { + if (!is_array($value_items)) { + $value_items = [ $value_items ]; + } + foreach ($value_items as $value_item) { + if ($remove && isset( $result[ $key ] ) && ( $i = array_search( + $value_item, + $result[ $key ][ $value_key ] + ) ) !== false + ) { + unset( $result[ $key ][ $value_key ][ $i ] ); + if (empty( $result[ $key ][ $value_key ] )) { + unset( $result[ $key ][ $value_key ] ); + } + } else { + if (!isset( $result[ $key ][ $value_key ] ) || array_search( + $value_item, + $result[ $key ][ $value_key ] + ) === false + ) { + $result[ $key ][ $value_key ][] = $value_item; + } + } + } + } + } else { + if ($remove && isset( $result[ $key ] ) && ( $i = array_search($value, $result[ $key ]) ) !== false) { + unset( $result[ $key ][ $i ] ); + if (empty( $result[ $key ] )) { + unset( $result[ $key ] ); + } + } else { + if (!isset( $result[ $key ] ) || array_search($value, $result[ $key ]) === false) { + $result[ $key ][] = $value; + } + } + } + + $filterView = []; + + foreach ($optionsTemplate as $optionKey) { + if (isset( $result[ $optionKey ] )) { + $filterView[ $optionKey ] = $result[ $optionKey ]; + } + + } + + return $filterView; + } + + /** + * Fill query with filter conditions + * + * @param ActiveQuery $query + * @param array $params + */ + public static function setQueryParams(ActiveQuery $query, array $params) + { + $last_query = null; + foreach ($params as $key => $param) { + switch ($key) { + case 'special': + self::filterSpecial($param, $query); + break; + case 'brands': + self::filterBrands($param, $query); + break; + case 'keywords': + self::filterKeywords($param, $query); + break; + case 'prices': + self::filterPrices($param, $query); + break; + default: + $last_query = self::filterOptions($param, $last_query); + break; + } + } + // If tax option filters were provided filter query with them + if (!empty( $last_query )) { + $query->andWhere([ 'product.id' => $last_query ]); + } + } + + /** + * Tax Option filter + * + * @param string[] $params + * @param \yii\db\Query|null $last_query + * + * @return Query + */ + private static function filterOptions(array $params, Query $last_query = null): Query + { + $variant_query = ( new Query() )->distinct() + ->select('product_variant.product_id as products') + ->from('product_variant_option') + ->innerJoin( + 'product_variant', + 'product_variant_option.product_variant_id = product_variant.id' + ) + ->innerJoin( + 'tax_option', + 'tax_option.id = product_variant_option.option_id' + ) + ->innerJoin( + 'tax_option_lang', + 'tax_option_lang.tax_option_id = tax_option.id' + ) + ->where([ 'tax_option_lang.alias' => $params ]); + $product_query = ( new Query() )->distinct() + ->select('product_option.product_id as products') + ->from('product_option') + ->innerJoin('tax_option', 'product_option.option_id = tax_option.id') + ->innerJoin( + 'tax_option_lang', + 'tax_option_lang.tax_option_id = tax_option.id' + ) + ->where( + [ 'tax_option_lang.alias' => $params ] + ) + ->union($variant_query); + $query = ( new Query() )->select('products') + ->from([ 'result_table' => $product_query ]); + if (!empty( $last_query )) { + $query->andWhere([ 'product.id' => $last_query ]); + } + return $query; + } + + /** + * Fill $query with special filters (used in Product) + * + * @param array $params + * @param \yii\db\ActiveQuery $query + */ + private static function filterSpecial(array $params, ActiveQuery $query) + { + $conditions = []; + /** + * @var string $key + */ + foreach ($params as $key => $param) { + $conditions[] = [ + '=', + Product::tableName() . '.' . $key, + $param, + ]; + } + /* If 2 or more special conditions get all that satisfy at least one of them. */ + if (count($conditions) > 1) { + array_unshift($conditions, 'or'); + } else { + $conditions = $conditions[ 0 ]; + } + $query->andFilterWhere($conditions); + } + + /** + * Fill query with brands filter + * + * @param int[] $param + * @param \yii\db\ActiveQuery $query + */ + private static function filterBrands(array $param, ActiveQuery $query) + { + $query->andFilterWhere([ Product::tableName() . '.brand_id' => $param ]); + } + + /** + * Fill query with keywords filter + * + * @param array $params + * @param \yii\db\ActiveQuery $query + */ + private static function filterKeywords(array $params, ActiveQuery $query) + { + $conditions = []; + if (!empty( $params )) { + if (!is_array($params)) { + $params = [ $params ]; + } + /** + * @var string $param Inputed keyword + */ + foreach ($params as $param) { + $conditions[] = [ + 'or', + [ + 'ilike', + ProductLang::tableName() . '.title', + $param, + ], + [ + 'ilike', + BrandLang::tableName() . '.title', + $param, + ], + [ + 'ilike', + CategoryLang::tableName() . '.title', + $param, + ], + [ + 'ilike', + ProductVariantLang::tableName() . '.title', + $param, + ], + ]; + } + } + if (count($conditions) > 1) { + array_unshift($conditions, 'or'); + } else { + $conditions = $conditions[ 0 ]; + } + $query->andFilterWhere($conditions); + } + + /** + * Fill query with price limits filter + * + * @param array $params + * @param \yii\db\ActiveQuery $query + */ + private static function filterPrices(array $params, ActiveQuery $query) + { + $conditions = []; + if (!empty( $params[ 'min' ] ) && $params[ 'min' ] > 0) { + $conditions[] = [ + '>=', + ProductVariant::tableName() . '.price', + $params[ 'min' ], + ]; + } + if (!empty( $params[ 'max' ] ) && $params[ 'max' ] > 0) { + $conditions[] = [ + '<=', + ProductVariant::tableName() . '.price', + $params[ 'max' ], + ]; + } + if (count($conditions) > 1) { + array_unshift($conditions, 'and'); + } else { + $conditions = $conditions[ 0 ]; + } + $query->andFilterWhere($conditions); + } + + } + \ No newline at end of file diff --git a/helpers/ProductHelper.php b/helpers/ProductHelper.php new file mode 100755 index 0000000..2962c6a --- /dev/null +++ b/helpers/ProductHelper.php @@ -0,0 +1,184 @@ +getTree(null, 'lang'); + } + + /** + * Add $product_id to last products in session. Limit 16 products. + * + * @param int $product_id + */ + public static function addLastProducts(int $product_id) + { + $last_products = self::getLastProducts(); + if (!in_array($product_id, $last_products)) { + $last_products[] = intval($product_id); + if (count($last_products) > 16) { + array_shift($last_products); + } + Yii::$app->session->set('last_products', $last_products); + } + } + + /** + * Get last products ids from session or last Product models with ProductVariant, which are in stock if + * $as_object is true + * + * @param bool $as_object + * + * @return array + */ + public static function getLastProducts(bool $as_object = false) + { + $last_products = Yii::$app->session->get('last_products', []); + if ($as_object) { + $last_products = Product::find() + ->joinWith([ 'variant' ]) + ->where([ 'product.id' => $last_products ]) + ->andWhere( + [ + '!=', + 'product_variant.stock', + 0, + ] + ) + ->indexBy('id') + ->all(); + } + return array_reverse($last_products); + } + + /** + * Get special Products array with ProductVariants, which are in stock + * Available types: + * * top + * * new + * * promo + * + * @param string $type + * @param int $count + * + * @return Product[] + */ + public static function getSpecialProducts(string $type, int $count) + { + switch ($type) { + case 'top': + $data = [ 'is_top' => true ]; + break; + case 'new': + $data = [ 'is_new' => true ]; + break; + case 'promo': + $data = [ 'is_discount' => true ]; + break; + default: + return []; + break; + } + return Product::find() + ->with('lang') + ->joinWith('variants.lang') + ->where($data) + ->andWhere( + [ + '!=', + 'productVariant.stock', + 0, + ] + ) + ->limit($count) + ->all(); + } + + /** + * Get ActiveQuery to get similar products to $product + * + * @param Product $product + * @param int $count + * + * @return ActiveQuery + */ + public static function getSimilarProducts(Product $product, $count = 10): ActiveQuery + { + $query = Product::find(); + if (empty( $product->properties )) { + $query->where('0 = 1'); + return $query; + } + $query->innerJoinWith('variants') + ->joinWith('categories') + ->where( + [ + '!=', + 'product_variant.stock', + 0, + ] + ) + ->andWhere( + [ 'product_category.category_id' => ArrayHelper::getColumn($product->categories, 'id') ] + ); + $options = []; + foreach ($product->properties as $group) { + foreach ($group->options as $option) { + $options[] = $option->id; + } + } + if (!empty( $options )) { + $query->innerJoinWith('options') + ->andWhere([ 'product_option.option_id' => $options ]); + } else { + $query->where('0 = 1'); + return $query; + } + $query->andWhere( + [ + '!=', + 'product.id', + $product->id, + ] + ); + $query->groupBy('product.id'); + $query->limit($count); + return $query; + } + + /** + * Add last category id to session + * + * @param int $category_id + */ + public static function addLastCategory(int $category_id) + { + \Yii::$app->session->set('last_category_id', $category_id); + } + + /** + * Get last category id from session + * + * @return int + */ + public static function getLastCategory(): int + { + return \Yii::$app->session->get('last_category_id'); + } + } \ No newline at end of file diff --git a/models/Brand.php b/models/Brand.php new file mode 100755 index 0000000..bd4d78c --- /dev/null +++ b/models/Brand.php @@ -0,0 +1,108 @@ + SaveImgBehavior::className(), + 'fields' => [ + [ + 'name' => 'image', + 'directory' => 'brand', + ], + ], + ], + 'language' => [ + 'class' => LanguageBehavior::className(), + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'in_menu' ], + 'boolean', + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('product', 'Brand ID'), + 'image' => Yii::t('product', 'Image'), + ]; + } + + /** + * Get all products with current brand + * + * @return \yii\db\ActiveQuery + */ + public function getProducts() + { + return $this->hasMany(Product::className(), [ 'brand_id' => 'id' ]); + } + } diff --git a/models/BrandLang.php b/models/BrandLang.php new file mode 100755 index 0000000..36fcdff --- /dev/null +++ b/models/BrandLang.php @@ -0,0 +1,137 @@ + [ + 'class' => 'common\behaviors\Slug', + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'title' ], + 'required', + ], + [ + [ 'seo_text' ], + 'string', + ], + [ + [ + 'title', + 'meta_title', + 'meta_robots', + 'meta_description', + 'alias', + ], + 'string', + 'max' => 255, + ], + [ + [ + 'brand_id', + 'language_id', + ], + 'unique', + 'targetAttribute' => [ + 'brand_id', + 'language_id', + ], + 'message' => 'The combination of Brand ID and Language ID has already been taken.', + ], + [ + [ 'brand_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Brand::className(), + 'targetAttribute' => [ 'brand_id' => 'id' ], + ], + [ + [ 'language_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Language::className(), + 'targetAttribute' => [ 'language_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'brand_id' => Yii::t('app', 'Brand ID'), + 'language_id' => Yii::t('app', 'Language ID'), + 'title' => Yii::t('app', 'Name'), + 'meta_title' => Yii::t('app', 'Meta Title'), + 'meta_robots' => Yii::t('app', 'Meta Robots'), + 'meta_description' => Yii::t('app', 'Meta Desc'), + 'seo_text' => Yii::t('app', 'Seo Text'), + 'alias' => Yii::t('app', 'Alias'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getBrand() + { + return $this->hasOne(Brand::className(), [ 'id' => 'brand_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getLanguage() + { + return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); + } + } diff --git a/models/BrandSearch.php b/models/BrandSearch.php new file mode 100755 index 0000000..6aaa8d0 --- /dev/null +++ b/models/BrandSearch.php @@ -0,0 +1,104 @@ +joinWith('lang'); + + // add conditions that should always apply here + + $dataProvider = new ActiveDataProvider( + [ + 'query' => $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; + }*/ + + $dataProvider->setSort( + [ + 'attributes' => [ + 'id', + 'brandName' => [ + 'asc' => [ 'brand_lang.title' => SORT_ASC ], + 'desc' => [ 'brand_lang.title' => SORT_DESC ], + ], + ], + ] + ); + + // grid filtering conditions + $query->andFilterWhere( + [ + 'brand.id' => $this->id, + ] + ) + ->andFilterWhere( + [ + 'ilike', + 'brand_lang.title', + $this->brandName, + ] + ); + + return $dataProvider; + } + } diff --git a/models/Category.php b/models/Category.php new file mode 100755 index 0000000..c4eb1cc --- /dev/null +++ b/models/Category.php @@ -0,0 +1,313 @@ + [ + 'class' => ArtboxTreeBehavior::className(), + 'keyNameGroup' => null, + 'keyNamePath' => 'path', + ], + 'language' => [ + 'class' => LanguageBehavior::className(), + ], + [ + 'class' => SaveImgBehavior::className(), + 'fields' => [ + [ + 'name' => 'image', + 'directory' => 'categories', + ], + ], + ], + ]; + } + + /** + * @inheritdoc + */ + public static function tableName() + { + return 'category'; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ + 'parent_id', + 'depth', + 'product_unit_id', + ], + 'integer', + ], + [ + [ + 'path', + ], + 'string', + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('product', 'Category ID'), + 'parent_id' => Yii::t('product', 'Parent ID'), + 'path' => Yii::t('product', 'Path'), + 'depth' => Yii::t('product', 'Depth'), + 'image' => Yii::t('product', 'Image'), + 'imageUrl' => Yii::t('product', 'Image'), + 'product_unit_id' => Yii::t('product', 'Product Unit ID'), + 'remote_id' => Yii::t('product', 'Remote ID'), + ]; + } + + public static function find() + { + return new CategoryQuery(get_called_class()); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductUnit() + { + return $this->hasOne(ProductUnit::className(), [ 'id' => 'product_unit_id' ]); + } + + /** + * @return ActiveQuery + */ + public function getProducts() + { + return $this->hasMany(Product::className(), [ 'id' => 'product_id' ]) + ->viaTable('product_category', [ 'category_id' => 'id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductCategories() + { + return $this->hasMany(ProductCategory::className(), [ 'category_id' => 'id' ]); + } + + /** + * Get all brands for Category query + * + * @return ActiveQuery + */ + public function getBrands() + { + return $this->hasMany(Brand::className(), [ 'id' => 'brand_id' ]) + ->via('products'); + } + + /** + * Get Tax Groups by level + * * 0 for Product + * * 1 for ProductVariant + * + * @param int $level + * + * @return ActiveQuery + */ + public function getTaxGroupsByLevel(int $level) + { + if ($level !== 0 || $level !== 1) { + throw new InvalidParamException('Level supports only 0 and 1 values'); + } + return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) + ->viaTable('tax_group_to_category', [ 'category_id' => 'id' ]) + ->andWhere([ 'level' => $level ]); + } + + /** + * Леша найди путь как убрать это, мб в базу записать просто по умолчанию значение и notnull + * + * @param bool $insert + * + * @return bool + */ + public function beforeSave($insert) + { + if (parent::beforeSave($insert)) { + + if (empty( $this->parent_id )) { + $this->parent_id = 0; + } + + return true; + } + return false; + } + + /** + * Get query for activefilter for current category + * + * @return Query + */ + public function getActiveFilters() + { + $language_id = Language::getCurrent()->id; + $query1 = ( new Query() )->distinct() + ->select( + [ + 'option_id', + ] + ) + ->from('tax_option') + ->innerJoin( + 'product_variant_option', + 'tax_option.id = product_variant_option.option_id' + ) + ->innerJoin('tax_group', 'tax_group.id = tax_option.tax_group_id') + ->innerJoin( + 'product_variant', + 'product_variant.id = product_variant_option.product_variant_id' + ) + ->innerJoin('product', 'product.id = product_variant.product_id') + ->innerJoin('product_category', 'product_category.product_id = product.id') + ->where( + [ + 'product_category.category_id' => $this->id, + 'tax_group.is_filter' => true, + ] + ) + ->andWhere( + [ + '!=', + 'product_variant.stock', + 0, + ] + ); + + $query2 = ( new Query() )->distinct() + ->select( + [ + 'option_id', + ] + ) + ->from('tax_option') + ->innerJoin( + 'product_option', + 'tax_option.id = product_option.option_id' + ) + ->innerJoin('tax_group', 'tax_group.id = tax_option.tax_group_id') + ->innerJoin('product', 'product.id = product_option.product_id') + ->innerJoin('product_category', 'product_category.product_id = product.id') + ->innerJoin('product_variant', 'product_variant.product_id = product.id') + ->where( + [ + 'product_category.category_id' => $this->id, + 'tax_group.is_filter' => true, + ] + ) + ->andWhere( + [ + '!=', + 'product_variant.stock', + 0, + ] + ); + $query3 = ( new Query() )->select( + [ + 'tax_option.*', + 'tax_option_lang.alias as option_alias', + 'tax_group_lang.alias as group_alias', + 'tax_option_lang.value as value', + 'tax_option.sort AS tax_option_sort', + 'tax_group.sort AS tax_group_sort', + ] + ) + ->from([ 'tax_option' ]) + ->where([ 'tax_option.id' => $query1->union($query2) ]) + ->innerJoin('tax_group', 'tax_group.id = tax_option.tax_group_id') + ->innerJoin('tax_option_lang', 'tax_option.id = tax_option_lang.tax_option_id') + ->innerJoin('tax_group_lang', 'tax_group.id = tax_group_lang.tax_group_id') + ->andWhere([ 'tax_option_lang.language_id' => $language_id ]) + ->andWhere([ 'tax_group_lang.language_id' => $language_id ]) + ->orderBy('tax_option.sort, tax_group.sort'); + return $query3; + } + + /** + * Get query to get all TaxGroup for current Category + * + * @return ActiveQuery + */ + public function getTaxGroups() + { + return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) + ->viaTable('tax_group_to_category', [ 'category_id' => 'id' ]); + } + } diff --git a/models/CategoryLang.php b/models/CategoryLang.php new file mode 100755 index 0000000..3a10105 --- /dev/null +++ b/models/CategoryLang.php @@ -0,0 +1,139 @@ + [ + 'class' => 'common\behaviors\Slug', + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'title' ], + 'required', + ], + [ + [ + 'seo_text', + 'alias', + ], + 'string', + ], + [ + [ + 'title', + 'meta_title', + 'meta_robots', + 'meta_description', + 'h1', + ], + 'string', + 'max' => 255, + ], + [ + [ + 'category_id', + 'language_id', + ], + 'unique', + 'targetAttribute' => [ + 'category_id', + 'language_id', + ], + 'message' => 'The combination of Category ID and Language ID has already been taken.', + ], + [ + [ 'category_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Category::className(), + 'targetAttribute' => [ 'category_id' => 'id' ], + ], + [ + [ 'language_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Language::className(), + 'targetAttribute' => [ 'language_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'category_id' => Yii::t('app', 'Category ID'), + 'language_id' => Yii::t('app', 'Language ID'), + 'title' => Yii::t('app', 'Name'), + 'meta_title' => Yii::t('app', 'Meta Title'), + 'meta_robots' => Yii::t('app', 'Meta Robots'), + 'meta_description' => Yii::t('app', 'Meta Desc'), + 'seo_text' => Yii::t('app', 'Seo Text'), + 'h1' => Yii::t('app', 'H1'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getCategory() + { + return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getLanguage() + { + return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); + } + } diff --git a/models/CategoryQuery.php b/models/CategoryQuery.php new file mode 100755 index 0000000..ed0b7d4 --- /dev/null +++ b/models/CategoryQuery.php @@ -0,0 +1,34 @@ +joinWith('lang'); + + $dataProvider = new ActiveDataProvider( + [ + 'query' => $query, + 'sort' => false, + ] + ); + + $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( + [ + 'category.id' => $this->id, + ] + ) + ->andFilterWhere( + [ + 'ilike', + 'category_lang.title', + $this->categoryName, + ] + ); + + $query->orderBy( + [ + 'category.path' => SORT_ASC, + 'category.depth' => SORT_ASC, + 'category.id' => SORT_ASC, + ] + ); + + return $dataProvider; + } + } diff --git a/models/Export.php b/models/Export.php new file mode 100755 index 0000000..2aa0dd4 --- /dev/null +++ b/models/Export.php @@ -0,0 +1,207 @@ + Language::getCurrent()->id, + ], + ]; + } + + /** + * Perform product export + * + * @param null|string $filename Export csv file name + * @param int $from Product start + * + * @return array + */ + public function process($filename = null, $from = 0) + { + $limit = 100; + + if (empty( $filename )) { + $filename = 'products_' . date('d_m_Y_H_i') . '.csv'; + $handle = fopen(\Yii::getAlias('@storage/sync/') . $filename, "w"); + } else { + $handle = fopen(\Yii::getAlias('@storage/sync/') . $filename, "a"); + } + + $language = Language::findOne(\Yii::$app->session->get('export_lang', Language::getDefaultLanguage()->id)); + Language::setCurrent($language->url); + + /** + * @var Product[] $products + */ + $products = Product::find() + ->with('variantsWithFilters', 'brand.lang', 'categories.lang', 'filters', 'images') + ->joinWith('lang', true, 'INNER JOIN') + ->limit($limit) + ->offset($from) + ->all(); + $filesize = Product::find() + ->joinWith('lang', true, 'INNER JOIN') + ->count(); + foreach ($products as $product) { + $mods = []; + $filterString = $this->convertFilterToString($product->filters); + + foreach ($product->variantsWithFilters as $variant) { + /** + * @var ProductVariant $variant + */ + $color = $variant->lang->title; + $mods[] = $variant->sku . $this->generateID( + $variant->remote_id + ) . '=' . $this->convertFilterToString( + $variant->filters + ) . '=' . $color . '=' . ( ( !empty( $variant->image ) ) ? $variant->image->image : '' ) . '=' . $variant->stock; + } + + $fotos = []; + foreach ($product->images as $image) { + $fotos[] = $image->image; + } + + $categories = []; + foreach ($product->categories as $value) { + $categories[] = $value->lang->title . $this->generateID($value->remote_id); + } + + $categories = implode(',', $categories); + + $list = [ + $categories, + //A - категории через запятую Название(remote_id) + ( ( !empty( $product->brand ) ) ? $product->brand->lang->title . $this->generateID( + $product->brand->remote_id + ) : '' ), + //B - бренд Название(remote_id) + $product->lang->title . $this->generateID($product->remote_id), + //C - название товара Название(remote_id) + ( ( !empty( $product->lang->description ) ) ? $product->lang->description : '' ), + //D - описание товара Описание(remote_id) + $filterString, + //E - характеристики товара. Структура: [Группа1(remote_id):Характеристика11(remote_id),Характеристика12(remote_id)]*[Группа2(remote_id):Характеристика21(remote_id),Характеристика22(remote_id)] + ( !empty( $product->variant ) ) ? $product->variant->price_old : '', + //F - страрая цена + ( !empty( $product->variant ) ) ? $product->variant->price : '', + //G - новая цена + intval($product->is_discount), + //H - товар акционный (1/0) + '', + //I - пустой + intval($product->is_new), + //J - товар новинка + intval($product->is_top), + //K - товар в топе + $product->video, + //L - ссылка на видео (iframe) + implode(',', $fotos), + //M - название файлов через запятую, картинки должны хранится в /storage/sync/product_images + // Все последующие модификации: SKU(remote_id)=[Группа1(remote_id):Характеристика11(remote_id),Характеристика12(remote_id)]*[Группа2(remote_id):Характеристика21(remote_id),Характеристика22(remote_id)]=Название=Изображение=Остаток + ]; + $to_write = array_merge($list, $mods); + fputcsv($handle, $to_write, ';'); + unset( $product ); + } + + fclose($handle); + + $from += $limit; + $end = false; + if ($from > $filesize) { + $end = true; + } + + $result = [ + 'end' => $end, + 'from' => $from, + 'totalsize' => $filesize, + 'filename' => $filename, + ]; + + if ($end) { + $result = array_merge( + $result, + [ + 'link' => '/storage/sync/' . $filename, + ] + ); + } + + return $result; + } + + /** + * Stringify filters for export + * * Result: [filterName1:filterValue11,filterValue12]*[filterName2:filterValue21,filterValue22] + * + * @param $filters + * + * @return string + */ + public function convertFilterToString($filters) + { + $filtersArray = []; + /** + * @var TaxOption[] $filters + */ + foreach ($filters as $filter) { + $filtersArray[ $filter->taxGroup->lang->title . $this->generateID( + $filter->taxGroup->remote_id + ) ][] = $filter->lang->value . $this->generateID($filter->remote_id); + } + $filterString = []; + + foreach ($filtersArray as $filterName => $filterRows) { + $row = implode(',', $filterRows); + $filterString[] = "[{$filterName}:{$row}]"; + } + return implode('*', $filterString); + } + + /** + * Generate remote ID string + * + * @param string $id Remote ID + * + * @return string Formatted remote ID + */ + private function generateID(string $id):string + { + return sprintf('(#%s#)', $id); + } + } + \ No newline at end of file diff --git a/models/Import.php b/models/Import.php new file mode 100755 index 0000000..7662973 --- /dev/null +++ b/models/Import.php @@ -0,0 +1,841 @@ + 'csv', + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'file' => Yii::t('product', 'File'), + ]; + } + + /** + * Get import type + * + * @see Import::type + * @return string + */ + public function getType() + { + if (!$this->type) { + $this->type = 'products'; + } + return $this->type; + } + + /** + * Import prices + * + * @param int $from Start row + * @param null $limit Row limit + * + * @return array|bool Array if OK, false otherwise + */ + public function goPrices($from = 0, $limit = null) + { + set_time_limit(0); + + if (!( $handle = $this->getProductsFile('uploadFilePrices') )) { + $this->errors[] = 'File not found'; + return false; + } + + $filesize = filesize(Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFilePrices')); + if ($from) { + fseek($handle, $from); + } + + $j = 0; + + $is_utf = ( preg_match( + '//u', + file_get_contents( + Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFilePrices'), + null, + null, + null, + 1000000 + ) + ) ); + + while (( empty( $limit ) || $j++ < $limit ) && ( $data = fgetcsv($handle, 10000, ";") ) !== false) { + foreach ($data as &$value) { + if (!$is_utf) { + $value = iconv('windows-1251', "UTF-8//TRANSLIT//IGNORE", $value); + } + $value = trim($value); + } + + // данные строк + $modification_code = @$data[ 0 ]; + $price = floatval(@$data[ 1 ]); + $price_promo = floatval(@$data[ 2 ]); + $count = intval(@$data[ 3 ]); + $city_name = @$data[ 4 ]; + $product_title = @$data[ 5 ]; + + if (empty ( $modification_code )) { + continue; + } + // товары в пути + if (empty ( $city_name )) { + $this->output[] = 'Товар ' . $product_title . ' в пути'; + continue; + } + /** + * @var ProductVariant $productVariant + */ + if (( $productVariant = ProductVariant::find() + ->filterWhere([ 'sku' => $modification_code ]) + ->one() ) === null + ) { + $this->output[] = 'Для товара ' . $product_title . ' не найдено соотвествие'; + continue; + } + // ===== Set stock ==== + if ($city_name) { + if (( $stock = Stock::find() + ->filterWhere([ 'title' => trim($city_name) ]) + ->one() ) === null + ) { + // Create stock + $stock = new Stock(); + $stock->title = trim($city_name); + $stock->save(false); + } + + $productStock = ProductStock::find() + ->where( + [ + 'product_variant_id' => $productVariant->id, + 'stock_id' => $stock->id, + ] + ) + ->one(); + if (!$productStock instanceof ProductStock) { + $productStock = new ProductStock(); + $productStock->product_variant_id = $productVariant->id; + $productStock->stock_id = $stock->id; + } + $productStock->quantity = $count; + + $productStock->save(false); + $productStocks = ProductStock::find() + ->where( + [ 'product_variant_id' => $productVariant->id ] + ) + ->andWhere( + [ + '<>', + 'stock_id', + $stock->id, + ] + ) + ->all(); + + $quantity = array_sum(ArrayHelper::getColumn($productStocks, 'quantity')) + $count; + } else { + + $productStocks = ProductStock::find() + ->where( + [ 'product_variant_id' => $productVariant->id ] + ) + ->all(); + + if ($productStocks instanceof ProductStock) { + $quantity = array_sum(ArrayHelper::getColumn($productStocks, 'quantity')) + $count; + } else { + $quantity = 0; + } + + } + + if ($price_promo) { + $productVariant->price_old = $price; + $productVariant->price = $price_promo; + } else { + $productVariant->price = $price; + $productVariant->price_old = $price_promo; + } + + $productVariant->stock = $quantity; + + $productVariant->save(false); + + $this->output[] = 'Товар ' . $product_title . ' успешно сохранен'; + } + + $result = [ + 'end' => feof($handle), + 'from' => ftell($handle), + 'totalsize' => $filesize, + 'items' => $this->output, + + ]; + + fclose($handle); + + if ($result[ 'end' ]) { + unlink(Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFilePrices')); + } + + return $result; + } + + /** + * Pull name and remote_id from formatted string + * + * @param string $name + * + * @return array + */ + private function parseName(string $name):array + { + $pattern = '/^(?P.*)(?:\(#(?P\w+)#\))?$/U'; + $name = trim($name); + $matches = []; + if (preg_match($pattern, $name, $matches)) { + if (!isset( $matches[ 'remote_id' ] )) { + $matches[ 'remote_id' ] = ''; + } + return $matches; + } + return [ + 'name' => $name, + 'remote_id' => '', + ]; + } + + /** + * Save categories + * + * @param array $catalog_names + * + * @return int[] Category IDs + * @throws \Exception + */ + private function saveCatalog(array $catalog_names):array + { + $category_id = []; + + foreach ($catalog_names as $catalog_name) { + // ==== Set category ==== + $parsed_name = $this->parseName($catalog_name); + if (!empty( $parsed_name[ 'remote_id' ] ) && ( $category = Category::find() + ->joinWith('lang') + ->andFilterWhere( + [ 'remote_id' => $parsed_name[ 'remote_id' ] ] + ) + ->one() ) !== null + ) { + if (!empty( $category->lang )) { + $category->lang->title = $parsed_name[ 'name' ]; + $category->lang->save(false); + } else { + throw new \Exception( + 'Category with ID ' . $category->id . ' and lang ' . Language::getCurrent( + )->id . ' doesn\'t exist' + ); + } + + } else { + // Create category + $category = new Category(); + $category->generateLangs(); + $category_langs = $category->modelLangs; + foreach ($category_langs as $category_lang) { + $category_lang->title = $parsed_name[ 'name' ]; + } + $category->remote_id = $parsed_name[ 'remote_id' ]; + $category->save(false); + } + $category_id[] = $category->id; + } + return $category_id; + } + + /** + * Save brand + * + * @param string|null $brand_name + * + * @return int|null New Brand ID if inserted or exist or null if skipped + * @throws \Exception + */ + private function saveBrand(string $brand_name = null):int + { + + $parsed_name = $this->parseName($brand_name); + if (!empty( $brand_name )) { + /** + * @var Brand $brand + */ + if (!empty( $parsed_name[ 'remote_id' ] ) && ( $brand = Brand::find() + ->joinWith('lang') + ->andFilterWhere( + [ 'remote_id' => $parsed_name[ 'remote_id' ] ] + ) + ->one() ) !== null + ) { + if (!empty( $brand->lang )) { + $brand->lang->title = $parsed_name[ 'name' ]; + $brand->lang->save(false); + } else { + throw new \Exception( + 'Brand with ID ' . $brand->id . ' and lang ' . Language::getCurrent( + )->id . ' doesn\'t exist' + ); + } + return $brand->id; + } else { + // Create brand + $brand = new Brand(); + $brand->generateLangs(); + $brand_langs = $brand->modelLangs; + foreach ($brand_langs as $brand_lang) { + $brand_lang->title = $parsed_name[ 'name' ]; + } + $brand->remote_id = $parsed_name[ 'remote_id' ]; + $brand->save(false); + return $brand->id; + } + } + return null; + } + + /** + * Save Product or ProductVariant photoes + * + * @param string[] $fotos Photoes names + * @param int $product_id + * @param int|null $product_variant_id Null if photo for Product + */ + private function saveFotos(array $fotos, int $product_id, int $product_variant_id = null) + { + if (!empty( $fotos )) { + foreach ($fotos as $foto) { + if (empty( $foto )) { + continue; + } + $source_image = Yii::getAlias('@uploadDir') . '/product_images/' . urlencode($foto); + if (file_exists($source_image)) { + if (( $productImage = ProductImage::find() + ->andWhere([ 'image' => $foto ]) + ->andWhere([ 'product_id' => $product_id ]) + ->andFilterWhere( + [ 'product_variant_id' => $product_variant_id ] + ) + ->one() ) === null + ) { + copy($source_image, Yii::getAlias('@productsDir') . "/" . $foto); + $productImage = new ProductImage(); + $productImage->product_id = $product_id; + $productImage->product_variant_id = $product_variant_id; + $productImage->image = $foto; + $productImage->save(false); + } + } + } + } + } + + /** + * Save ProductVariants + * + * @param array $data ProductVariats data + * @param float $product_cost_old Old price + * @param int $product_id Product ID + * @param array $category_id Ca + * @param float|null $product_cost + * + * @return int[] Array of ProductVariants IDs + * @throws \Exception + */ + private function saveVariants( + array $data, + float $product_cost_old, + int $product_id, + array $category_id, + float $product_cost = null + ):array + { + $MOD_ARRAY = []; + for ($i = 13; $i < count($data); $i++) { + if (!empty ( $data[ $i ] )) { + $mod_arr = explode('=', $data[ $i ]); + $mod_art = $mod_arr[ 0 ]; + $mod_art_parsed = $this->parseName($mod_art); + $variant_filters = explode('*', $mod_arr[ 1 ]); + $mod_name = $mod_arr[ 2 ]; + if (empty( $mod_name )) { + $mod_name = $mod_art_parsed[ 'name' ]; + } + $mod_image = $mod_arr[ 3 ]; + $mod_stock = isset( $mod_arr[ 4 ] ) ? $mod_arr[ 4 ] : 1; + $mod_cost = isset( $product_cost ) ? floatval($product_cost) : 0; + $mod_old_cost = floatval($product_cost_old); + // Check product variant + /** + * @var ProductVariant $_productVariant + */ + if (( $_productVariant = ProductVariant::find() + ->joinWith('lang') + ->andFilterWhere( + [ 'remote_id' => $mod_art_parsed[ 'remote_id' ] ] + ) + ->andFilterWhere( + [ 'product_variant.product_id' => $product_id ] + ) + ->one() ) === null + ) { + $_productVariant = new ProductVariant(); + $_productVariant->product_id = $product_id; + if (!empty( $mod_art_parsed[ 'remote_id' ] )) { + $_productVariant->remote_id = $mod_art_parsed[ 'remote_id' ]; + } + $_productVariant->generateLangs(); + $product_variant_langs = $_productVariant->modelLangs; + foreach ($product_variant_langs as $product_variant_lang) { + $product_variant_lang->title = $mod_name; + } + } else { + if (!empty( $_productVariant->lang )) { + $_productVariant->lang->title = $mod_name; + $_productVariant->lang->save(false); + } else { + throw new \Exception( + 'Product variant with ID ' . $_productVariant->id . ' and lang ' . Language::getCurrent( + )->id . ' doesn\'t exist' + ); + } + } + $_productVariant->product_unit_id = 1; + $_productVariant->sku = $mod_art_parsed[ 'name' ]; + $_productVariant->price = $mod_cost; + $_productVariant->price_old = $mod_old_cost; + $_productVariant->stock = $mod_stock; + + if (!empty ( $variant_filters )) { + $variants_options = $this->saveFilters($variant_filters, 1, $category_id); + } + + if (isset( $variants_options ) && !empty( $variants_options )) { + $_productVariant->options = $variants_options; + } + + /** + * @todo set to false + */ + $_productVariant->save(false); + + $MOD_ARRAY[] = $_productVariant->id; + $this->saveFotos([ $mod_image ], $product_id, $_productVariant->id); + } + } + return $MOD_ARRAY; + } + + /** + * Perform product import + * + * @param int $from Begin row + * @param null $limit Row limit + * + * @return array|bool Array if OK, false if error + */ + public function goProducts($from = 0, $limit = null) + { + set_time_limit(0); + + if (!( $handle = $this->getProductsFile('uploadFileProducts') )) { + $this->errors[] = 'File not found'; + return false; + } + + $filesize = filesize(Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFileProducts')); + + if ($from) { + fseek($handle, $from); + } + + $j = 0; + + $is_utf = ( preg_match( + '//u', + file_get_contents( + Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFileProducts'), + null, + null, + null, + 1000000 + ) + ) ); + + $result_items = []; + + while (( empty( $limit ) || $j++ < $limit ) && ( $data = fgetcsv($handle, 10000, ";") ) !== false) { + try { + + foreach ($data as &$value) { + if (!$is_utf) { + $value = iconv('windows-1251', "UTF-8//TRANSLIT//IGNORE", $value); + } + $value = trim($value); + } + // будет всегда 19 элементов + for ($i = 0; $i <= 18; $i++) { + if (!isset ( $data[ $i ] )) { + $data[ $i ] = null; + } + } + // 1 Группа (категория) + $catalog_names = explode(',', $data[ 0 ]); + if (empty ( $catalog_names )) { + $result_items[] = "Не указана категория (строка $j)"; + continue; + } + + // 2 Бренд + $brand_name = $data[ 1 ]; + // if(empty ( $brand_name )) { + // $result_items[] = "Не указан бренд (строка $j)"; + // continue; + // } + + // 3 Название товара + $product_name = $data[ 2 ]; + if (empty ( $product_name )) { + $result_items[] = "Не указано наименование товара (строка $j)"; + continue; + } + + // 5 Описание товара + $product_body = $data[ 3 ]; + + // 6 Фильтр + $filters = explode('*', $data[ 4 ]); + + // 11 Цена акция + $product_cost_old = floatval($data[ 6 ]); + + $product_cost = null; + // 10 Цена + if ($product_cost_old) { + $product_cost_old = floatval($data[ 5 ]); + $product_cost = floatval($data[ 6 ]); + } + + // 12 Акция + $product_discount = (bool) $data[ 7 ]; + + // 13 Сопуд. Тов. + $similar = explode(',', $data[ 8 ]); + + // 14 Новинки + $product_new = (bool) $data[ 9 ]; + + // 15 Топ продаж + $product_top = (bool) $data[ 10 ]; + + // 17 ВИДЕО КОД + $product_video = $data[ 11 ]; + + // 18 Галлерея фото + $fotos = []; + if (trim($data[ 12 ])) { + $fotos = explode(',', trim($data[ 12 ])); + } + + // $lang = \Yii::$app->session->get('export_lang', Language::getDefaultLanguage()->id); + // /** + // * @var Language $language + // */ + // $language = Language::find() + // ->where([ 'id' => $lang ]) + // ->one(); + // Language::setCurrent($language->url); + $categories = $this->saveCatalog($catalog_names); + + $brand_id = $this->saveBrand($brand_name); + + $options = []; + if (!empty ( $filters )) { + $options = $this->saveFilters($filters, 0, $categories); + } + $parsed_name = $this->parseName($product_name); + /** + * @var Product $_product + */ + if (!empty( $parsed_name[ 'remote_id' ] ) && ( $_product = Product::find() + ->joinWith('lang') + ->andFilterWhere( + [ 'remote_id' => $parsed_name[ 'remote_id' ] ] + ) + ->one() ) !== null + ) { + if (!empty( $_product->lang )) { + $_product->lang->title = $parsed_name[ 'name' ]; + $_product->lang->description = $product_body; + $_product->lang->save(false); + } else { + throw new \Exception( + 'Product with ID ' . $_product->id . ' and lang ' . Language::getCurrent( + )->id . ' doesn\'t exist' + ); + } + } else { + $_product = new Product(); + $_product->generateLangs(); + $product_langs = $_product->modelLangs; + foreach ($product_langs as $product_lang) { + $product_lang->title = $parsed_name[ 'name' ]; + $product_lang->description = $product_body; + } + } + + $is_new_product = empty( $_product->id ); + + $_product->categories = $categories; + + $_product->brand_id = $brand_id; + + $_product->video = $product_video; + $_product->is_top = $product_top; + $_product->is_discount = $product_discount; + $_product->is_new = $product_new; + if (!empty( $options )) { + $_product->options = $options; + } + + if (!empty( $_product->lang )) { + $product_name_inserted = $_product->lang->title; + } else { + $product_name_inserted = $_product->modelLangs[ Language::$current->id ]->title; + } + + if (( $_product->save(false) === false ) || !$_product->transactionStatus) { + $result_items[] = 'Product #' . $product_name_inserted . ' not saved' . " (line $j)"; + continue; + } + + $this->saveFotos($fotos, $_product->id); + // нужно для проставления характеристик относящихся к модификациям + + $this->saveVariants($data, $product_cost_old, $_product->id, $_product->categories, $product_cost); + + // $_product->save(false); + + $result_items[] = "Product {$product_name_inserted} #{$_product->id} saved (" . ( $is_new_product ? 'new product' : 'exists product' ) . ")" . " (line $j)"; + + } catch (\Exception $e) { + $result_items[] = $e->getMessage() . '(line ' . $j . ')'; + } + + } + + $result = [ + 'end' => feof($handle), + 'from' => ftell($handle), + 'totalsize' => $filesize, + 'items' => $result_items, + ]; + + fclose($handle); + + if ($result[ 'end' ]) { + // unlink(Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@uploadFileProducts')); + } + + return $result; + } + + /** + * Get import file + * + * @param string $file_type + * + * @return bool|resource false if File not found and file resource if OK + */ + private function getProductsFile($file_type) + { + $filename = Yii::getAlias('@uploadDir') . '/' . Yii::getAlias('@' . $file_type); + if (!is_file($filename)) { + $this->errors[] = "File $filename not found"; + return false; + } + return fopen($filename, 'r'); + } + + /** + * Save filters + * + * @param array $filters array of filters like [['pol'='мужской'],['god' = + * '2013'],['volume'='25 л']*['size'='49 x 30 x + * 20см'],['composition'='600D полиэстер']] + * @param int $level 0 for products and 1 for product variant + * @param int[] $catalog_names array catalogs id + * + * @return array + * @throws \Exception + */ + private function saveFilters(array $filters, int $level, array $catalog_names):array + { + $options = []; + foreach ($filters as $filter) { + + preg_match_all('/\[(.*):(.*)\]/', $filter, $filter); + + if (empty( $filter[ 1 ][ 0 ] )) { + continue; + } + $filter_name = trim($filter[ 1 ][ 0 ]); + $parsed_group_name = $this->parseName($filter_name); + + /** + * @var TaxGroup $taxGroup + */ + if (!empty( $parsed_group_name[ 'remote_id' ] ) && ( $taxGroup = TaxGroup::find() + ->joinWith('lang') + ->andFilterWhere( + [ 'remote_id' => $parsed_group_name[ 'remote_id' ] ] + ) + ->one() ) !== null + ) { + if (!empty( $taxGroup->lang )) { + $taxGroup->lang->title = $parsed_group_name[ 'name' ]; + $taxGroup->lang->save(false); + } else { + throw new \Exception( + 'Tax group with ID ' . $taxGroup->id . ' and lang ' . Language::getCurrent( + )->id . ' doesn\'t exist' + ); + } + } else { + $taxGroup = new TaxGroup(); + $taxGroup->generateLangs(); + $tax_group_langs = $taxGroup->modelLangs; + foreach ($tax_group_langs as $tax_group_lang) { + $tax_group_lang->title = $parsed_group_name[ 'name' ]; + } + $taxGroup->level = $level; + $taxGroup->categories = $catalog_names; + $taxGroup->is_filter = false; + $taxGroup->save(false); + } + $filters_options = explode(',', $filter[ 2 ][ 0 ]); + foreach ($filters_options as $filter_options) { + $parsed_option_name = $this->parseName($filter_options); + /** + * @var TaxOption $option + */ + + if (!empty( $parsed_option_name[ 'remote_id' ] ) && ( $option = TaxOption::find() + ->joinWith('lang') + ->andFilterWhere( + [ 'remote_id' => $parsed_option_name[ 'remote_id' ] ] + ) + ->andFilterWhere( + [ 'tax_group_id' => $taxGroup->id ] + ) + ->one() ) !== null + ) { + if (!empty( $option->lang )) { + $option->lang->value = $parsed_option_name[ 'name' ]; + $option->lang->save(false); + } else { + throw new \Exception( + 'Tax option with ID ' . $option->id . ' and lang ' . Language::getCurrent( + )->id . ' doesn\'t exist' + ); + } + } else { + // Create option + $option = new TaxOption(); + $option->generateLangs(); + $option_langs = $option->modelLangs; + foreach ($option_langs as $option_lang) { + $option_lang->value = $parsed_option_name[ 'name' ]; + } + $option->id = $taxGroup->id; + $option->save(false); + } + $options[] = $option->id; + } + } + return $options; + } + } + \ No newline at end of file diff --git a/models/Product.php b/models/Product.php new file mode 100755 index 0000000..bae90fe --- /dev/null +++ b/models/Product.php @@ -0,0 +1,614 @@ + [ + 'class' => SaveMultipleFileBehavior::className(), + 'name' => 'imagesUpload', + 'directory' => 'products', + 'column' => 'image', + 'links' => [ + 'id' => 'product_id', + ], + 'model' => ProductImage::className(), + ], + 'multipleImage' => [ + 'class' => MultipleImgBehavior::className(), + 'links' => [ + 'product_id' => 'id', + ], + 'conditions' => [ + 'product_variant_id' => null, + ], + 'model' => ProductImage::className(), + 'config' => [ + 'caption' => 'image', + 'delete_action' => '/product/manage/delete-image', + 'id' => 'id', + ], + ], + 'language' => [ + 'class' => LanguageBehavior::className(), + ], + 'defaultVariant' => DefaultVariantBehavior::className(), + ]; + } + + /** + * @inheritdoc + */ + public static function tableName() + { + return 'product'; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'brand_id' ], + 'integer', + ], + [ + [ + 'categories', + 'variants', + 'options', + 'imagesUpload', + ], + 'safe', + ], + [ + [ + 'video', + ], + 'safe', + ], + [ + [ + 'is_top', + 'is_new', + 'is_discount', + ], + 'boolean', + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('product', 'ID'), + 'brand_id' => Yii::t('product', 'Brand'), + 'categories' => Yii::t('product', 'Categories'), + 'category' => Yii::t('product', 'Category'), + 'image' => Yii::t('product', 'Image'), + 'images' => Yii::t('product', 'Images'), + 'video' => Yii::t('product', 'Video embeded'), + 'variants' => Yii::t('product', 'Variants'), + 'is_top' => Yii::t('product', 'Is top'), + 'is_new' => Yii::t('product', 'Is new'), + 'is_discount' => Yii::t('product', 'Is promo'), + ]; + } + + /** + * Get Brand query to current Product + * + * @return \yii\db\ActiveQuery + */ + public function getBrand() + { + return $this->hasOne(Brand::className(), [ 'id' => 'brand_id' ]); + } + + /** + * Get ProductVariant query to current Product + * + * @return \yii\db\ActiveQuery + */ + public function getVariant() + { + return $this->hasOne(ProductVariant::className(), [ 'product_id' => 'id' ]); + } + + /** + * Synonim of getVariant() + * + * @see Product::getVariant() + * @return \yii\db\ActiveQuery + */ + public function getProductVariant() + { + return $this->getVariant(); + } + + /** + * Get ProductVariants query to current Product + * + * @return \yii\db\ActiveQuery + */ + public function getVariants() + { + return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]); + } + + /** + * Synonim of getVariants() + * + * @see Product::getVariants() + * @return \yii\db\ActiveQuery + */ + public function getProductVariants() + { + return $this->getVariant(); + } + + /** + * Get ProductVariant query fetching only available in stock to current Product + * + * @see Product::getVariant() + * @return \yii\db\ActiveQuery + */ + public function getEnabledVariant() + { + return $this->hasOne(ProductVariant::className(), [ 'product_id' => 'id' ]) + ->andWhere( + [ + '!=', + ProductVariant::tableName() . '.stock', + 0, + ] + ); + } + + /** + * Get ProductVariants query fetching only available in stock to current Product + * + * @see Product::getVariants() + * @return \yii\db\ActiveQuery + */ + public function getEnabledVariants() + { + return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]) + ->andWhere( + [ + '!=', + ProductVariant::tableName() . '.stock', + 0, + ] + ); + } + + /** + * Get random ProductVariant price or 0 if not exist + * + * @param bool $exception Whether to throw exception if variant not exist + * + * @return float + * @throws \yii\web\NotFoundHttpException + */ + public function getVariantPrice(bool $exception = false): float + { + if (!empty( $this->variant )) { + return $this->variant->price; + } elseif ($exception) { + throw new NotFoundHttpException('Product with ID ' . $this->id . ' hasn\'t got variants'); + } else { + return 0; + } + } + + /** + * Get random ProductVariant that in stock price or 0 or exception if not exist + * + * @param bool $exception Whether to throw exception if variant not exist + * + * @return float + * @throws \yii\web\NotFoundHttpException + */ + public function getEnabledVariantPrice(bool $exception = false): float + { + if (!empty( $this->enabledVariant )) { + return $this->enabledVariant->price; + } elseif ($exception) { + throw new NotFoundHttpException('Product with ID ' . $this->id . ' hasn\'t got enabled variants'); + } else { + return 0; + } + } + + /** + * Get Product name concatenated with Brand name + * + * @return string + */ + public function getFullname():string + { + return empty( $this->brand ) ? $this->lang->title : $this->brand->lang->title . ' ' . $this->lang->title; + } + + /** + * Get Category query for current Product + * + * @return ActiveQuery + */ + public function getCategory() + { + return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]) + ->viaTable('product_category', [ 'product_id' => 'id' ]); + } + + /** + * Get Categories query for current Product + * + * @return ActiveQuery + */ + public function getCategories() + { + return $this->hasMany(Category::className(), [ 'id' => 'category_id' ]) + ->viaTable('product_category', [ 'product_id' => 'id' ]); + } + + /** + * @param bool $index + * + * @return array + */ + public function getCategoryNames(bool $index = false): array + { + if ($index) { + $result = ArrayHelper::map($this->categories, 'id', 'lang.title'); + } else { + $result = ArrayHelper::getColumn($this->categories, 'lang.title'); + } + return $result; + } + + /** + * Get ProductVariants query with lang, filters and image for current Product + * + * @return ActiveQuery + */ + public function getVariantsWithFilters() + { + return $this->hasMany(ProductVariant::className(), [ 'product_id' => 'id' ]) + ->joinWith('lang') + ->with( + [ + 'filters', + 'image', + ] + ); + } + + /** + * Get TaxOptions query for current Product + * + * @return ActiveQuery + */ + public function getOptions() + { + return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) + ->viaTable('product_option', [ 'product_id' => 'id' ]); + } + + /** + * Get TaxOptions query for current Product joined with TaxGroups + * + * @see Product::getOptions() + * @return ActiveQuery + */ + public function getFilters() + { + return $this->getOptions() + ->joinWith('taxGroup.lang') + ->joinWith('lang'); + } + + /** + * Get all TaxGroups for current Product filled with $customOptions that satisfy current Product + * + * @return TaxGroup[] + */ + public function getProperties(): array + { + $groups = $options = []; + foreach ($this->getOptions() + ->with('lang') + ->all() as $option) { + /** + * @var TaxOption $option + */ + $options[ $option[ 'tax_group_id' ] ][] = $option; + } + foreach (TaxGroup::find() + ->where([ 'id' => array_keys($options) ]) + ->with('lang') + ->all() as $group) { + /** + * @var TaxGroup $group + */ + if (!empty( $options[ $group->id ] )) { + $group->customOptions = $options[ $group->id ]; + $groups[] = $group; + } + } + return $groups; + } + + /** + * Get Stock query where current Product is in stock + * + * @return ActiveQuery + */ + public function getStocks() + { + return $this->hasMany(Stock::className(), [ 'id' => 'stock_id' ]) + ->via('productStocks'); + } + + /** + * Get ProductStocks query for current Product + * + * @return ActiveQuery + */ + public function getProductStocks() + { + return $this->hasMany(ProductStock::className(), [ 'product_variant_id' => 'id' ]) + ->via('variants'); + } + + /** + * Get quantity of all ProductVariants for current Product + * + * @see Product::getProductStocks() + * @return int + */ + public function getQuantity():int + { + return $this->getProductStocks() + ->sum('quantity'); + } + + /** + * Override Categories and TaxOptions + * + * @inheritdoc + */ + public function afterSave($insert, $changedAttributes) + { + parent::afterSave($insert, $changedAttributes); + + if (!empty( $this->categories )) { + $categories = Category::findAll($this->categories); + $this->unlinkAll('categories', true); + foreach ($categories as $category) { + $this->link('categories', $category); + } + } + + if (!empty( $this->options )) { + $options = TaxOption::findAll($this->options); + $this->unlinkAll('options', true); + foreach ($options as $option) { + $this->link('options', $option); + } + } + } + + /** + * Recalculate rating for artboxcomment module + * + * @todo Rewrite with behavior + * @return bool + */ + public function recalculateRating():bool + { + /** + * @var ProductToRating $averageRating + */ + $average = $this->getComments() + ->joinWith('rating') + ->select([ 'average' => 'avg(artbox_comment_rating.value)::float' ]) + ->scalar(); + if (!$average) { + $average = 0; + } + $averageRating = $this->averageRating; + if (!empty( $averageRating )) { + $averageRating->value = $average; + } else { + $averageRating = new ProductToRating( + [ + 'product_id' => $this->id, + 'value' => $average, + ] + ); + } + if ($averageRating->save()) { + return true; + } else { + return false; + } + } + + /** + * Get CommmentModel query for artboxcomment module + * + * @todo Rewrite with behavior + * @return ActiveQuery + */ + public function getComments() + { + return $this->hasMany(CommentModel::className(), [ 'entity_id' => 'id' ]) + ->where( + [ + 'artbox_comment.entity' => self::className(), + 'artbox_comment.status' => CommentModel::STATUS_ACTIVE, + 'artbox_comment.artbox_comment_pid' => null, + ] + ); + } + + /** + * Get ProductToRating query in order to get average rating for current Product + * + * @return \yii\db\ActiveQuery + */ + public function getAverageRating() + { + return $this->hasOne(ProductToRating::className(), [ 'product_id' => 'id' ]); + } + + /** + * Get TaxGroupToCategories query via product_category table + * + * @return ActiveQuery + */ + public function getCategoriesToGroups() + { + return $this->hasMany(TaxGroupToCategory::className(), [ 'category_id' => 'category_id' ]) + ->viaTable('product_category', [ 'product_id' => 'id' ]); + } + + /** + * Get TaxGroups query for current Product according to level + * * 0 - Product Tax Groups + * * 1 - ProductVariant Tax Groups + * + * @param int $level + * + * @return ActiveQuery + * @throws InvalidParamException + */ + public function getTaxGroupsByLevel(int $level = 0) + { + if ($level !== 0 && $level !== 1) { + throw new InvalidParamException( + 'Level must be 0 for Product Tax Groups or 1 for Product Variant Tax Groups' + ); + } + return $this->hasMany(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) + ->via('categoriesToGroups') + ->where([ 'level' => $level ]) + ->distinct(); + } + + /** + * Setter for Categories + * + * @param array $values + */ + public function setCategories($values) + { + $this->categories = $values; + } + + /** + * Setter for Options + * + * @param array $values + */ + public function setOptions($values) + { + $this->options = $values; + } + } diff --git a/models/ProductCategory.php b/models/ProductCategory.php new file mode 100755 index 0000000..d304460 --- /dev/null +++ b/models/ProductCategory.php @@ -0,0 +1,74 @@ + true, + 'targetClass' => Category::className(), + 'targetAttribute' => [ 'category_id' => 'id' ], + ], + [ + [ 'product_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Product::className(), + 'targetAttribute' => [ 'product_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'product_id' => Yii::t('product', 'Product'), + 'category_id' => Yii::t('product', 'Category'), + ]; + } + + public function getCategory() + { + return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]); + } + + public function getProduct() + { + return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); + } + } diff --git a/models/ProductImage.php b/models/ProductImage.php new file mode 100755 index 0000000..7adc9ed --- /dev/null +++ b/models/ProductImage.php @@ -0,0 +1,120 @@ + ImageBehavior::className(), + 'link' => 'image', + 'directory' => 'products', + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'product_id' ], + 'required', + ], + [ + [ + 'id', + 'product_id', + 'product_variant_id', + ], + 'integer', + ], + [ + [ + 'alt', + 'title', + 'image', + ], + 'string', + 'max' => 255, + ], + [ + [ 'product_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Product::className(), + 'targetAttribute' => [ 'product_id' => 'id' ], + ], + [ + [ 'product_variant_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => ProductVariant::className(), + 'targetAttribute' => [ 'product_variant_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('product', 'Product Image ID'), + 'product_id' => Yii::t('product', 'Product ID'), + 'product_variant_id' => Yii::t('product', 'Product Variant ID'), + 'product' => Yii::t('product', 'Product'), + 'product_variant' => Yii::t('product', 'Product Variant'), + 'image' => Yii::t('product', 'Image'), + 'alt' => Yii::t('product', 'Alt'), + 'title' => Yii::t('product', 'Title'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProduct() + { + $return = $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); + return $return; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductVariant() + { + return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]); + } + } diff --git a/models/ProductLang.php b/models/ProductLang.php new file mode 100755 index 0000000..6792271 --- /dev/null +++ b/models/ProductLang.php @@ -0,0 +1,127 @@ + [ + 'class' => 'common\behaviors\Slug', + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'title' ], + 'required', + ], + [ + [ 'description' ], + 'string', + ], + [ + [ + 'title', + 'alias', + ], + 'string', + 'max' => 255, + ], + [ + [ + 'product_id', + 'language_id', + ], + 'unique', + 'targetAttribute' => [ + 'product_id', + 'language_id', + ], + 'message' => 'The combination of Product ID and Language ID has already been taken.', + ], + [ + [ 'language_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Language::className(), + 'targetAttribute' => [ 'language_id' => 'id' ], + ], + [ + [ 'product_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Product::className(), + 'targetAttribute' => [ 'product_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'product_id' => Yii::t('app', 'Product ID'), + 'language_id' => Yii::t('app', 'Language ID'), + 'title' => Yii::t('app', 'Name'), + 'description' => Yii::t('app', 'Description'), + '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 getProduct() + { + return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); + } + } diff --git a/models/ProductOption.php b/models/ProductOption.php new file mode 100755 index 0000000..bb54b21 --- /dev/null +++ b/models/ProductOption.php @@ -0,0 +1,90 @@ + true, + 'targetClass' => Product::className(), + 'targetAttribute' => [ 'product_id' => 'id' ], + ], + [ + [ 'option_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => TaxOption::className(), + 'targetAttribute' => [ 'option_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'product_id' => Yii::t('product', 'Product ID'), + 'option_id' => Yii::t('product', 'Option ID'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProduct() + { + return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getOption() + { + return $this->hasOne(TaxOption::className(), [ 'id' => 'option_id' ]); + } + } diff --git a/models/ProductSearch.php b/models/ProductSearch.php new file mode 100755 index 0000000..19e1bef --- /dev/null +++ b/models/ProductSearch.php @@ -0,0 +1,192 @@ + 'Category ID', + 'brand_id' => 'Brand ID', + 'productName' => 'Product name', + 'variantCount' => 'Variant count', + ]; + return array_merge($labels, $new_labels); + } + + /** + * @inheritdoc + */ + public function scenarios() + { + // bypass scenarios() implementation in the parent class + return Model::scenarios(); + } + + /** + * Creates data provider instance with search query applied + * + * @param array $params + * + * @return ActiveDataProvider + */ + public function search($params) + { + $query = Product::find(); + $query->select( + [ + 'product.*', + ] + ); + + $query->joinWith( + [ + 'categories', + 'lang', + ] + ) + ->joinWith( + [ + 'brand' => function ($query) { + /** + * @var ActiveQuery $query + */ + $query->joinWith('lang'); + }, + ] + ) + ->joinWith('variants'); + + $query->groupBy( + [ + 'product.id', + 'product_variant.id', + ] + ); + + $dataProvider = new ActiveDataProvider( + [ + 'query' => $query, + ] + ); + + $dataProvider->setSort( + [ + 'attributes' => [ + 'id', + 'productName' => [ + 'asc' => [ 'product_lang.title' => SORT_ASC ], + 'desc' => [ 'product_lang.title' => SORT_DESC ], + ], + 'brand_id' => [ + 'asc' => [ 'brand_lang.title' => SORT_ASC ], + 'desc' => [ 'brand_lang.title' => SORT_DESC ], + ], + ], + ] + ); + + $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; + } + + if (isset( $this->is_top )) { + $query->andWhere( + [ + 'is_top' => (bool) $this->is_top, + ] + ); + } + if (isset( $this->is_new )) { + $query->andWhere( + [ + 'is_new' => (bool) $this->is_new, + ] + ); + } + if (isset( $this->is_discount )) { + $query->andWhere( + [ + 'is_discount' => (bool) $this->is_discount, + ] + ); + } + $query->andFilterWhere( + [ + 'product.brand_id' => $this->brand_id, + 'product.id' => $this->id, + 'product_category.category_id' => $this->categoryId, + ] + ); + $query->andFilterWhere( + [ + 'like', + 'product_lang.title', + $this->productName, + ] + ); + + return $dataProvider; + } + } diff --git a/models/ProductStock.php b/models/ProductStock.php new file mode 100755 index 0000000..8785cbc --- /dev/null +++ b/models/ProductStock.php @@ -0,0 +1,138 @@ + true, + 'targetClass' => ProductVariant::className(), + 'targetAttribute' => [ 'product_variant_id' => 'id' ], + ], + [ + [ 'stock_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Stock::className(), + 'targetAttribute' => [ 'stock_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'stock_id' => 'Stock ID', + 'quantity' => 'Количество', + 'product_variant_id' => 'Product Variant ID', + 'title' => "Название", + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProduct() + { + return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]) + ->via('variants'); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductVariant() + { + return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]); + } + + /** + * Get Stock title, tries to get from Stock lang + * + * @return string + */ + public function getTitle(): string + { + if (!empty( $this->title )) { + return $this->title; + } elseif (!empty( $this->stock )) { + return $this->stock->lang->title; + } else { + return ''; + } + } + + /** + * Set Stock title, will be saved to Stock table + * + * @param mixed $value + */ + public function setTitle(string $value) + { + $this->title = $value; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getStock() + { + return $this->hasOne(Stock::className(), [ 'id' => 'stock_id' ]); + } + + /** + * @inheritdoc + */ + public static function primaryKey() + { + return [ + "stock_id", + "product_variant_id", + ]; + } + } diff --git a/models/ProductUnit.php b/models/ProductUnit.php new file mode 100755 index 0000000..62d5428 --- /dev/null +++ b/models/ProductUnit.php @@ -0,0 +1,98 @@ + [ + 'class' => LanguageBehavior::className(), + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'is_default' ], + 'boolean', + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('product', 'Product Unit ID'), + 'is_default' => Yii::t('product', 'Is Default'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getCategories() + { + return $this->hasMany(Category::className(), [ 'product_unit_id' => 'id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductVariants() + { + return $this->hasMany(ProductVariant::className(), [ 'product_unit_id' => 'id' ]); + } + } diff --git a/models/ProductUnitLang.php b/models/ProductUnitLang.php new file mode 100755 index 0000000..c49d76a --- /dev/null +++ b/models/ProductUnitLang.php @@ -0,0 +1,113 @@ + 255, + ], + [ + [ + 'product_unit_id', + 'language_id', + ], + 'unique', + 'targetAttribute' => [ + 'product_unit_id', + 'language_id', + ], + 'message' => 'The combination of Product Unit ID and Language ID has already been taken.', + ], + [ + [ 'language_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Language::className(), + 'targetAttribute' => [ 'language_id' => 'id' ], + ], + [ + [ 'product_unit_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => ProductUnit::className(), + 'targetAttribute' => [ 'product_unit_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'product_unit_id' => Yii::t('app', 'Product Unit ID'), + 'language_id' => Yii::t('app', 'Language ID'), + 'title' => Yii::t('app', 'Name'), + 'short' => Yii::t('app', 'Short'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getLanguage() + { + return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductUnit() + { + return $this->hasOne(ProductUnit::className(), [ 'id' => 'product_unit_id' ]); + } + } diff --git a/models/ProductUnitSearch.php b/models/ProductUnitSearch.php new file mode 100755 index 0000000..ac894aa --- /dev/null +++ b/models/ProductUnitSearch.php @@ -0,0 +1,116 @@ + \Yii::t('product', 'Product Unit Name'), + ]; + return array_merge($labels, $new_labels); + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'title' ], + 'safe', + ], + [ + [ 'id' ], + 'integer', + ], + [ + [ 'is_default' ], + 'boolean', + ], + ]; + } + + /** + * @inheritdoc + */ + public function scenarios() + { + // bypass scenarios() implementation in the parent class + return Model::scenarios(); + } + + /** + * Creates data provider instance with search query applied + * + * @param array $params + * + * @return ActiveDataProvider + */ + public function search($params) + { + $query = ProductUnit::find() + ->joinWith('lang'); + + // add conditions that should always apply here + + $dataProvider = new ActiveDataProvider( + [ + 'query' => $query, + 'sort' => [ + 'attributes' => [ + 'id', + 'is_defaut', + 'title' => [ + 'asc' => [ 'product_unit_lang.title' => SORT_ASC ], + 'desc' => [ 'product_unit_lang.title' => SORT_DESC ], + ], + ], + ], + ] + ); + + $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, + 'is_default' => $this->is_default, + ] + ) + ->andFilterWhere( + [ + 'ilike', + 'product_unit_lang.title', + $this->title, + ] + ); + + return $dataProvider; + } + } diff --git a/models/ProductVariant.php b/models/ProductVariant.php new file mode 100755 index 0000000..4505318 --- /dev/null +++ b/models/ProductVariant.php @@ -0,0 +1,418 @@ + [ + 'class' => LanguageBehavior::className(), + ], + 'images' => [ + 'class' => SaveMultipleFileBehavior::className(), + 'name' => 'imagesUpload', + 'directory' => 'products', + 'column' => 'image', + 'links' => [ + 'product_id' => 'product_id', + 'id' => 'product_variant_id', + ], + 'model' => ProductImage::className(), + ], + 'multipleImage' => [ + 'class' => MultipleImgBehavior::className(), + 'links' => [ + 'product_variant_id' => 'id', + ], + 'model' => ProductImage::className(), + 'config' => [ + 'caption' => 'image', + 'delete_action' => '/product/variant/delete-image', + 'id' => 'id', + ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ + 'product_id', + 'product_unit_id', + ], + 'required', + ], + [ + [ + 'product_id', + 'product_unit_id', + ], + 'integer', + ], + [ + [ + 'price', + 'price_old', + 'stock', + ], + 'number', + ], + [ + [ + 'sku', + ], + 'string', + 'max' => 255, + ], + [ + [ + 'options', + ], + 'safe', + ], + [ + [ 'product_unit_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => ProductUnit::className(), + 'targetAttribute' => [ 'product_unit_id' => 'id' ], + ], + [ + [ 'product_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Product::className(), + 'targetAttribute' => [ 'product_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('product', 'Product Variant ID'), + 'product_id' => Yii::t('product', 'Product ID'), + 'sku' => Yii::t('product', 'Sku'), + 'price' => Yii::t('product', 'Price'), + 'price_old' => Yii::t('product', 'Price Old'), + 'stock' => Yii::t('product', 'Stock'), + 'product_unit_id' => Yii::t('product', 'Product Unit ID'), + 'stock_caption' => Yii::t('product', 'Stock'), + 'image' => Yii::t('product', 'Image'), + 'images' => Yii::t('product', 'Images'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductUnit() + { + return $this->hasOne(ProductUnit::className(), [ 'id' => 'product_unit_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProduct() + { + return $this->hasOne(Product::className(), [ 'id' => 'product_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductStocks() + { + return $this->hasMany(ProductStock::className(), [ 'product_variant_id' => 'id' ]); + } + + /** + * Get qunatity for current ProductVariant + * If $recalculate set to true will recalculate stock via product_stock table + * + * @param bool $recalculate + * + * @return int + */ + public function getQuantity(bool $recalculate = false): int + { + if (!$recalculate) { + return $this->stock; + } else { + $quantity = $this->getProductStocks() + ->sum('quantity'); + if (empty( $quantity )) { + $this->stock = 0; + } else { + $this->stock = (int) $quantity; + } + $this->save(false, [ 'stock' ]); + return $this->stock; + } + } + + /** + * Get ProductStocks query woth preloaded Stocks for current ProductVariant + * **Used in dynamic fields in product variant form** + * + * @return ActiveQuery + */ + public function getVariantStocks() + { + return $this->getProductStocks() + ->joinWith('stock'); + } + + /** + * @return ActiveQuery + */ + public function getStocks() + { + return $this->hasMany(Stock::className(), [ 'id' => 'stock_id' ]) + ->via('productStocks'); + } + + /** + * @return ActiveQuery + */ + public function getOptions() + { + return $this->hasMany(TaxOption::className(), [ 'id' => 'option_id' ]) + ->viaTable('product_variant_option', [ 'product_variant_id' => 'id' ]); + } + + /** + * Get TaxOptions with preloaded TaxGroups for current ProductVariant + * + * @return ActiveQuery + */ + public function getFilters() + { + return $this->getOptions() + ->joinWith('taxGroup.lang') + ->joinWith('lang'); + } + + /** + * Get Product title concanated with current ProductVariant title + * + * @return string + */ + public function getFullname(): string + { + return $this->product->lang->title . ' ' . $this->lang->title; + } + + /** + * Set Options to override previous + * + * @param int[] $values + */ + public function setOptions($values) + { + $this->options = $values; + } + + /** + * Get all TaxGroups for current ProductVariant filled with $customOptions that satisfy current ProductVariant + * + * @return TaxGroup[] + */ + public function getProperties() + { + $groups = $options = []; + foreach ($this->getOptions() + ->with('lang') + ->all() as $option) { + /** + * @var TaxOption $option + */ + $options[ $option->tax_group_id ][] = $option; + } + foreach (TaxGroup::find() + ->where([ 'tax_group.id' => array_keys($options) ]) + ->orderBy([ 'sort' => SORT_ASC ]) + ->with('lang') + ->all() as $group) { + /** + * @var TaxGroup $group + */ + if (!empty( $options[ $group->id ] )) { + $group->customOptions = $options[ $group->id ]; + $groups[] = $group; + } + } + return $groups; + } + + /** + * Set stocks to override existing in product_stock table + * + * @param mixed $stocks + */ + public function setStocks($stocks) + { + $this->stocks = (array) $stocks; + } + + /** + * @return ActiveQuery + */ + public function getCategory() + { + return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]) + ->viaTable('product_category', [ 'product_id' => 'product_id' ]); + } + + /** + * @return ActiveQuery + */ + public function getCategories() + { + return $this->hasMany(Category::className(), [ 'id' => 'category_id' ]) + ->viaTable('product_category', [ 'product_id' => 'product_id' ]); + } + + /** + * Get TaxGroups query for current ProductVariant according to level + * * 0 - Product Tax Groups + * * 1 - ProductVariant Tax Groups + * + * @param int $level + * + * @return ActiveQuery + * @throws InvalidParamException + */ + public function getTaxGroupsByLevel(int $level = 0) + { + return $this->product->getTaxGroupsByLevel($level); + } + + public function afterSave($insert, $changedAttributes) + { + parent::afterSave($insert, $changedAttributes); + if (!empty( $this->options )) { + $options = TaxOption::findAll($this->options); + $this->unlinkAll('options', true); + foreach ($options as $option) { + $this->link('options', $option); + } + } + + if (!empty( $this->stocks )) { + ProductStock::deleteAll([ 'product_variant_id' => $this->id ]); + foreach ($this->stocks as $id => $quantity) { + /** + * @var ProductStock $productStock + */ + $productStock = ProductStock::find() + ->where( + [ + 'product_variant_id' => $this->id, + 'stock_id' => $id, + ] + ) + ->one(); + $productStock->quantity = $quantity; + $productStock->save(); + } + } + } + } diff --git a/models/ProductVariantLang.php b/models/ProductVariantLang.php new file mode 100755 index 0000000..cb12b4a --- /dev/null +++ b/models/ProductVariantLang.php @@ -0,0 +1,108 @@ + 255, + ], + [ + [ + 'product_variant_id', + 'language_id', + ], + 'unique', + 'targetAttribute' => [ + 'product_variant_id', + 'language_id', + ], + 'message' => 'The combination of Product Variant ID and Language ID has already been taken.', + ], + [ + [ 'language_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Language::className(), + 'targetAttribute' => [ 'language_id' => 'id' ], + ], + [ + [ 'product_variant_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => ProductVariant::className(), + 'targetAttribute' => [ 'product_variant_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'product_variant_id' => Yii::t('app', 'Product Variant ID'), + 'language_id' => Yii::t('app', 'Language ID'), + 'title' => Yii::t('app', 'Name'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getLanguage() + { + return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductVariant() + { + return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]); + } + } diff --git a/models/ProductVariantOption.php b/models/ProductVariantOption.php new file mode 100755 index 0000000..da29e6c --- /dev/null +++ b/models/ProductVariantOption.php @@ -0,0 +1,90 @@ + true, + 'targetClass' => ProductVariant::className(), + 'targetAttribute' => [ 'product_variant_id' => 'id' ], + ], + [ + [ 'option_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => TaxOption::className(), + 'targetAttribute' => [ 'option_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'product_variant_id' => 'Product Variant ID', + 'option_id' => 'Option ID', + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductVariant() + { + return $this->hasOne(ProductVariant::className(), [ 'id' => 'product_variant_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getOption() + { + return $this->hasOne(TaxOption::className(), [ 'id' => 'option_id' ]); + } + } diff --git a/models/ProductVariantSearch.php b/models/ProductVariantSearch.php new file mode 100755 index 0000000..02779dc --- /dev/null +++ b/models/ProductVariantSearch.php @@ -0,0 +1,137 @@ +joinWith('lang'); + + // add conditions that should always apply here + + $dataProvider = new ActiveDataProvider( + [ + 'query' => $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; + } + + $dataProvider->setSort( + [ + 'attributes' => [ + 'id', + 'sku', + 'variantName' => [ + 'asc' => [ 'product_variant_lang.title' => SORT_ASC ], + 'desc' => [ 'product_variant_lang.title' => SORT_DESC ], + ], + 'price', + 'price_old', + 'stock', + ], + ] + ); + + $query->andFilterWhere( + [ + 'price' => $this->price, + 'price_old' => $this->price_old, + 'stock' => $this->stock, + ] + ); + + $query->andFilterWhere( + [ + 'ilike', + 'product_variant_lang.title', + $this->variantName, + ] + ) + ->andFilterWhere( + [ + 'ilike', + 'sku', + $this->sku, + ] + ); + + $query->groupBy( + [ + 'product_variant.id', + 'product_variant_lang.title', + ] + ); + + return $dataProvider; + } + } diff --git a/models/Stock.php b/models/Stock.php new file mode 100755 index 0000000..8eb6c62 --- /dev/null +++ b/models/Stock.php @@ -0,0 +1,93 @@ + [ + 'class' => LanguageBehavior::className(), + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('product', 'Stock ID'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getProductStocks() + { + return $this->hasMany(ProductStock::className(), [ 'stock_id' => 'id' ]); + } + + /** + * @return ActiveQuery + */ + public function getProductVariants() + { + return $this->hasMany(ProductVariant::className(), [ 'id' => 'product_variant_id' ]) + ->via('productStocks'); + } + + /** + * @return ActiveQuery + */ + public function getProducts() + { + return $this->hasMany(Product::className(), [ 'id' => 'product_id' ]) + ->via('productVariants'); + } + } diff --git a/models/StockLang.php b/models/StockLang.php new file mode 100644 index 0000000..2024ad3 --- /dev/null +++ b/models/StockLang.php @@ -0,0 +1,110 @@ + 255, + ], + [ + [ + 'stock_id', + 'language_id', + ], + 'unique', + 'targetAttribute' => [ + 'stock_id', + 'language_id', + ], + 'message' => 'The combination of Stock ID and Language ID has already been taken.', + ], + [ + [ 'stock_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Stock::className(), + 'targetAttribute' => [ 'stock_id' => 'id' ], + ], + [ + [ 'language_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Language::className(), + 'targetAttribute' => [ 'language_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'stock_id' => Yii::t('app', 'Stock ID'), + 'language_id' => Yii::t('app', 'Language ID'), + 'title' => Yii::t('app', 'Name'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getStock() + { + return $this->hasOne(Stock::className(), [ 'id' => 'stock_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getLanguage() + { + return $this->hasOne(Language::className(), [ 'id' => 'language_id' ]); + } + } diff --git a/models/TaxGroup.php b/models/TaxGroup.php new file mode 100755 index 0000000..c87a0ac --- /dev/null +++ b/models/TaxGroup.php @@ -0,0 +1,225 @@ + [ + 'class' => LanguageBehavior::className(), + ], + ]; + } + + /** + * @inheritdoc + */ + public static function tableName() + { + return 'tax_group'; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ + 'is_filter', + 'display', + 'is_menu', + ], + 'boolean', + ], + [ + [ + 'level', + 'sort', + ], + 'integer', + ], + [ + [ 'categories' ], + 'safe', + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => 'Tax Group ID', + 'is_filter' => 'Use in filter', + 'sort' => 'Sort', + 'display' => 'Display', + 'is_menu' => 'Отображать в меню', + ]; + } + + /** + * @return ActiveQuery + */ + public function getCategories() + { + return $this->hasMany(Category::className(), [ 'id' => 'category_id' ]) + ->viaTable('tax_group_to_category', [ 'tax_group_id' => 'id' ]); + } + + /** + * Set categories to override tax_group_to_category table data + * + * @param int[] $values + */ + public function setCategories(array $values) + { + $this->categories = $values; + } + + /** + * @inheritdoc + */ + public function afterSave($insert, $changedAttributes) + { + parent::afterSave($insert, $changedAttributes); + $this->unlinkAll('categories', true); + $categories = []; + if (!empty( $this->categories )) { + $categories = Category::findAll($this->categories); + } + foreach ($categories as $category) { + $this->link('categories', $category); + } + } + + /** + * @return ActiveQuery + */ + public function getTaxOptions() + { + return $this->hasMany(TaxOption::className(), [ 'tax_group_id' => 'id' ]) + ->inverseOf('taxGroup'); + } + + /** + * Synonim for getTaxOptions() + * + * @see TaxGroup::getTaxOptions() + * @return \yii\db\ActiveQuery + */ + public function getOptions() + { + return $this->getTaxOptions(); + } + + /** + * Get customOptins that were filled dynamically. + * If $fillDefault is true then fill $customOptions with TaxOptions for current TaxGroup + * + * @param bool $fillDefault + * + * @return TaxOption[] + */ + public function getCustomOptions(bool $fillDefault = false): array + { + if ($fillDefault && empty( $this->custom_options )) { + $this->customOptions = $this->getTaxOptions() + ->with('lang') + ->all(); + } + return $this->customOptions; + } + + /** + * Set customOptions + * + * @param TaxOption[] $value + */ + public function setCustomOptions(array $value) + { + foreach ($value as $item) { + if (!( $item instanceof TaxOption )) { + throw new InvalidValueException('All elements must be instances of ' . TaxOption::className()); + } + } + $this->customOptions = $value; + } + + /** + * Get default lang alias + * + * @return string + */ + public function getAlias() + { + $default_lang = Language::getDefaultLanguage(); + /** + * @var TaxGroupLang $lang + */ + $lang = $this->getLang($default_lang->id) + ->one(); + return $lang->alias; + } + } diff --git a/models/TaxGroupLang.php b/models/TaxGroupLang.php new file mode 100755 index 0000000..3058107 --- /dev/null +++ b/models/TaxGroupLang.php @@ -0,0 +1,128 @@ + [ + 'class' => 'common\behaviors\Slug', + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'title' ], + 'required', + ], + [ + [ 'description' ], + 'string', + ], + [ + [ + 'title', + 'alias', + ], + 'string', + 'max' => 255, + ], + [ + [ + 'tax_group_id', + 'language_id', + ], + 'unique', + 'targetAttribute' => [ + 'tax_group_id', + 'language_id', + ], + 'message' => 'The combination of Tax Group ID and Language ID has already been taken.', + ], + [ + [ 'language_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Language::className(), + 'targetAttribute' => [ 'language_id' => 'id' ], + ], + [ + [ 'tax_group_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => TaxGroup::className(), + 'targetAttribute' => [ 'tax_group_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'tax_group_id' => Yii::t('app', 'Tax Group ID'), + 'language_id' => Yii::t('app', 'Language ID'), + 'title' => Yii::t('app', 'Name'), + 'description' => Yii::t('app', 'Description'), + '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 getTaxGroup() + { + return $this->hasOne(TaxGroup::className(), [ 'id' => 'tax_group_id' ]); + } + } diff --git a/models/TaxGroupSearch.php b/models/TaxGroupSearch.php new file mode 100755 index 0000000..aa1739d --- /dev/null +++ b/models/TaxGroupSearch.php @@ -0,0 +1,120 @@ +joinWith('lang'); + + $dataProvider = new ActiveDataProvider( + [ + 'query' => $query, + 'sort' => [ + 'attributes' => [ + 'id', + 'is_filter', + 'groupName' => [ + 'asc' => [ 'tax_group_lang.title' => SORT_ASC ], + 'desc' => [ 'tax_group_lang.title' => SORT_DESC ], + ], + ], + ], + ] + ); + + $this->load($params); + + if (!is_null($level)) { + $this->level = $level; + } + + 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; + } + + $query->andFilterWhere( + [ + 'id' => $this->id, + 'is_filter' => $this->is_filter, + 'level' => $this->level, + ] + ) + ->andFilterWhere( + [ + 'ilike', + 'tax_group_lang.title', + $this->groupName, + ] + ); + + return $dataProvider; + } + } diff --git a/models/TaxGroupToCategory.php b/models/TaxGroupToCategory.php new file mode 100755 index 0000000..97e29de --- /dev/null +++ b/models/TaxGroupToCategory.php @@ -0,0 +1,92 @@ + true, + 'targetClass' => Category::className(), + 'targetAttribute' => [ 'category_id' => 'id' ], + ], + [ + [ 'tax_group_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => TaxGroup::className(), + 'targetAttribute' => [ 'tax_group_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'tax_group_to_category_id' => 'Tax Group To Category ID', + 'tax_group_id' => 'Tax Group ID', + 'category_id' => 'Category ID', + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getCategory() + { + return $this->hasOne(Category::className(), [ 'id' => 'category_id' ]); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getTaxGroup() + { + return $this->hasOne(TaxGroup::className(), [ 'id' => 'tax_group_id' ]); + } + } diff --git a/models/TaxOption.php b/models/TaxOption.php new file mode 100755 index 0000000..dbaa865 --- /dev/null +++ b/models/TaxOption.php @@ -0,0 +1,158 @@ + SaveImgBehavior::className(), + 'fields' => [ + [ + 'name' => 'image', + 'directory' => 'tax_option', + ], + ], + ], + 'language' => [ + 'class' => LanguageBehavior::className(), + ], + ]; + } + + /** + * @inheritdoc + */ + public static function tableName() + { + return '{{%tax_option}}'; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ + 'tax_group_id', + 'sort', + ], + 'integer', + ], + [ + [ 'tax_group_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => TaxGroup::className(), + 'targetAttribute' => [ 'tax_group_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('app', 'Tax Option ID'), + 'tax_group_id' => Yii::t('app', 'Tax Group ID'), + 'sort' => Yii::t('app', 'Sort'), + 'image' => Yii::t('product', 'Image'), + ]; + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getTaxGroup() + { + return $this->hasOne(TaxGroup::className(), [ 'id' => 'tax_group_id' ]) + ->inverseOf('taxOptions'); + } + + /** + * Synonim for TaxOption::getTaxGroup() + * + * @see TaxOption::getTaxGroup() + * @return \yii\db\ActiveQuery + */ + public function getGroup() + { + return $this->getTaxGroup(); + } + + /** + * @return ActiveQuery + */ + public function getProducts() + { + return $this->hasMany(Product::className(), [ 'id' => 'product_id' ]) + ->viaTable('product_option', [ 'option_id' => 'id' ]); + } + + /** + * @return ActiveQuery + */ + public function getProductVariants() + { + return $this->hasMany(ProductVariant::className(), [ 'id' => 'product_variant_id' ]) + ->viaTable('product_variant_option', [ 'option_id' => 'id' ]); + } + } diff --git a/models/TaxOptionLang.php b/models/TaxOptionLang.php new file mode 100755 index 0000000..dd09e88 --- /dev/null +++ b/models/TaxOptionLang.php @@ -0,0 +1,123 @@ + [ + 'class' => 'common\behaviors\Slug', + 'inAttribute' => 'value', + ], + ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ 'value' ], + 'required', + ], + [ + [ + 'value', + 'alias', + ], + 'string', + 'max' => 255, + ], + [ + [ + 'tax_option_id', + 'language_id', + ], + 'unique', + 'targetAttribute' => [ + 'tax_option_id', + 'language_id', + ], + 'message' => 'The combination of Tax Option ID and Language ID has already been taken.', + ], + [ + [ 'language_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => Language::className(), + 'targetAttribute' => [ 'language_id' => 'id' ], + ], + [ + [ 'tax_option_id' ], + 'exist', + 'skipOnError' => true, + 'targetClass' => TaxOption::className(), + 'targetAttribute' => [ 'tax_option_id' => 'id' ], + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'tax_option_id' => Yii::t('app', 'Tax Option ID'), + 'language_id' => Yii::t('app', 'Language ID'), + 'value' => Yii::t('app', 'Value'), + '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 getTaxOption() + { + return $this->hasOne(TaxOption::className(), [ 'id' => 'tax_option_id' ]); + } + } diff --git a/models/TaxOptionSearch.php b/models/TaxOptionSearch.php new file mode 100755 index 0000000..8304ddd --- /dev/null +++ b/models/TaxOptionSearch.php @@ -0,0 +1,101 @@ +joinWith('lang'); + + $dataProvider = new ActiveDataProvider( + [ + 'query' => $query, + 'sort' => [ + 'attributes' => [ + 'id', + 'value' => [ + 'asc' => [ 'tax_option_lang.value' => SORT_ASC ], + 'desc' => [ 'tax_option_lang.value' => SORT_DESC ], + ], + ], + ], + ] + ); + + $this->load($params); + + // if (!$this->validate()) { + // return $dataProvider; + // } + + // grid filtering conditions + $query->andFilterWhere( + [ + 'id' => $this->id, + ] + ) + ->andFilterWhere( + [ + 'like', + 'tax_option_lang.value', + $this->value, + ] + ); + + return $dataProvider; + } + } diff --git a/views/manage/_form.php b/views/manage/_form.php new file mode 100755 index 0000000..765e924 --- /dev/null +++ b/views/manage/_form.php @@ -0,0 +1,142 @@ + + +
      + + [ 'enctype' => 'multipart/form-data' ], + ] + ); ?> + + field($model, 'is_top') + ->checkbox([ 'label' => 'ТОП' ]) ?> + field($model, 'is_new') + ->checkbox([ 'label' => 'Новинка' ]) ?> + field($model, 'is_discount') + ->checkbox([ 'label' => 'Акционный' ]) ?> + + field($model, 'video') + ->textarea(); ?> + + field($model, 'brand_id') + ->dropDownList( + ArrayHelper::map( + Brand::find() + ->with('lang') + ->all(), + 'id', + 'lang.title' + ), + [ + 'prompt' => Yii::t('product', 'Select brand'), + ] + ) ?> + + field($model, 'categories') + ->widget( + Select2::className(), + [ + 'data' => ArtboxTreeHelper::treeMap(ProductHelper::getCategories(), 'id', 'lang.title'), + 'language' => 'ru', + 'options' => [ + 'placeholder' => Yii::t('product', 'Select categories'), + 'multiple' => true, + ], + 'pluginOptions' => [ + 'allowClear' => true, + ], + ] + ) ?> + + field($model, 'imagesUpload[]') + ->widget( + \kartik\file\FileInput::className(), + [ + 'language' => 'ru', + 'options' => [ + 'accept' => 'image/*', + 'multiple' => true, + ], + 'pluginOptions' => [ + 'allowedFileExtensions' => [ + 'jpg', + 'gif', + 'png', + ], + 'initialPreview' => !empty( $model->imagesHTML ) ? $model->imagesHTML : [], + 'initialPreviewConfig' => $model->imagesConfig, + 'overwriteInitial' => false, + 'showRemove' => false, + 'showUpload' => false, + 'uploadAsync' => !empty( $model->id ), + 'previewFileType' => 'image', + ], + ] + ); ?> + + with('lang') + ->all() as $group) { + /** + * @var TaxGroup $group + */ + echo $form->field($model, 'options') + ->checkboxList( + ArrayHelper::map( + $group->getOptions() + ->with('lang') + ->all(), + 'id', + 'lang.value' + ), + [ + 'multiple' => true, + 'unselect' => NULL, + ] + ) + ->label($group->lang->title); + } + } + ?> + +
      + + $modelLangs, + 'formView' => '@common/modules/product/views/manage/_form_language', + 'form' => $form, + ] + ) ?> + +
      + isNewRecord ? Yii::t('product', 'Create') : Yii::t('product', 'Update'), + [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ] + ) ?> +
      + + + +
      diff --git a/views/manage/_form_language.php b/views/manage/_form_language.php new file mode 100755 index 0000000..601f2ac --- /dev/null +++ b/views/manage/_form_language.php @@ -0,0 +1,28 @@ + +field($model_lang, '[' . $language->id . ']title') + ->textInput([ 'maxlength' => true ]); ?> +field($model_lang, '[' . $language->id . ']alias') + ->textInput([ 'maxlength' => true ]); ?> +field($model_lang, '[' . $language->id . ']description') + ->widget(CKEditor::className(), [ + 'editorOptions' => ElFinder::ckeditorOptions('elfinder', [ + 'preset' => 'full', + 'inline' => false, + 'filebrowserUploadUrl' => Yii::$app->getUrlManager() + ->createUrl('file/uploader/images-upload'), + ]), + ]) ?> \ No newline at end of file diff --git a/views/manage/create.php b/views/manage/create.php new file mode 100755 index 0000000..adb632d --- /dev/null +++ b/views/manage/create.php @@ -0,0 +1,30 @@ +title = Yii::t('product', 'Create Product'); + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Products'), + 'url' => [ 'index' ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
      + +

      title) ?>

      + + render('_form', [ + 'model' => $model, + 'modelLangs' => $modelLangs, + ]) ?> + +
      diff --git a/views/manage/export-process.php b/views/manage/export-process.php new file mode 100755 index 0000000..9278200 --- /dev/null +++ b/views/manage/export-process.php @@ -0,0 +1,104 @@ + +registerJs("var in_process=true; + var count=1; + var filename = null; + + doExport(0,filename); + + function doExport(from,filename) { + from = typeof(from) != 'undefined' ? from : 0; + + $.ajax({ + method: 'get', + url: '" . Yii::$app->request->baseUrl . '/product/manage/export-process' . "', + data: { + from:from, + filename: filename + }, + dataType: 'json', + success: function(data){ + + var per = Math.round(100*data.from/data.totalsize)+'%'; + $('#progressbar div').css({width: per}); + + if(data != false && !data.end) + { + doExport(data.from,data.filename); + } + else + { + console.log(data.link); + $(progressbar).hide('fast'); + $('#result_link').attr('href', data.link).removeClass('hidden'); + in_process = false; + } + }, + error: function(xhr, status, errorThrown) { + } + }); + }"); +?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +

      Экспорт данных товаров

      + + [ + 'value' => 100, + 'label' => '', + ], + 'options' => [ + 'id' => 'progressbar', + ], + ]); ?> +
        + +
        diff --git a/views/manage/export.php b/views/manage/export.php new file mode 100755 index 0000000..432d031 --- /dev/null +++ b/views/manage/export.php @@ -0,0 +1,51 @@ + + +
        + false, + 'options' => [ 'enctype' => 'multipart/form-data' ], + ]); ?> + + errors) : ?> +
        + \n", $model->errors); ?> +
        + + + output) : ?> +

        Лог операции

        +
        + \n", $model->output); ?> +
        + + + field($model, 'lang') + ->dropDownList(Language::find() + ->select([ + 'name', + 'id', + ]) + ->where([ 'status' => 1 ]) + ->orderBy([ 'default' => SORT_DESC ]) + ->asArray() + ->indexBy('id') + ->column()) ?> + +
        + 'btn btn-success' ]) ?> +
        + + +
        \ No newline at end of file diff --git a/views/manage/import-process.php b/views/manage/import-process.php new file mode 100755 index 0000000..91199cf --- /dev/null +++ b/views/manage/import-process.php @@ -0,0 +1,64 @@ +registerJs(" +var in_process=false; + var count=1; + + in_process=true; + + doImport(); + + function doImport(from) { + from = typeof(from) != 'undefined' ? from : 0; + console.log('go', from); + $.ajax({ + url: '" . \Yii::$app->request->baseUrl . '/product/manage/' . $method . "', + data: {from:from}, + dataType: 'json', + success: function(data){ + for(var key in data.items) + { + $('ul#process-result').prepend('
      • '+ data.items[key] +'
      • '); + count++; + } + + var per = Math.round(100*data.from/data.totalsize)+'%'; + $('#progressbar div').css({width: per}); + + if(data != false && !data.end) + { + doImport(data.from); + } + else + { + $('ul#process-result').prepend('
      • Импорт цен успешно завершен!
      • '); + progressbar.hide('fast'); + in_process = false; + } + }, + error: function(xhr, status, errorThrown) { + } + }); + } +"); + //?> + +
        +

        Импорт товаров

        + + [ + 'value' => 100, + 'label' => '', + ], + 'options' => [ + 'id' => 'progressbar', + ], + ]); ?> +
          +
          diff --git a/views/manage/import.php b/views/manage/import.php new file mode 100755 index 0000000..4e67ef4 --- /dev/null +++ b/views/manage/import.php @@ -0,0 +1,63 @@ + + +
          + false, + 'options' => [ 'enctype' => 'multipart/form-data' ], + ]); ?> + + errors) : ?> +
          + \n", $model->errors); ?> +
          + + + output) : ?> +

          Лог операции

          +
          + \n", $model->output); ?> +
          + + + field($model, 'type') + ->radioList([ + 'products' => Yii::t('product', 'Load products'), + 'prices' => Yii::t('product', 'Load prices'), + ]); ?> + + field($model, 'lang') + ->dropDownList($languages); ?> + + field($model, 'file') + ->fileInput([ 'multiple' => false, ]) ?> + + field($model, 'file')->widget(\kartik\file\FileInput::classname(), [ + 'language' => 'ru', + 'options' => [ + 'multiple' => false, + ], + 'pluginOptions' => [ + 'allowedFileExtensions' => ['csv'], + 'overwriteInitial' => true, + 'showRemove' => false, + 'showUpload' => false, + ], + ])*/ ?> + +
          + 'btn btn-primary' ]) ?> +
          + + +
          diff --git a/views/manage/index.php b/views/manage/index.php new file mode 100755 index 0000000..5d4da9b --- /dev/null +++ b/views/manage/index.php @@ -0,0 +1,232 @@ +title = Yii::t('product', 'Products'); + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + +

          + 'btn btn-success' ]) ?> +

          + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + 'id', + [ + 'attribute' => 'productName', + 'value' => 'lang.title', + ], + [ + 'label' => Yii::t('product', 'Brand'), + 'attribute' => 'brand_id', + 'value' => 'brand.lang.title', + 'filter' => Select2::widget( + [ + 'model' => $searchModel, + 'attribute' => 'brand_id', + 'data' => Brand::find() + ->joinWith('lang') + ->select( + [ + 'brand_lang.title', + 'brand.id', + ] + ) + ->asArray() + ->indexBy('id') + ->column(), + 'language' => 'ru', + 'options' => [ + 'placeholder' => Yii::t('product', 'Select brand'), + 'multiple' => false, + ], + 'pluginOptions' => [ + 'allowClear' => true, + ], + ] + ), + ], + [ + 'label' => Yii::t('product', 'Category'), + 'attribute' => 'categoryId', + 'value' => function ($model) { + /** + * @var Product $model + */ + $categories = []; + foreach ($model->getCategories() + ->with('lang') + ->all() as $category) { + /** + * @var Category $category + */ + $categories[] = $category->lang->title; + } + return implode(", ", $categories); + }, + 'filter' => Select2::widget( + [ + 'model' => $searchModel, + 'attribute' => 'categoryId', + 'data' => ArtboxTreeHelper::treeMap( + ProductHelper::getCategories(), + 'id', + 'lang.title' + ), + 'language' => 'ru', + 'options' => [ + 'placeholder' => Yii::t('product', 'Select category'), + 'multiple' => false, + ], + 'pluginOptions' => [ + 'allowClear' => true, + ], + ] + ), + ], + [ + 'attribute' => 'variantCount', + 'value' => function ($model) { + /** + * @var Product $model + */ + return count($model->variants); + }, + ], + [ + 'class' => 'yii\grid\ActionColumn', + 'template' => '{items} {view} |{is_top} {is_new} {is_discount} | {update} {delete}', + 'buttons' => [ + 'is_top' => function ($url, $model) { + return Html::a( + '', + $url, + [ + 'title' => Yii::t('product', ( $model->is_top ? 'Set not is top' : 'Set is top' )), + ] + ); + }, + 'is_new' => function ($url, $model) { + return Html::a( + '', + $url, + [ + 'title' => Yii::t('product', ( $model->is_new ? 'Set not is new' : 'Set is new' )), + ] + ); + }, + 'is_discount' => function ($url, $model) { + return Html::a( + '', + $url, + [ + 'title' => Yii::t( + 'product', + ( $model->is_discount ? 'Set not is promotion' : 'Set is promotion' ) + ), + ] + ); + }, + 'items' => function ($url, $model) { + return Html::a( + '', + $url, + [ + 'title' => Yii::t('product', 'Variants'), + ] + ); + }, + + ], + 'urlCreator' => function ($action, $model, $key, $index) { + /** + * @var Product $model + */ + switch ($action) { + case 'items': + return \yii\helpers\Url::to( + [ + '/product/variant', + 'product_id' => $model->id, + ] + ); + break; + case 'is_top': + return \yii\helpers\Url::to( + [ + 'manage/is_top', + 'id' => $model->id, + ] + ); + break; + case 'is_new': + return \yii\helpers\Url::to( + [ + 'manage/is_new', + 'id' => $model->id, + ] + ); + break; + case 'is_discount': + return \yii\helpers\Url::to( + [ + 'manage/is-discount', + 'id' => $model->id, + ] + ); + break; + case 'view': + return \yii\helpers\Url::to( + [ + 'manage/view', + 'id' => $model->id, + ] + ); + break; + case 'update': + return \yii\helpers\Url::to( + [ + 'manage/update', + 'id' => $model->id, + ] + ); + break; + case 'delete': + return \yii\helpers\Url::to( + [ + 'manage/delete', + 'id' => $model->id, + ] + ); + break; + default: + return ''; + break; + } + }, + ], + ], + ] + ); ?> +
          diff --git a/views/manage/update.php b/views/manage/update.php new file mode 100755 index 0000000..b4c5f9d --- /dev/null +++ b/views/manage/update.php @@ -0,0 +1,42 @@ +title = Yii::t('product', 'Update {modelClass}: ', [ + 'modelClass' => 'Product', + ]) . ' ' . $model->lang->title; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Products'), + 'url' => [ 'index' ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $model->lang->title, + 'url' => [ + 'view', + 'id' => $model->id, + ], + ]; + $this->params[ 'breadcrumbs' ][] = Yii::t('product', 'Update'); +?> +
          + +

          title) ?>

          + + render('_form', [ + 'model' => $model, + 'modelLangs' => $modelLangs, + 'groups' => $groups, + ]) ?> + +
          diff --git a/views/manage/view.php b/views/manage/view.php new file mode 100755 index 0000000..85e8ee7 --- /dev/null +++ b/views/manage/view.php @@ -0,0 +1,140 @@ +title = $model->lang->title; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Products'), + 'url' => [ 'index' ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; + $properties_string = ''; + foreach ($properties as $property) { + $property_list = ''; + foreach ($property->options as $option) { + $property_list .= Html::tag('li', $option->lang->value); + } + $properties_string .= Html::tag('p', $property->lang->title) . Html::tag('ul', $property_list); + } + $variants_string = ''; + foreach ($variants as $variant) { + $variants_string .= Html::a( + $variant->lang->title, + [ + '/product/variant/view', + 'id' => $variant->id, + ] + ) . '
          '; + } +?> +
          + +

          title) ?>

          + +

          + $model->id, + ], + [ 'class' => 'btn btn-primary' ] + ) ?> + $model->id, + ], + [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'), + 'method' => 'post', + ], + ] + ) ?> + $model->id, + ], + [ 'class' => 'btn btn-info' ] + ) ?> +

          + + $model, + 'attributes' => [ + 'id', + 'brand.lang.title', + [ + 'label' => \Yii::t('app', 'Categories'), + 'value' => implode('
          ', ArrayHelper::getColumn($categories, 'lang.title')), + 'format' => 'html', + ], + [ + 'attribute' => 'is_top', + 'value' => $model->is_top ? Html::tag( + 'span', + '', + [ 'class' => 'glyphicon glyphicon-ok' ] + ) : Html::tag('span', '', [ 'class' => 'glyphicon glyphicon-remove' ]), + 'format' => 'html', + ], + [ + 'attribute' => 'is_new', + 'value' => $model->is_new ? Html::tag( + 'span', + '', + [ 'class' => 'glyphicon glyphicon-ok' ] + ) : Html::tag('span', '', [ 'class' => 'glyphicon glyphicon-remove' ]), + 'format' => 'html', + ], + [ + 'attribute' => 'is_discount', + 'value' => $model->is_discount ? Html::tag( + 'span', + '', + [ 'class' => 'glyphicon glyphicon-ok' ] + ) : Html::tag('span', '', [ 'class' => 'glyphicon glyphicon-remove' ]), + 'format' => 'html', + ], + [ + 'attribute' => 'video', + 'format' => 'html', + ], + [ + 'label' => \Yii::t('app', 'Properties'), + 'value' => $properties_string, + 'format' => 'html', + ], + [ + 'label' => \Yii::t('app', 'Variants'), + 'value' => $variants_string, + 'format' => 'html', + ], + 'lang.description:html', + 'image.imageUrl:image', + ], + ] + ) ?> + +
          diff --git a/views/product-unit/_form.php b/views/product-unit/_form.php new file mode 100755 index 0000000..98f221d --- /dev/null +++ b/views/product-unit/_form.php @@ -0,0 +1,37 @@ + + +
          + + + + field($model, 'is_default') + ->checkbox() ?> + + $modelLangs, + 'form' => $form, + 'formView' => '@common/modules/product/views/product-unit/_form_language', + ]) ?> + +
          + isNewRecord ? Yii::t('product', 'Create') : Yii::t('product', 'Update'), [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ]) ?> +
          + + + +
          diff --git a/views/product-unit/_form_language.php b/views/product-unit/_form_language.php new file mode 100755 index 0000000..bf8d725 --- /dev/null +++ b/views/product-unit/_form_language.php @@ -0,0 +1,17 @@ + +field($model_lang, '[' . $language->id . ']title') + ->textInput([ 'maxlength' => true ]); ?> +field($model_lang, '[' . $language->id . ']short') + ->textInput([ 'maxlength' => true ]); ?> \ No newline at end of file diff --git a/views/product-unit/create.php b/views/product-unit/create.php new file mode 100755 index 0000000..20611aa --- /dev/null +++ b/views/product-unit/create.php @@ -0,0 +1,30 @@ +title = Yii::t('product', 'Create Product Unit'); + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Product Units'), + 'url' => [ 'index' ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + + render('_form', [ + 'model' => $model, + 'modelLangs' => $modelLangs, + ]) ?> + +
          diff --git a/views/product-unit/index.php b/views/product-unit/index.php new file mode 100755 index 0000000..ac912eb --- /dev/null +++ b/views/product-unit/index.php @@ -0,0 +1,48 @@ +title = Yii::t('product', 'Product Units'); + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + +

          + 'btn btn-success' ]) ?> +

          + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + 'id', + [ + 'attribute' => 'is_default', + 'format' => 'boolean', + 'filter' => [ + \Yii::$app->formatter->asBoolean(false), + \Yii::$app->formatter->asBoolean(true), + ], + ], + [ + 'attribute' => 'title', + 'value' => 'lang.title', + ], + 'lang.short', + [ 'class' => 'yii\grid\ActionColumn' ], + ], + ] + ); ?> +
          diff --git a/views/product-unit/update.php b/views/product-unit/update.php new file mode 100755 index 0000000..8d3692f --- /dev/null +++ b/views/product-unit/update.php @@ -0,0 +1,39 @@ +title = Yii::t('product', 'Update {modelClass}: ', [ + 'modelClass' => 'Product Unit', + ]) . $model->lang->title; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Product Units'), + 'url' => [ 'index' ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $model->lang->title, + 'url' => [ + 'view', + 'id' => $model->id, + ], + ]; + $this->params[ 'breadcrumbs' ][] = Yii::t('product', 'Update'); +?> +
          + +

          title) ?>

          + + render('_form', [ + 'model' => $model, + 'modelLangs' => $modelLangs, + ]) ?> + +
          diff --git a/views/product-unit/view.php b/views/product-unit/view.php new file mode 100755 index 0000000..36894cd --- /dev/null +++ b/views/product-unit/view.php @@ -0,0 +1,61 @@ +title = $model->lang->title; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Product Units'), + 'url' => [ 'index' ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + +

          + $model->id, + ], + [ 'class' => 'btn btn-primary' ] + ) ?> + $model->id, + ], + [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'), + 'method' => 'post', + ], + ] + ) ?> +

          + + $model, + 'attributes' => [ + 'id', + 'is_default:boolean', + 'lang.title', + 'lang.short', + ], + ] + ) ?> + +
          diff --git a/views/tax-group/_form.php b/views/tax-group/_form.php new file mode 100755 index 0000000..b1b4f2e --- /dev/null +++ b/views/tax-group/_form.php @@ -0,0 +1,56 @@ + + +
          + + [ 'enctype' => 'multipart/form-data' ] ]); ?> + + field($model, 'categories') + ->dropDownList(ArtboxTreeHelper::treeMap(ProductHelper::getCategories(), 'id', 'lang.title'), [ + 'multiple' => true, + ]) + ->label('Use in the following categories') ?> + + field($model, 'is_filter') + ->checkbox() ?> + + field($model, 'display') + ->checkbox() ?> + + field($model, 'is_menu') + ->checkbox() ?> + + field($model, 'sort') + ->textInput() ?> + + $modelLangs, + 'formView' => '@common/modules/rubrication/views/tax-group/_form_language', + 'form' => $form, + ]); + ?> + +
          + isNewRecord ? Yii::t('rubrication', 'Create') : Yii::t('rubrication', 'Update'), [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ]) ?> +
          + + + +
          diff --git a/views/tax-group/_form_language.php b/views/tax-group/_form_language.php new file mode 100755 index 0000000..5a78eea --- /dev/null +++ b/views/tax-group/_form_language.php @@ -0,0 +1,19 @@ + +field($model_lang, '[' . $language->id . ']title') + ->textInput([ 'maxlength' => true ]); ?> +field($model_lang, '[' . $language->id . ']alias') + ->textInput([ 'maxlength' => true ]); ?> +field($model_lang, '[' . $language->id . ']description') + ->textarea([ 'rows' => 6 ]) ?> \ No newline at end of file diff --git a/views/tax-group/create.php b/views/tax-group/create.php new file mode 100755 index 0000000..3bf2f17 --- /dev/null +++ b/views/tax-group/create.php @@ -0,0 +1,29 @@ +title = Yii::t('rubrication', 'Create Tax Group'); + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('rubrication', 'Tax Groups'), + 'url' => [ 'index' ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + + render('_form', [ + 'model' => $model, + 'modelLangs' => $modelLangs, + ]) ?> + +
          diff --git a/views/tax-group/index.php b/views/tax-group/index.php new file mode 100755 index 0000000..4d1af2d --- /dev/null +++ b/views/tax-group/index.php @@ -0,0 +1,116 @@ +title = $level ? Yii::t('rubrication', 'Modification Groups') : Yii::t('rubrication', 'Product Groups'); + $this->params[ 'breadcrumbs' ][] = $this->title; +?> + +
          + +

          title) ?>

          +

          + $level, + ] + ), + [ 'class' => 'btn btn-success' ] + ) ?> +

          + + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + [ 'class' => 'yii\grid\SerialColumn' ], + 'id', + [ + 'attribute' => 'is_filter', + 'format' => 'boolean', + 'filter' => \Yii::$app->formatter->booleanFormat, + ], + [ + 'attribute' => 'groupName', + 'value' => 'lang.title', + ], + [ + 'label' => \Yii::t('rubrication', 'Options count'), + 'value' => function ($model) { + /** + * @var TaxGroup $model + */ + return count($model->options); + }, + ], + [ + 'label' => \Yii::t('rubrication', 'Categories count'), + 'value' => function ($model) { + /** + * @var TaxGroup $model + */ + return count($model->categories); + }, + ], + [ + 'class' => 'yii\grid\ActionColumn', + 'template' => '{update} {options} {delete}', + 'buttons' => [ + 'options' => function ($url, $model) { + return Html::a( + '', + $url, + [ + 'title' => Yii::t('rubrication', 'Options'), + ] + ); + }, + ], + 'urlCreator' => function ($action, $model, $key, $index) use ($level) { + if ($action === 'options') { + $url = '/admin/rubrication/tax-option?group=' . $model->id; + return $url; + } elseif ($action === 'update') { + $url = Url::to( + [ + '/rubrication/tax-group/update', + 'level' => $level, + 'id' => $model->id, + ] + ); + return $url; + } elseif ($action === 'delete') { + $url = Url::to( + [ + '/rubrication/tax-group/delete', + 'level' => $level, + 'id' => $model->id, + ] + ); + return $url; + } + return ''; + }, + ], + ], + ] + ); ?> +
          + diff --git a/views/tax-group/update.php b/views/tax-group/update.php new file mode 100755 index 0000000..454b852 --- /dev/null +++ b/views/tax-group/update.php @@ -0,0 +1,36 @@ +title = Yii::t('rubrication', 'Update {modelClass}: ', [ + 'modelClass' => 'Tax Group', + ]) . ' ' . $model->lang->title; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $level ? Yii::t('rubrication', 'Modification Groups') : Yii::t('rubrication', 'Product Groups'), + 'url' => [ + 'index', + 'level' => $level, + ], + ]; + $this->params[ 'breadcrumbs' ][] = Yii::t('rubrication', 'Update'); +?> +
          + +

          title) ?>

          + + render('_form', [ + 'model' => $model, + 'modelLangs' => $modelLangs, + ]) ?> + +
          diff --git a/views/tax-group/view.php b/views/tax-group/view.php new file mode 100755 index 0000000..4594b87 --- /dev/null +++ b/views/tax-group/view.php @@ -0,0 +1,60 @@ +title = $model->id; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('rubrication', 'Tax Groups'), + 'url' => [ 'index' ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + +

          + $model->id, + ], + [ 'class' => 'btn btn-primary' ] + ) ?> + $model->id, + ], + [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => Yii::t('rubrication', 'Are you sure you want to delete this item?'), + 'method' => 'post', + ], + ] + ) ?> + id ], + [ 'class' => 'btn btn-success' ] + ) ?> +

          + + $model, + 'attributes' => [ + 'id', + 'is_filter:boolean', + ], + ] + ) ?> + +
          diff --git a/views/tax-option/_form.php b/views/tax-option/_form.php new file mode 100755 index 0000000..b533917 --- /dev/null +++ b/views/tax-option/_form.php @@ -0,0 +1,102 @@ + + +
          + + [ 'enctype' => 'multipart/form-data' ] ]); ?> + id )) : ?> + field($model, 'tax_group_id') + ->dropDownList( + ArrayHelper::map( + TaxOption::find() + ->all(), + 'tax_group_id', + 'tax_group_id' + ), + [ + 'prompt' => Yii::t('rubrication', 'Select group'), + ] + ) ?> + + field($model, 'tax_group_id') + ->hiddenInput() + ->label('') ?> + + + field($model, 'image') + ->widget( + \kartik\file\FileInput::className(), + [ + 'language' => 'ru', + 'options' => [ + 'accept' => 'image/*', + 'multiple' => false, + ], + 'pluginOptions' => [ + 'allowedFileExtensions' => [ + 'jpg', + 'gif', + 'png', + ], + 'initialPreview' => !empty( $model->imageUrl ) ? \common\components\artboximage\ArtboxImageHelper::getImage( + $model->imageUrl, + 'list' + ) : '', + 'overwriteInitial' => true, + 'showRemove' => false, + 'showUpload' => false, + 'previewFileType' => 'image', + ], + ] + ) + ->hint( + ( ( $model->tax_group_id == 5 ) ? 'Для корректного отображения на сайте, размер изображения должен быть 262x144 либо соблюдать соотношение сторон примерно 2:1' : '' ) + ); ?> + field($model, 'sort') + ->textInput() ?> + + $modelLangs, + 'formView' => '@common/modules/rubrication/views/tax-option/_form_language', + 'form' => $form, + ] + ); + ?> + +
          + isNewRecord ? Yii::t('rubrication', 'Create') : Yii::t('rubrication', 'Update'), + [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ] + ) ?> + isNewRecord) : ?> + 'create_and_new', + 'class' => 'btn btn-primary', + ] + ) ?> + +
          + + + +
          diff --git a/views/tax-option/_form_language.php b/views/tax-option/_form_language.php new file mode 100755 index 0000000..6f7d22a --- /dev/null +++ b/views/tax-option/_form_language.php @@ -0,0 +1,17 @@ + +field($model_lang, '[' . $language->id . ']value') + ->textInput([ 'maxlength' => true ]); ?> +field($model_lang, '[' . $language->id . ']alias') + ->textInput([ 'maxlength' => true ]); ?> diff --git a/views/tax-option/create.php b/views/tax-option/create.php new file mode 100755 index 0000000..f1ba296 --- /dev/null +++ b/views/tax-option/create.php @@ -0,0 +1,49 @@ +title = Yii::t('rubrication', 'Create Tax Option'); + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $group->level ? Yii::t('rubrication', 'Modification Groups') : Yii::t('rubrication', 'Product Groups'), + 'url' => [ + 'tax-group/index', + 'level' => $group->level, + ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $group->lang->title, + 'url' => [ + 'tax-group/update', + 'id' => $group->id, + 'level' => $group->level, + ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('rubrication', 'Options for group {group}', [ 'group' => $group->lang->title ]), + 'url' => [ + 'index', + 'group' => $group->id, + 'level' => $group->level, + ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + + render('_form', [ + 'model' => $model, + 'modelLangs' => $modelLangs, + 'group' => $group, + ]) ?> + +
          diff --git a/views/tax-option/index.php b/views/tax-option/index.php new file mode 100755 index 0000000..12de0fe --- /dev/null +++ b/views/tax-option/index.php @@ -0,0 +1,79 @@ +title = Yii::t('rubrication', 'Options for group {group}', [ 'group' => $group->lang->title ]); + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $group->level ? Yii::t('rubrication', 'Modification Groups') : Yii::t( + 'rubrication', + 'Product Groups' + ), + 'url' => [ + 'tax-group/index', + 'level' => $group->level, + ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $group->lang->title, + 'url' => [ + 'tax-group/update', + 'id' => $group->id, + 'level' => $group->level, + ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + +

          + id ], + [ 'class' => 'btn btn-success' ] + ) ?> +

          + + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + [ 'class' => 'yii\grid\SerialColumn' ], + 'id', + [ + 'attribute' => 'value', + 'value' => 'lang.value', + ], + 'imageUrl:image', + [ + 'label' => $group->level ? \Yii::t('rubrication', 'Variants count') : \Yii::t( + 'rubrication', + 'Products count' + ), + 'value' => function ($model) use ($group) { + /** + * @var TaxOption $model + */ + return count($group->level ? $model->productVariants : $model->products); + }, + ], + [ + 'class' => 'yii\grid\ActionColumn', + 'template' => '{update} {delete}', + ], + ], + ] + ); ?> +
          diff --git a/views/tax-option/update.php b/views/tax-option/update.php new file mode 100755 index 0000000..ffd5e15 --- /dev/null +++ b/views/tax-option/update.php @@ -0,0 +1,51 @@ +title = Yii::t('rubrication', 'Update {modelClass}: ', [ + 'modelClass' => 'Tax Option', + ]) . ' ' . $model->lang->value; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $group->level ? Yii::t('rubrication', 'Modification Groups') : Yii::t('rubrication', 'Product Groups'), + 'url' => [ + 'tax-group/index', + 'level' => $group->level, + ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $group->lang->title, + 'url' => [ + 'tax-group/update', + 'id' => $group->id, + 'level' => $group->level, + ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('rubrication', 'Options for group {group}', [ 'group' => $group->lang->title ]), + 'url' => [ + 'index', + 'group' => $group->id, + 'level' => $group->level, + ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + + render('_form', [ + 'model' => $model, + 'modelLangs' => $modelLangs, + 'group' => $group, + ]) ?> + +
          diff --git a/views/tax-option/view.php b/views/tax-option/view.php new file mode 100755 index 0000000..29960b4 --- /dev/null +++ b/views/tax-option/view.php @@ -0,0 +1,81 @@ +title = $model->id; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('rubrication', 'Groups'), + 'url' => [ 'tax-group/index' ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('rubrication', $group->id), + 'url' => [ + 'index', + 'group' => $group->id, + ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('rubrication', Yii::t('rubrication', 'Options of {title}', [ 'title' => $group->id ])), + 'url' => [ + 'index', + 'group' => $group->id, + ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + +

          + $model->id, + ], + [ 'class' => 'btn btn-primary' ] + ) ?> + $model->id, + ], + [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => Yii::t('rubrication', 'Are you sure you want to delete this item?'), + 'method' => 'post', + ], + ] + ) ?> + id ], + [ 'class' => 'btn btn-success' ] + ) ?> +

          + + $model, + 'attributes' => [ + 'id', + 'group.id', + 'sort', + ], + ] + ) ?> + +
          diff --git a/views/variant/_form.php b/views/variant/_form.php new file mode 100755 index 0000000..abbff5f --- /dev/null +++ b/views/variant/_form.php @@ -0,0 +1,207 @@ +registerJs($js, View::POS_END); +?> +
          + + 'dynamic-form', + 'options' => [ 'enctype' => 'multipart/form-data' ], + ] + ); ?> + + field($model, 'product_id') + ->hiddenInput() + ->label(false); ?> + + field($model, 'sku') + ->textarea(); ?> + field($model, 'price') + ->textarea(); ?> + field($model, 'price_old') + ->textarea(); ?> + field($model, 'imagesUpload[]') + ->widget( + \kartik\file\FileInput::className(), + [ + 'language' => 'ru', + 'options' => [ + 'accept' => 'image/*', + 'multiple' => true, + ], + 'pluginOptions' => [ + 'allowedFileExtensions' => [ + 'jpg', + 'gif', + 'png', + ], + 'initialPreview' => !empty( $model->imagesHTML ) ? $model->imagesHTML : [], + 'initialPreviewConfig' => $model->imagesConfig, + 'overwriteInitial' => false, + 'showRemove' => false, + 'showUpload' => false, + 'uploadAsync' => !empty( $model->id ), + 'previewFileType' => 'image', + ], + ] + ); ?> + + $modelLangs, + 'formView' => '@common/modules/product/views/variant/_form_language', + 'form' => $form, + ] + ) ?> + + 'dynamicform_wrapper', + // required: only alphanumeric characters plus "_" [A-Za-z0-9_] + 'widgetBody' => '.container-items', + // required: css class selector + 'widgetItem' => '.item', + // required: css class + 'limit' => 10, + // the maximum times, an element can be added (default 999) + 'min' => 0, + // 0 or 1 (default 1) + 'insertButton' => '.add-item', + // css class + 'deleteButton' => '.remove-item', + // css class + 'model' => $stocks[ 0 ], + 'formId' => 'dynamic-form', + 'formFields' => [ + 'quantity', + 'title', + ], + ] + ); ?> + +
          +
          +

          + Склады + +

          +
          +
          +
          + $stock): ?> +
          +
          + isNewRecord) { + echo Html::activeHiddenInput($stock, "[{$i}]stock_id"); + } + ?> +
          +
          + field($stock, "[{$i}]quantity") + ->textInput([ 'maxlength' => true ]) ?> +
          +
          + field($stock, "[{$i}]title") + ->textInput([ 'maxlength' => true ]) ?> +
          +
          + +
          +
          +
          +
          + +
          +
          +
          + + + field($model, 'product_unit_id') + ->dropDownList( + ArrayHelper::map( + ProductUnit::find() + ->with('lang') + ->all(), + 'id', + 'lang.title' + ) + ) + ->label(Yii::t('product', 'Unit')) ?> + + with('lang') + ->all() as $group) { + /** + * @var TaxGroup $group + */ + echo $form->field($model, 'options') + ->checkboxList( + ArrayHelper::map( + $group->getOptions() + ->with('lang') + ->all(), + 'id', + 'lang.value' + ), + [ + 'multiple' => true, + 'unselect' => null, + ] + ) + ->label($group->lang->title); + } + } ?> + +
          + isNewRecord ? Yii::t('product', 'Create') : Yii::t('product', 'Update'), + [ 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary' ] + ) ?> +
          + + + +
          diff --git a/views/variant/_form_language.php b/views/variant/_form_language.php new file mode 100755 index 0000000..39c1482 --- /dev/null +++ b/views/variant/_form_language.php @@ -0,0 +1,15 @@ + +field($model_lang, '[' . $language->id . ']title') + ->textInput([ 'maxlength' => true ]); ?> \ No newline at end of file diff --git a/views/variant/create.php b/views/variant/create.php new file mode 100755 index 0000000..f432298 --- /dev/null +++ b/views/variant/create.php @@ -0,0 +1,55 @@ +title = Yii::t('product', 'Create Variant'); + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Products'), + 'url' => [ '/product/manage/index' ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $product->lang->title, + 'url' => [ + '/product/manage/view', + 'id' => $product->id, + ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Variants'), + 'url' => [ + 'index', + 'product_id' => $product->id, + ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
          + +

          title) ?>

          + + render( + '_form', + [ + 'model' => $model, + 'modelLangs' => $modelLangs, + 'groups' => $groups, + 'stocks' => $stocks, + 'product' => $product, + ] + ) ?> + +
          diff --git a/views/variant/index.php b/views/variant/index.php new file mode 100755 index 0000000..2e8cb34 --- /dev/null +++ b/views/variant/index.php @@ -0,0 +1,120 @@ +title = Yii::t('product', 'Variants for ') . $product->lang->title; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Products'), + 'url' => [ '/product/manage/index' ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $product->lang->title, + 'url' => [ + '/product/manage/view', + 'id' => $product->id, + ], + ]; + $this->params[ 'breadcrumbs' ][] = \Yii::t('product', 'Variants'); +?> +
          + +

          title) ?>

          + +

          + $product->id, + ] + ), + [ 'class' => 'btn btn-success' ] + ) ?> +

          + $dataProvider, + 'filterModel' => $searchModel, + 'columns' => [ + 'id', + [ + 'attribute' => 'variantName', + 'value' => 'lang.title', + ], + 'sku', + 'price', + 'price_old', + 'stock', + 'image.imageUrl:image', + [ + 'class' => 'yii\grid\ActionColumn', + 'buttons' => [ + 'view' => function ($url, $model) { + return Html::a( + '', + Url::to( + [ + 'view', + 'product_id' => $model->product_id, + 'id' => $model->id, + ] + ), + [ + 'title' => \Yii::t('app', "Просмотр"), + ] + ); + }, + 'update' => function ($url, $model) { + return Html::a( + '', + Url::to( + [ + 'update', + 'product_id' => $model->product_id, + 'id' => $model->id, + ] + ), + [ + 'title' => \Yii::t('app', "Редактировать"), + ] + ); + }, + 'delete' => function ($url, $model) { + + return Html::a( + '', + Url::to( + [ + 'delete', + 'product_id' => $model->product_id, + 'id' => $model->id, + ] + ), + [ + 'title' => Yii::t('yii', 'Delete'), + 'data-confirm' => Yii::t('yii', 'Are you sure to delete this item?'), + 'data-method' => 'post', + ] + ); + + }, + ], + ], + ], + ] + ); ?> +
          diff --git a/views/variant/update.php b/views/variant/update.php new file mode 100755 index 0000000..85c329f --- /dev/null +++ b/views/variant/update.php @@ -0,0 +1,73 @@ +title = Yii::t( + 'product', + 'Update {modelClass}: ', + [ + 'modelClass' => 'Product', + ] + ) . ' ' . $model->lang->title; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Products'), + 'url' => [ '/product/manage/index' ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $model->product->lang->title, + 'url' => [ + '/product/manage/view', + 'id' => $model->product->id, + ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Variants'), + 'url' => Url::to( + [ + 'index', + 'product_id' => $model->product->id, + ] + ), + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', $model->lang->title), + 'url' => Url::to( + [ + 'view', + 'id' => $model->id, + ] + ), + ]; + $this->params[ 'breadcrumbs' ][] = Yii::t('product', 'Update'); +?> +
          + +

          title) ?>

          + + render( + '_form', + [ + 'model' => $model, + 'modelLangs' => $modelLangs, + 'groups' => $groups, + 'stocks' => $stocks, + 'product' => $product, + ] + ) ?> + +
          diff --git a/views/variant/view.php b/views/variant/view.php new file mode 100755 index 0000000..2a707e8 --- /dev/null +++ b/views/variant/view.php @@ -0,0 +1,102 @@ +title = $model->lang->title; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Products'), + 'url' => [ 'index' ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => $model->product->lang->title, + 'url' => [ + 'view', + 'id' => $model->product->id, + ], + ]; + $this->params[ 'breadcrumbs' ][] = [ + 'label' => Yii::t('product', 'Variants'), + 'url' => [ '/product/variant?product_id=' . $model->product->id ], + ]; + $this->params[ 'breadcrumbs' ][] = $this->title; + $properties_string = ''; + foreach ($properties as $property) { + $options_string = ''; + foreach ($property->options as $option) { + $options_string .= Html::tag('li', $option->lang->value); + } + $properties_string .= Html::tag('p', $property->lang->title) . Html::tag('ul', $options_string); + } +?> +
          + +

          title) ?>

          + +

          + $model->id, + ], + [ 'class' => 'btn btn-primary' ] + ) ?> + $model->id, + ], + [ + 'class' => 'btn btn-danger', + 'data' => [ + 'confirm' => Yii::t('product', 'Are you sure you want to delete this item?'), + 'method' => 'post', + ], + ] + ) ?> +

          + + $model, + 'attributes' => [ + 'id', + 'lang.title', + 'sku', + 'price', + 'price_old', + 'stock', + 'productUnit.lang.title', + [ + 'attribute' => 'product_id', + 'value' => Html::a( + $model->product->fullname, + [ + '/product/manage/view', + 'id' => $model->id, + ] + ), + 'format' => 'html', + ], + 'image.imageUrl:image', + [ + 'label' => \Yii::t('app', 'Properties'), + 'value' => $properties_string, + 'format' => 'html', + ], + ], + ] + ) ?> + +
          diff --git a/widgets/brandsCarouselWidget.php b/widgets/brandsCarouselWidget.php new file mode 100755 index 0000000..f9713f1 --- /dev/null +++ b/widgets/brandsCarouselWidget.php @@ -0,0 +1,29 @@ +with('lang') + ->all(); + return $this->render( + 'brandsCarousel', + [ + 'brands' => $brands, + ] + ); + } + } + \ No newline at end of file diff --git a/widgets/lastProducts.php b/widgets/lastProducts.php new file mode 100755 index 0000000..14e4495 --- /dev/null +++ b/widgets/lastProducts.php @@ -0,0 +1,28 @@ +render( + 'products_block', + [ + 'title' => \Yii::t('product', 'Вы недавно просматривали'), + 'class' => 'last-products', + 'products' => ProductHelper::getLastProducts(true), + ] + ); + } + } + \ No newline at end of file diff --git a/widgets/similarProducts.php b/widgets/similarProducts.php new file mode 100755 index 0000000..fbcc712 --- /dev/null +++ b/widgets/similarProducts.php @@ -0,0 +1,41 @@ +product, $this->count); + + if (!$this->title) { + $this->title = Yii::t('product', 'Similar products'); + } + + return $this->render( + 'products_block', + [ + 'title' => $this->title, + 'class' => 'similar-products', + 'products' => $products, + ] + ); + } + } + \ No newline at end of file diff --git a/widgets/specialProducts.php b/widgets/specialProducts.php new file mode 100755 index 0000000..f8aad91 --- /dev/null +++ b/widgets/specialProducts.php @@ -0,0 +1,51 @@ +type, $this->count); + + if (!$this->title) { + switch ($this->type) { + case 'top': + $this->title = Yii::t('product', 'Top products'); + break; + case 'promo': + $this->title = Yii::t('product', 'Promo products'); + break; + case 'new': + $this->title = Yii::t('product', 'New products'); + break; + } + } + + return $this->render( + 'products_block', + [ + 'title' => $this->title, + 'class' => $this->type, + 'products' => $products, + ] + ); + } + } + \ No newline at end of file diff --git a/widgets/views/brandsCarousel.php b/widgets/views/brandsCarousel.php new file mode 100755 index 0000000..845ed10 --- /dev/null +++ b/widgets/views/brandsCarousel.php @@ -0,0 +1,21 @@ + + \ No newline at end of file diff --git a/widgets/views/product_smart.php b/widgets/views/product_smart.php new file mode 100755 index 0000000..4db88e7 --- /dev/null +++ b/widgets/views/product_smart.php @@ -0,0 +1,110 @@ + +
          +
          +
          + +
          + lang->title, + Url::to( + [ + 'catalog/product', + 'product' => $product->lang->alias, + ] + ), + [ 'class' => 'btn-product-details' ] + ) ?> +
          + +
          +
          + Цена: + + variant->price ?> + грн + +
          +
          +
          +
          Особенности
          +
          +
          +
            + +
          • Бренд: brand->lang->title ?>
          • + + getProperties() as $group): ?> +
          • + lang->title ?> customOptions as $option ) : ?> lang->value ?> +
          • + + + +
          +
          +
          +
          + +
          +
          +
          +
          +
          +
          \ No newline at end of file diff --git a/widgets/views/products_block.php b/widgets/views/products_block.php new file mode 100755 index 0000000..ba647d7 --- /dev/null +++ b/widgets/views/products_block.php @@ -0,0 +1,35 @@ + + +
          +
          + +
          + + render('product_smart', [ 'product' => $product ]); ?> + +
          +
          +
          +
          + registerJs($js, View::POS_READY); + ?> + \ No newline at end of file diff --git a/widgets/views/submenu.php b/widgets/views/submenu.php new file mode 100755 index 0000000..ccae3ff --- /dev/null +++ b/widgets/views/submenu.php @@ -0,0 +1,44 @@ + + -- libgit2 0.21.4