<?php
if (!defined("ROOT_PATH"))
{
	header("HTTP/1.1 403 Forbidden");
	exit;
}
class pjAdminProducts extends pjAdmin
{
	public function pjActionOpenDigital()
	{
		$this->checkLogin();
	    if (!pjAuth::factory()->hasAccess())
	    {
	        $this->sendForbidden();
	        return;
	    }

		if ($this->_get->check('id') && $this->_get->toInt('id') > 0)
		{
			$arr = pjProductModel::factory()->find($this->_get->toInt('id'))->getData();
			if (empty($arr))
			{
				exit;
			}
			if ((int) $arr['is_digital'] !== 1)
			{
				exit;
			}
			if (empty($arr['digital_file']) || !is_file($arr['digital_file']))
			{
				exit;
			}
			$zip = new pjZipStream();
			
			$handle = @fopen($arr['digital_file'], "rb");
			if ($handle)
			{
				$zip->addLargeFile($handle, $arr['digital_name']);
				fclose($handle);
			}
			$zip->finalize();
			$zip->sendZip(sprintf("%s.zip", $arr['digital_name']));
			exit;
			
		} else {
			exit;
		}
	}

	public function pjActionAttrGroupDelete()
	{
		$this->setAjax(true);
		
		if ($this->isXHR() && $this->isLoged())
		{
			pjAppController::validateCsrfToken();
			if ($this->_post->check('id') && (int) $this->_post->toInt('id') > 0)
			{
				$pjAttributeModel = pjAttributeModel::factory();
				$ids = $pjAttributeModel->where('t1.parent_id', $this->_post->toInt('id'))->findAll()->getDataPair(null, 'id');
				if ($pjAttributeModel->reset()->set('id', $this->_post->toInt('id'))->erase()->getAffectedRows() == 1)
				{
					$pjMultiLangModel = pjMultiLangModel::factory();
					$pjMultiLangModel->where('model', 'pjAttribute')->where('foreign_id', $this->_post->toInt('id'))->eraseAll();
					
					if (!empty($ids))
					{
						$pjAttributeModel->reset()->whereIn('id', $ids)->eraseAll();
						$pjMultiLangModel->reset()->where('model', 'pjAttribute')->whereIn('foreign_id', $ids)->eraseAll();
					}
					
					pjAppController::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => ''));
				}
			}
			pjAppController::jsonResponse(array('status' => 'ERR', 'code' => 100, 'text' => ''));
		}
		exit;
	}
	
	public function pjActionAttrDelete()
	{
		$this->setAjax(true);
		
		if ($this->isXHR() && $this->isLoged())
		{
			pjAppController::validateCsrfToken();
			if ($this->_post->check('id') && (int) $this->_post->toInt('id') > 0)
			{
				if (pjAttributeModel::factory()->set('id', $this->_post->toInt('id'))->erase()->getAffectedRows() == 1)
				{
					pjMultiLangModel::factory()->where('model', 'pjAttribute')->where('foreign_id', $this->_post->toInt('id'))->eraseAll();
					pjAppController::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => ''));
				}
			}
			pjAppController::jsonResponse(array('status' => 'ERR', 'code' => 100, 'text' => ''));
		}
		exit;
	}
	
	public function pjActionAttrCopy()
	{
		$this->setAjax(true);
		
		if ($this->isXHR() && $this->isLoged())
		{
			pjAppController::validateCsrfToken();
			if ($this->_post->check('from_product_id') && $this->_post->toInt('from_product_id') > 0)
			{
				if ($this->_post->check('product_id') && $this->_post->toInt('product_id') > 0)
				{
					$ak = 'product_id';
					$av = $this->_post->toInt('product_id');
				} elseif ($this->_post->check('hash') && $this->_post->toString('hash') != '') {
					$ak = 'hash';
					$av = $this->_post->toString('hash');
				}
			
				$pjAttributeModel = pjAttributeModel::factory();
				$pjMultiLangModel = pjMultiLangModel::factory();
				
				$attr = $pjAttributeModel->where('t1.product_id', $this->_post->toInt('from_product_id'))->orderBy('t1.`order_group`, `order_item` ASC')->findAll()->getDataPair('id', 'parent_id');
				$arr = array();
				foreach ($attr as $id => $parent_id)
				{
					if (empty($parent_id))
					{
						$arr[$id] = array();
					} else {
						$arr[$parent_id][] = $id;
					}
				}
				
				$multi = $pjMultiLangModel
					->where('t1.model', 'pjAttribute')
					->whereIn('t1.foreign_id', array_keys($attr))
					->where("t1.field='name'")
					->findAll()
					->getData();

				$stack = array();
				foreach ($multi as $item)
				{
					if (!isset($stack[$item['foreign_id']]))
					{
						$stack[$item['foreign_id']] = array();
					}
					$stack[$item['foreign_id']][] = $item;
				}
				
				$last_order = $pjAttributeModel->getLastOrder($this->_post->toInt('product_id'));
				
				foreach ($arr as $parent_id => $items)
				{
					$insert_id = $pjAttributeModel->reset()->setAttributes(array($ak => $av, 'order_group' => $last_order))->insert()->getInsertId();
					if ($insert_id !== false && (int) $insert_id > 0)
					{
						if (isset($stack[$parent_id]))
						{
							foreach ($stack[$parent_id] as $locale)
							{
								$pjMultiLangModel->reset()->setAttributes(array(
									'model' => $locale['model'],
									'foreign_id' => $insert_id,
									'field' => $locale['field'],
									'locale' => $locale['locale'],
									'content' => $locale['content'],
									'source' => 'data'
								))->insert();
							}
						}
					
						$item_order = 0;
						foreach ($items as $id)
						{
							$attr_id = $pjAttributeModel->reset()->setAttributes(array($ak => $av, 'parent_id' => $insert_id, 'order_group' => $last_order, 'order_item'=>$item_order))->insert()->getInsertId();
							if ($attr_id !== false && (int) $attr_id > 0)
							{
								if (isset($stack[$id]))
								{
									foreach ($stack[$id] as $locale)
									{
										$pjMultiLangModel->reset()->setAttributes(array(
											'model' => $locale['model'],
											'foreign_id' => $attr_id,
											'field' => $locale['field'],
											'locale' => $locale['locale'],
											'content' => $locale['content'],
											'source' => 'data'
										))->insert();
									}
								}
								$item_order++;
							}
						}
					}
					$last_order++;
				}
				pjAppController::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => ''));
			}
		}
		exit;
	}
	
	private function pjActionAttrHandle($product_id)
	{
		if ($attr_arr = $this->_post->toArray('attr'))
		{
			$pjAttributeModel = pjAttributeModel::factory();
			$pjMultiLangModel = pjMultiLangModel::factory();
			
			$i18n_arr = $this->_post->toArray('i18n');
			$keys = array_keys($i18n_arr);
			$fkey = $keys[0];
			
			$group_arr = $this->_post->check('orderAttributes') && $this->_post->toString('orderAttributes') != '' ? explode("|", $this->_post->toString('orderAttributes')) : array();
			$order_group_arr = array();
			foreach($group_arr as $k => $v)
			{
				$order_group_arr[$v] = $k;
			}
			foreach ($attr_arr as $group_id => $whatever)
			{
				if (strpos($group_id, 'x_') === 0)
				{
					// insert new group attr
					$attr_group_id = $pjAttributeModel->reset()
						->setAttributes(array(
								'product_id'=> $product_id,
								'order_group'=> $order_group_arr['attrBox_' . $group_id]
							))
						->insert()->getInsertId();
					if ($attr_group_id !== false && (int) $attr_group_id > 0)
					{
						$tmp = $this->pjActionTurnI18n($i18n_arr, 'attr_group', $group_id, NULL, 'name');
						$pjMultiLangModel->saveMultiLang($tmp, $attr_group_id, 'pjAttribute');
						
						$item_arr = $this->_post->check('orderItems_' . $group_id) &&  $this->_post->toString('orderItems_' . $group_id) != '' ? explode("|", $this->_post->toString('orderItems_' . $group_id)) : array();
						$order_item_arr = array();
						foreach($item_arr as $k => $v)
						{
							$order_item_arr[$v] = $k;
						}
						if(isset($i18n_arr[$fkey]['attr_item'][$group_id]) && count(($i18n_arr[$fkey]['attr_item'][$group_id])) > 0)
						{
							foreach ($i18n_arr[$fkey]['attr_item'][$group_id] as $index => $value)
							{
								$attr_item_id = $pjAttributeModel->reset()->setAttributes(array(
									'product_id' => $product_id,
									'parent_id' => $attr_group_id,
									'order_group'=> $order_group_arr['attrBox_' . $group_id],
									'order_item'=> $order_item_arr['attrBoxRowItems_' . $index]
								))->insert()->getInsertId();
								if ($attr_item_id !== false && (int) $attr_item_id > 0)
								{
									$tmp = $this->pjActionTurnI18n($i18n_arr, 'attr_item', $group_id, $index, 'name');
									$pjMultiLangModel->saveMultiLang($tmp, $attr_item_id, 'pjAttribute');
								}
							}
						}
					}
				} else {
					// update group attr
					$tmp = $this->pjActionTurnI18n($i18n_arr, 'attr_group', $group_id, NULL, 'name');
					$pjMultiLangModel->updateMultiLang($tmp, $group_id, 'pjAttribute');

					$pjAttributeModel->reset()->set('id', $group_id)->modify(array(
							'order_group' => $order_group_arr['attrBox_' . $group_id]
					));
					$item_arr = $this->_post->check('orderItems_' . $group_id) && $this->_post->toString('orderItems_' . $group_id) != '' ? explode("|", $this->_post->toString('orderItems_' . $group_id)) : array();
					$order_item_arr = array();
					foreach($item_arr as $k => $v)
					{
						$order_item_arr[$v] = $k;
					}
					if(isset($i18n_arr[$fkey]['attr_item'][$group_id]) && is_array($i18n_arr[$fkey]['attr_item'][$group_id]))
					{
						foreach ($i18n_arr[$fkey]['attr_item'][$group_id] as $index => $value)
						{
							if (strpos($index, 'y_') === 0)
							{
								# Add items
								$attr_item_id = $pjAttributeModel->reset()->setAttributes(array(
									'product_id' => $product_id,
									'parent_id' => $group_id,
									'order_group'=> $order_group_arr['attrBox_' . $group_id],
									'order_item'=> $order_item_arr['attrBoxRowItems_' . $index]
								))->insert()->getInsertId();
								if ($attr_item_id !== false && (int) $attr_item_id > 0)
								{
									$tmp = $this->pjActionTurnI18n($i18n_arr, 'attr_item', $group_id, $index, 'name');
									$pjMultiLangModel->saveMultiLang($tmp, $attr_item_id, 'pjAttribute');
								}
							} else {
								# Update items
								$tmp = $this->pjActionTurnI18n($i18n_arr, 'attr_item', $group_id, $index, 'name');
								$pjAttributeModel->reset()->set('id', $index)->modify(array(
										'order_group'=> $order_group_arr['attrBox_' . $group_id],
										'order_item' => $order_item_arr['attrBoxRowItems_' . $index]
								));
								$pjMultiLangModel->updateMultiLang($tmp, $index, 'pjAttribute');
							}
						}
					}
				}
			}
			foreach ($i18n_arr as $locale_id => $whatever)
			{
				if (isset($i18n_arr[$locale_id]['attr_group']))
				{
					unset($i18n_arr[$locale_id]['attr_group']);
				}
				if (isset($i18n_arr[$locale_id]['attr_item']))
				{
					unset($i18n_arr[$locale_id]['attr_item']);
				}
			}
			return $i18n_arr;
		}
	}
	
	public function pjActionCheckSku()
	{
		$this->setAjax(true);
	
		if ($this->isXHR())
		{
			if (!$this->_get->check('sku') || $this->_get->toString('sku') == '')
			{
				echo 'false';
				exit;
			}
			$pjProductModel = pjProductModel::factory()->where('t1.sku', $this->_get->toString('sku'));
			if ($this->_get->check('id') && $this->_get->toInt('id') > 0)
			{
				$pjProductModel->where('t1.id !=', $this->_get->toInt('id'));
			}
			echo $pjProductModel->findCount()->getData() == 0 ? 'true' : 'false';
		}
		exit;
	}
	
	public function pjActionCreate()
	{
		$this->checkLogin();
	    if (!pjAuth::factory()->hasAccess())
	    {
	        $this->sendForbidden();
	        return;
	    }

		if (self::isPost() && $this->_post->toInt('product_create'))
		{
			$pjProductModel = pjProductModel::factory();
	        
	        $data = array();
	        $data['is_featured'] = $this->_post->check('is_featured') ? 1 : 0;
	        $id = $pjProductModel->setAttributes(array_merge($this->_post->raw(), $data))->insert()->getInsertId();
	        if ($id !== false && (int) $id > 0)
	        {
	        	$err = 'AP01';
	        	if ($i18n_arr = $this->_post->toArray('i18n')) {
	        		pjMultiLangModel::factory()->saveMultiLang($i18n_arr, $id, 'pjProduct', 'data');
	        	}
	        	$pjProductCategoryModel = pjProductCategoryModel::factory();
	            if ($category_id_arr = $this->_post->toArray('category_id'))
	            {
	                $pjProductCategoryModel->begin();
	                foreach ($category_id_arr as $category_id)
	                {
	                    $pjProductCategoryModel
	                    ->reset()
	                    ->set('product_id', $id)
	                    ->set('category_id', $category_id)
	                    ->insert();
	                }
	                $pjProductCategoryModel->commit();
	            }
	        	pjUtil::redirect(sprintf("%s?controller=pjAdminProducts&action=pjActionUpdate&id=%u&err=%s", $_SERVER['PHP_SELF'], $id, $err));
	        } else {
	        	$err = 'AP02';
	        }
	        pjUtil::redirect(sprintf("%s?controller=pjAdminProducts&action=pjActionIndex&err=%s", $_SERVER['PHP_SELF'], $err));
		}
		if (self::isGet()) {
			$this->setLocalesData();
			
			$this->set('category_arr', pjCategoryModel::factory()->getNode($this->getLocaleId(), 1));
			
			$this->appendJs('jquery.multilang.js', PJ_FRAMEWORK_LIBS_PATH . 'pj/js/');				
			$this->appendJs('tinymce.min.js', PJ_THIRD_PARTY_PATH . 'tinymce/');
			$this->appendCss('css/select2.min.css', PJ_THIRD_PARTY_PATH . 'select2/');
			$this->appendJs('js/select2.full.min.js', PJ_THIRD_PARTY_PATH . 'select2/');
			$this->appendJs('pjAdminProducts.js');
		}
	}
	
	private function pjActionDeleteProductAttr($product_id)
	{
		if (empty($product_id))
		{
			return false;
		}
		
		$pjAttributeModel = pjAttributeModel::factory();
		if (is_array($product_id))
		{
			$pjAttributeModel->whereIn('product_id', $product_id);
		} else {
			$pjAttributeModel->where('product_id', $product_id);
		}
		
		$attr_ids = $pjAttributeModel->findAll()->getDataPair(null, 'id');
		if (!empty($attr_ids))
		{
			$pjAttributeModel->eraseAll();
			$pjMultiLangModel = pjMultiLangModel::factory();
			$pjMultiLangModel->reset()->where('model', 'pjAttribute')->whereIn('foreign_id', $attr_ids)->eraseAll();
		}
	}
	
	private function pjActionDeleteStockAttr($product_id)
	{
		if (empty($product_id))
		{
			return false;
		}
		
		$pjStockAttributeModel = pjStockAttributeModel::factory();
		if (is_array($product_id))
		{
			$pjStockAttributeModel->whereIn('product_id', $product_id);
		} else {
			$pjStockAttributeModel->where('product_id', $product_id);
		}
		$pjStockAttributeModel->eraseAll();
	}
	
	public function pjActionDeactivate()
	{
		$this->setAjax(true);
	
		if ($this->isXHR() && $this->_get->check('id') && $this->_get->toInt('id') > 0)
		{
			pjProductModel::factory()->where('id', $this->_get->toInt('id'))->limit(1)->modifyAll(array('status' => 2));
			
			pjAppController::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => ''));
		}
		exit;
	}
	
	public function pjActionDeleteProduct()
	{
		$this->setAjax(true);
	    
	    if (!$this->isXHR())
	    {
	        self::jsonResponse(array('status' => 'ERR', 'code' => 100, 'text' => 'Missing headers.'));
	    }
	    if (!pjAuth::factory()->hasAccess())
	    {
	        self::jsonResponse(array('status' => 'ERR', 'code' => 102, 'text' => 'Access denied.'));
	    }
	    if (!($this->_get->toInt('id')))
	    {
	        self::jsonResponse(array('status' => 'ERR', 'code' => 103, 'text' => 'Missing, empty or invalid parameters.'));
	    }
	    $id = $this->_get->toInt('id');
	    $pjProductModel = pjProductModel::factory();
	    $arr = $pjProductModel->find($id)->getData();
	    if (!$arr)
	    {
	        self::jsonResponse(array('status' => 'ERR', 'code' => 103, 'text' => 'Product not found.'));
	    }
	    if ($pjProductModel->reset()->set('id', $id)->erase()->getAffectedRows() == 1)
	    {
	    	$pjExtraModel = pjExtraModel::factory();
			$pjMultiLangModel = pjMultiLangModel::factory();
			
			$pjMultiLangModel->where('model', 'pjProduct')->where('foreign_id', $id)->eraseAll();
			
			pjStockModel::factory()->where('product_id', $id)->eraseAll();
			pjCartModel::factory()->where('product_id', $id)->eraseAll();
			$this->pjActionDeleteStockAttr($id);
			pjProductSimilarModel::factory()->where('product_id', $id)->orWhere('similar_id', $id)->eraseAll();
			
			$extra_arr = $pjExtraModel->where('product_id', $id)->findAll()->getDataPair(null, 'id');
			if (!empty($extra_arr))
			{
				$pjExtraItemModel = pjExtraItemModel::factory();
				$pjExtraModel->eraseAll();
				$pjMultiLangModel->reset()->where('model', 'pjExtra')->whereIn('foreign_id', $extra_arr)->eraseAll();
				$extra_item_arr = $pjExtraItemModel->whereIn('extra_id', $extra_arr)->findAll()->getDataPair(NULL, 'id');
				if (!empty($extra_item_arr))
				{
					$pjExtraItemModel->reset()->whereIn('extra_id', $extra_arr)->eraseAll();
					$pjMultiLangModel->reset()->where('model', 'pjExtraItem')->whereIn('foreign_id', $extra_item_arr)->eraseAll();
				}
			}
			$this->pjActionDeleteProductAttr($id);
			pjProductCategoryModel::factory()->where('product_id', $id)->eraseAll();
			
			$pjGalleryModel = pjGalleryModel::factory();
			$image_arr = $pjGalleryModel->where('foreign_id', $id)->findAll()->getData();
			if (!empty($image_arr))
			{
				$pjGalleryModel->eraseAll();
				foreach ($image_arr as $image)
				{
					@clearstatcache();
					if (!empty($image['small_path']) && is_file($image['small_path']))
					{
						@unlink($image['small_path']);
					}
					@clearstatcache();
					if (!empty($image['medium_path']) && is_file($image['medium_path']))
					{
						@unlink($image['medium_path']);
					}
					@clearstatcache();
					if (!empty($image['large_path']) && is_file($image['large_path']))
					{
						@unlink($image['large_path']);
					}
					@clearstatcache();
					if (!empty($image['source_path']) && is_file($image['source_path']))
					{
						@unlink($image['source_path']);
					}
				}
			}
			
			self::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => 'Product has been deleted'));
	    }else{
	        self::jsonResponse(array('status' => 'ERR', 'code' => 105, 'text' => 'Product has not been deleted.'));
	    }
		exit;
	}
	
	public function pjActionDeleteProductBulk()
	{
		$this->setAjax(true);
	
		if (!pjAuth::factory()->hasAccess())
		{
		    self::jsonResponse(array('status' => 'ERR', 'code' => 103, 'text' => 'Access denied.'));
		}
		
		if (!$this->isXHR())
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 100, 'text' => 'Missing headers.'));
		}
		if (!self::isPost())
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 101, 'text' => 'HTTP method not allowed.'));
		}
		
		if (!$this->_post->has('record') || !($record = $this->_post->toArray('record')))
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 103, 'text' => 'Missing, empty or invalid data.'));
		}
		
		$pjExtraModel = pjExtraModel::factory();
		$pjMultiLangModel = pjMultiLangModel::factory();
		
		pjProductModel::factory()->whereIn('id', $record)->eraseAll();
		$pjMultiLangModel->where('model', 'pjProduct')->whereIn('foreign_id', $record)->eraseAll();
		
		pjStockModel::factory()->whereIn('product_id', $record)->eraseAll();
		pjCartModel::factory()->whereIn('product_id', $record)->eraseAll();
		$this->pjActionDeleteStockAttr($record);
		pjProductSimilarModel::factory()->whereIn('product_id', $record)->orWhereIn('similar_id', $record)->eraseAll();
		
		$extra_arr = $pjExtraModel->whereIn('product_id', $record)->findAll()->getDataPair(null, 'id');
		if (!empty($extra_arr))
		{
			$pjExtraItemModel = pjExtraItemModel::factory();
			$pjExtraModel->eraseAll();
			$pjMultiLangModel->reset()->where('model', 'pjExtra')->whereIn('foreign_id', $extra_arr)->eraseAll();
			$extra_item_arr = $pjExtraItemModel->whereIn('extra_id', $extra_arr)->findAll()->getDataPair(NULL, 'id');
			if (!empty($extra_item_arr))
			{
				$pjExtraItemModel->reset()->whereIn('extra_id', $extra_arr)->eraseAll();
				$pjMultiLangModel->reset()->where('model', 'pjExtraItem')->whereIn('foreign_id', $extra_item_arr)->eraseAll();
			}
		}

		$this->pjActionDeleteProductAttr($record);
		pjProductCategoryModel::factory()->whereIn('product_id', $record)->eraseAll();
		
		$pjGalleryModel = pjGalleryModel::factory();
		$image_arr = $pjGalleryModel->whereIn('foreign_id', $record)->findAll()->getData();
		if (!empty($image_arr))
		{
			$pjGalleryModel->eraseAll();
			foreach ($image_arr as $image)
			{
				@clearstatcache();
				if (!empty($image['small_path']) && is_file($image['small_path']))
				{
					@unlink($image['small_path']);
				}
				@clearstatcache();
				if (!empty($image['medium_path']) && is_file($image['medium_path']))
				{
					@unlink($image['medium_path']);
				}
				@clearstatcache();
				if (!empty($image['large_path']) && is_file($image['large_path']))
				{
					@unlink($image['large_path']);
				}
				@clearstatcache();
				if (!empty($image['source_path']) && is_file($image['source_path']))
				{
					@unlink($image['source_path']);
				}
			}
		}
		
		self::jsonResponse(array('status' => 'OK'));
	}

	public function pjActionDeleteSimilar()
	{
		$this->setAjax(true);
	    
	    if (!$this->isXHR())
	    {
	        self::jsonResponse(array('status' => 'ERR', 'code' => 100, 'text' => 'Missing headers.'));
	    }
	    if (!pjAuth::factory()->hasAccess())
	    {
	        self::jsonResponse(array('status' => 'ERR', 'code' => 102, 'text' => 'Access denied.'));
	    }
	    if (!($this->_get->toInt('id')))
	    {
	        self::jsonResponse(array('status' => 'ERR', 'code' => 103, 'text' => 'Missing, empty or invalid parameters.'));
	    }
	    $id = $this->_get->toInt('id');
	    if (pjProductSimilarModel::factory()->set('id', $id)->erase()->getAffectedRows() == 1)
	    {
	        self::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => 'Similar product has been deleted.'));
	    }else{
	        self::jsonResponse(array('status' => 'ERR', 'code' => 105, 'text' => 'Similar product has not been deleted.'));
	    }
		exit;
	}
	
	public function pjActionDeleteSimilarBulk()
	{
		$this->setAjax(true);
	
		if (!pjAuth::factory()->hasAccess())
		{
		    self::jsonResponse(array('status' => 'ERR', 'code' => 103, 'text' => 'Access denied.'));
		}
		
		if (!$this->isXHR())
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 100, 'text' => 'Missing headers.'));
		}
		if (!self::isPost())
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 101, 'text' => 'HTTP method not allowed.'));
		}
		
		if (!$this->_post->has('record') || !($record = $this->_post->toArray('record')))
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 103, 'text' => 'Missing, empty or invalid data.'));
		}
		
		if (pjProductSimilarModel::factory()->whereIn('id', $record)->eraseAll()->getAffectedRows() > 0)
		{
			pjAppController::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => 'Similar products has been deleted.'));
		}
		
		self::jsonResponse(array('status' => 'OK'));
	}
	
	public function pjActionGetProduct()
	{
		$this->setAjax(true);
	
		if ($this->isXHR())
		{
			$pjProductModel = pjProductModel::factory()
				->join('pjMultiLang', "t2.model='pjProduct' AND t2.foreign_id=t1.id AND t2.locale='".$this->getLocaleId()."' AND t2.field='name'", 'left outer')
				->join('pjMultiLang', "t3.model='pjProduct' AND t3.foreign_id=t1.id AND t3.locale='".$this->getLocaleId()."' AND t3.field='short_desc'", 'left outer')
				->join('pjMultiLang', "t4.model='pjProduct' AND t4.foreign_id=t1.id AND t4.locale='".$this->getLocaleId()."' AND t4.field='full_desc'", 'left outer');
			
			if ($q = $this->_get->toString('q'))
			{
				$q = str_replace(array('%', '_'), array('\%', '\_'), trim($q));
				$pjProductModel->where('(t2.content LIKE "%'.$q.'%" OR t3.content LIKE "%'.$q.'%" OR t4.content LIKE "%'.$q.'%" OR t1.sku LIKE "%'.$q.'%")');
			}
			
			if ($this->_get->check('status') && $this->_get->toString('status') != '' && in_array($this->_get->toString('status'), array(1,2)))
			{
				if ((int) $this->_get->toString('status') === 1)
				{
					// Active products only.
					$pjProductModel->where('t1.status', 1);
				}
				else
				{
					// Hidden = anything that is not active (covers legacy 0 and 2).
					$pjProductModel->where('t1.status <>', 1);
				}
			}

			if ($this->_get->check('name') && $this->_get->toString('name') != '')
			{
				$q = str_replace(array('%', '_'), array('\%', '\_'), $this->_get->toString('name'));
				$pjProductModel->where('t2.content LIKE', "%$q%");
			}
			
			if ($this->_get->check('sku') && $this->_get->toString('sku') != '')
			{
				$q = str_replace(array('%', '_'), array('\%', '\_'), $this->_get->toString('sku'));
				$pjProductModel->where('t1.sku LIKE', "%$q%");
			}
			
			if ($this->_get->check('category_id') && $this->_get->toInt('category_id') > 0)
			{
				$pjProductModel->where(sprintf("t1.id IN (SELECT `product_id` FROM `%s` WHERE `category_id` = '%u')",
					pjProductCategoryModel::factory()->getTable(), $this->_get->toInt('category_id')));
			}
			
			if ($this->_get->check('is_digital'))
			{
				$pjProductModel->where('t1.is_digital', 1);
			}
			
			if ($this->_get->check('is_featured'))
			{
				$pjProductModel->where('t1.is_featured', 1);
			}
			if ($this->_get->check('is_out') && $this->_get->toString('is_out') != '')
			{
				$pjProductModel->where("(t1.id NOT IN(SELECT TS.product_id FROM `".pjStockModel::factory()->getTable()."` AS TS GROUP BY TS.product_id HAVING SUM(TS.qty) > 0))");
			}
			if ($this->_get->check('is_active_out') && $this->_get->toString('is_active_out') != '')
			{
				$pjProductModel->where("(t1.status = 1 AND t1.id NOT IN(SELECT TS.product_id FROM `".pjStockModel::factory()->getTable()."` AS TS GROUP BY TS.product_id HAVING SUM(TS.qty) > 0))");
			}
			
			$column = 'name';
			$direction = 'ASC';
			$allowed_columns = array('id', 'name', 'sku', 'status', 'created', 'price', 'is_featured', 'cnt_orders', 'total_stock');
			if ($this->_get->check('direction') && $this->_get->check('column') && in_array(strtoupper($this->_get->toString('direction')), array('ASC', 'DESC')) && in_array($this->_get->toString('column'), $allowed_columns))
			{
				$column = $this->_get->toString('column');
				$direction = strtoupper($this->_get->toString('direction'));
			}

			$total = $pjProductModel->findCount()->getData();
			$rowCount = $this->_get->check('rowCount') && $this->_get->toInt('rowCount') > 0 ? $this->_get->toInt('rowCount') : 10;
			$pages = ceil($total / $rowCount);
			$page = $this->_get->check('page') && $this->_get->toInt('page') > 0 ? $this->_get->toInt('page') : 1;
			$offset = ((int) $page - 1) * $rowCount;
			if ($page > $pages)
			{
				$page = $pages;
			}

			$data = $pjProductModel
				->select(sprintf("t1.*, t2.content AS name,
					(SELECT `small_path` FROM `%1\$s`
						WHERE `foreign_id` = `t1`.`id`
						ORDER BY `sort` ASC
						LIMIT 1) AS `pic`,
					(SELECT COALESCE(SUM(`qty`), 0) FROM `%2\$s` WHERE `product_id` = `t1`.`id` LIMIT 1) AS `total_stock`,
					(SELECT MIN(`price`) FROM `%2\$s` WHERE `product_id` = `t1`.`id` LIMIT 1) AS `min_price`,
					(SELECT COUNT(`id`) FROM `%2\$s` WHERE `product_id` = `t1`.`id` LIMIT 1) AS `cnt_stock`,
					(SELECT COUNT(DISTINCT `order_id`) FROM `%3\$s` WHERE `product_id` = `t1`.`id` LIMIT 1) AS `cnt_orders`
				", pjGalleryModel::factory()->getTable(), pjStockModel::factory()->getTable(), pjOrderStockModel::factory()->getTable()))
				->orderBy("$column $direction")->limit($rowCount, $offset)->findAll()->getData();
				
			foreach ($data as $k => $v)
			{
				$data[$k]['name'] = pjSanitize::clean($v['name']);
				$data[$k]['min_price_format'] = pjCurrency::formatPrice($v['min_price']);
				if ($v['cnt_stock'] > 1)
				{
					$data[$k]['min_price_format'] = __('front_price_from', true) . " " . $data[$k]['min_price_format'];
				}
				// Normalize status for display: anything that is not active (1) is
				// treated as hidden (2) so the grid toggle always shows a label,
				// even for legacy rows stored with status 0.
				$data[$k]['status'] = ((int) $v['status'] === 1) ? 1 : 2;
			}
			
			pjAppController::jsonResponse(compact('data', 'total', 'pages', 'page', 'rowCount', 'column', 'direction'));
		}
		exit;
	}
	
	public function pjActionGetStock()
	{
		$this->setAjax(true);
	
		if ($this->isXHR())
		{
			$pjStockModel = pjStockModel::factory()
				->join('pjMultiLang', "t2.model='pjProduct' AND t2.foreign_id=t1.product_id AND t2.locale='".$this->getLocaleId()."' AND t2.field='name'", 'left outer')
				->join('pjProduct', 't3.id=t1.product_id', 'left outer')
			;
			
			if ($this->_get->check('q') && $this->_get->toString('q') != '')
			{
				$q = trim($this->_get->toString('q'));
				$q = str_replace(array('%', '_'), array('\%', '\_'), $q);
				$pjStockModel->where('t2.content LIKE', "%$q%");
				$pjStockModel->orWhere('t3.sku LIKE', "%$q%");
			}
			
			$column = 'name';
			$direction = 'ASC';
			$allowed_columns = array('id', 'name', 'sku', 'status', 'created', 'price', 'is_featured', 'cnt_orders', 'total_stock');
			if ($this->_get->check('direction') && $this->_get->check('column') && in_array(strtoupper($this->_get->toString('direction')), array('ASC', 'DESC')) && in_array($this->_get->toString('column'), $allowed_columns))
			{
				$column = $this->_get->toString('column');
				$direction = strtoupper($this->_get->toString('direction'));
			}

			$total = $pjStockModel->findCount()->getData();
			$rowCount = $this->_get->check('rowCount') && $this->_get->toInt('rowCount') > 0 ? $this->_get->toInt('rowCount') : 10;
			$pages = ceil($total / $rowCount);
			$page = $this->_get->check('page') && $this->_get->toInt('page') > 0 ? $this->_get->toInt('page') : 1;
			$offset = ((int) $page - 1) * $rowCount;
			if ($page > $pages)
			{
				$page = $pages;
			}

			$data = $pjStockModel
				->select(sprintf("t1.*, t2.content AS name,
					(SELECT `small_path` FROM `%1\$s`
						WHERE `id` = `t1`.`image_id`
						LIMIT 1) AS `pic`,
					(SELECT GROUP_CONCAT(CONCAT_WS('~:~', `tm2`.`content`, `tm1`.`content`) SEPARATOR '~|~')
						FROM `%2\$s` AS `tsa`
						LEFT JOIN `%3\$s` AS `tm1` ON `tm1`.`model` = 'pjAttribute' AND `tm1`.`foreign_id` = `tsa`.`attribute_id` AND `tm1`.`field` = 'name' AND `tm1`.`locale` = '%4\$u'
						LEFT JOIN `%3\$s` AS `tm2` ON `tm2`.`model` = 'pjAttribute' AND `tm2`.`foreign_id` = `tsa`.`attribute_parent_id` AND `tm2`.`field` = 'name' AND `tm2`.`locale` = '%4\$u'
						WHERE `tsa`.`product_id` = `t1`.`product_id`
						AND `tsa`.`stock_id` = `t1`.`id`
						LIMIT 1) AS `stock_attr`
				", pjGalleryModel::factory()->getTable(), pjStockAttributeModel::factory()->getTable(), pjMultiLangModel::factory()->getTable(), $this->getLocaleId()))
				->orderBy("$column $direction")
				->limit($rowCount, $offset)
				->findAll()
				->toArray('stock_attr', '~|~')
				->getData();
			
			foreach ($data as $k => $v)
			{
				$data[$k]['price_formated'] = pjCurrency::formatPrice($v['price']);
			}
				
			pjAppController::jsonResponse(compact('data', 'total', 'pages', 'page', 'rowCount', 'column', 'direction'));
		}
		exit;
	}

	public function pjActionDeleteExtra()
	{
		$this->setAjax(true);
	
		if ($this->isXHR())
		{
			pjAppController::validateCsrfToken();
			$resp = array('status' => 'ERR');
			if (pjExtraModel::factory()->set('id', $this->_post->toInt('id'))->erase()->getAffectedRows() == 1)
			{
				$pjMultiLangModel = pjMultiLangModel::factory();
				$pjExtraItemModel = pjExtraItemModel::factory();
				
				$pjMultiLangModel->reset()
					->where('model', 'pjExtra')
					->where('foreign_id', $this->_post->toInt('id'))
					->eraseAll();
					
				$extra_item = $pjExtraItemModel->where('extra_id', $this->_post->toInt('id'))->findAll()->getDataPair(NULL, 'id');
				if (!empty($extra_item))
				{
					$pjMultiLangModel->reset()
						->where('model', 'pjExtraItem')
						->whereIn('foreign_id', $extra_item)
						->eraseAll();
				
					$pjExtraItemModel->reset()->where('extra_id', $this->_post->toInt('id'))->eraseAll();
				}
				$resp['status'] = 'OK';
			}
			pjAppController::jsonResponse($resp);
		}
		exit;
	}
	
	public function pjActionDeleteStock()
	{
		$this->setAjax(true);
	
		if ($this->isXHR())
		{
			pjAppController::validateCsrfToken();
			$resp = array('status' => 'ERR');
			if (pjStockModel::factory()->set('id', $this->_post->toInt('id'))->erase()->getAffectedRows() == 1)
			{
				pjStockAttributeModel::factory()->where('stock_id', $this->_post->toInt('id'))->eraseAll();
				$resp['status'] = 'OK';
			}
			pjAppController::jsonResponse($resp);
		}
	}
	
	public function pjActionDeleteDigital()
	{
		$this->setAjax(true);
	
		if ($this->isXHR())
		{
			pjAppController::validateCsrfToken();
			$pjProductModel = pjProductModel::factory();
			
			$arr = $pjProductModel->find($this->_post->toInt('id'))->getData();
			if (count($arr) > 0)
			{
				if ($pjProductModel
					->reset()
					->set('id', $arr['id'])
					->modify(array(
						'digital_file' => ':NULL',
						'digital_name' => ':NULL'
					))->getAffectedRows() == 1)
				{
					@unlink($arr['digital_file']);
				}
				$this->set('arr', $arr);
			}
		}
	}
	
	public function pjActionExportProduct()
	{
		$this->checkLogin();
		
		if ($record = $this->_post->toArray('record'))
		{
			$arr = pjProductModel::factory()
				->join('pjMultiLang', "t2.model='pjProduct' AND t2.foreign_id=t1.id AND t2.locale='".$this->getLocaleId()."' AND t2.field='name'", 'left outer')
				->select("t1.*, t2.content as product_name, (SELECT COALESCE(SUM(`qty`), 0) FROM `".pjStockModel::factory()->getTable()."` WHERE `product_id` = `t1`.`id` LIMIT 1) AS `in_stock`")
				->whereIn('t1.id', $record)
				->findAll()->getData();
			$csv = new pjCSV();
			$csv
				->setHeader(true)
				->setName("Products-".time().".csv")
				->process($arr)
				->download();
		}
		exit;
	}
	
	public function pjActionExtraCopy()
	{
		$this->setAjax(true);
		
		if ($this->isXHR() && $this->isLoged())
		{
			if ($this->_post->check('from_product_id') && $this->_post->toInt('from_product_id') > 0 &&
				$this->_post->check('product_id') && $this->_post->toInt('product_id') > 0)
			{
				$pjExtraModel = pjExtraModel::factory();
				$pjExtraItemModel = pjExtraItemModel::factory();
				$pjMultiLangModel = pjMultiLangModel::factory();
				
				$extras = $pjExtraModel->where('t1.product_id', $this->_post->toInt('from_product_id'))->findAll()->getData();
				$extras_items = $pjExtraItemModel
					->where(sprintf("t1.extra_id IN (SELECT `id` FROM `%s` WHERE `product_id` = '%u')", $pjExtraModel->getTable(), $this->_post->toInt('from_product_id')))
					->findAll()
					->getData();
				
				foreach ($extras as $k => $extra)
				{
					$extras[$k]['items'] = array();
					if ($extra['type'] == 'multi')
					{
						foreach ($extras_items as $key => $item)
						{
							if ($item['extra_id'] == $extra['id'])
							{
								$extras[$k]['items'][] = $item;
							}
						}
					}
				}
				
				$query = sprintf("INSERT INTO `%1\$s` (`foreign_id`, `model`, `locale`, `field`, `content`, `source`)
						SELECT :foreign_id, `model`, `locale`, `field`, `content`, `source`
						FROM `%1\$s`
						WHERE `foreign_id` = :fid
						AND `model` = :model",
					$pjMultiLangModel->getTable());
							
				foreach ($extras as $extra)
				{
					$extra_id = $pjExtraModel->reset()->setAttributes(array(
						'product_id' => $this->_post->toInt('product_id'),
						'type' => $extra['type'],
						'price' => $extra['price'],
						'is_mandatory' => $extra['is_mandatory']
					))->insert()->getInsertId();
					if ($extra_id !== FALSE && (int) $extra_id > 0)
					{
						$pjMultiLangModel->reset()->prepare($query)->exec(array(
							'foreign_id' => $extra_id,
							'fid' => $extra['id'],
							'model' => 'pjExtra'
						));
						
						if ($extra['type'] == 'multi' && isset($extra['items']) && !empty($extra['items']))
						{
							foreach ($extra['items'] as $item)
							{
								$extra_item_id = $pjExtraItemModel->reset()->setAttributes(array(
									'extra_id' => $extra_id,
									'price' => $item['price']
								))->insert()->getInsertId();
								if ($extra_item_id !== FALSE && (int) $extra_item_id > 0)
								{
									$pjMultiLangModel->reset()->prepare($query)->exec(array(
										'foreign_id' => $extra_item_id,
										'fid' => $item['id'],
										'model' => 'pjExtraItem'
									));
								}
							}
						}
					}
				}
				
				pjAppController::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => 'Extras has been copied'));
			}
		}
		exit;
	}
	
	public function pjActionGetAttributes()
	{
		$this->setAjax(true);
	
		if ($this->isXHR())
		{
			if ($this->_get->check('product_id') && $this->_get->toInt('product_id') > 0)
			{
				$wk = 't1.product_id';
				$wv = $this->_get->toInt('product_id');
			} elseif ($this->_get->check('hash') && $this->_get->toString('hash') != '') {
				$wk = 't1.hash';
				$wv = $this->_get->toString('hash');
			}
			
			$attr_arr = array();
			$a_arr = pjAttributeModel::factory()
				->select('t1.id, t1.product_id, t1.parent_id, t1.hash, t2.content AS name')
				->join('pjMultiLang', "t2.model='pjAttribute' AND t2.foreign_id=t1.id AND t2.field='name' AND t2.locale='".$this->getLocaleId()."'", 'left outer')
				->where($wk, $wv)
				->orderBy('t1.`order_group` ASC, `order_item` ASC')
				->findAll()
				->getData();
			
			$pjMultiLangModel = pjMultiLangModel::factory();
				
			foreach ($a_arr as $attr)
			{
				$attr['i18n'] = $pjMultiLangModel->reset()->getMultiLang($attr['id'], 'pjAttribute');
				if ((int) $attr['parent_id'] === 0)
				{
					$attr_arr[$attr['id']] = $attr;
				} else {
					if (!isset($attr_arr[$attr['parent_id']]['child']))
					{
						$attr_arr[$attr['parent_id']]['child'] = array();
					}
					$attr_arr[$attr['parent_id']]['child'][] = $attr;
				}
			}
			$this->set('attr_arr', array_values($attr_arr));
			
			$this->setLocalesData();
		}
	}
	
	public function pjActionGetExtras()
	{
		$this->setAjax(true);
	
		if ($this->isXHR())
		{
			if (!$this->_get->check('product_id') || $this->_get->toInt('product_id') <= 0)
			{
				return;
			}
			
			$extra_arr = pjExtraModel::factory()->where('t1.product_id', $this->_get->toInt('product_id'))->findAll()->getData();
			$pjExtraItemModel = pjExtraItemModel::factory();
			$pjMultiLangModel = pjMultiLangModel::factory();
			foreach ($extra_arr as $k => $extra)
			{
				$extra_arr[$k]['i18n'] = $pjMultiLangModel->reset()->getMultiLang($extra['id'], 'pjExtra');
				$extra_arr[$k]['extra_items'] = $pjExtraItemModel->reset()->where('t1.extra_id', $extra['id'])->orderBy('t1.price ASC')->findAll()->getData();
				foreach ($extra_arr[$k]['extra_items'] as $key => $val)
				{
					$extra_arr[$k]['extra_items'][$key]['i18n'] = $pjMultiLangModel->reset()->getMultiLang($val['id'], 'pjExtraItem');
				}
			}
			$this->set('extra_arr', $extra_arr);
				
			$this->setLocalesData();
		}
	}
	
	public function pjActionGetHistory()
	{
		$this->setAjax(true);
	
		if ($this->isXHR())
		{
			$product = pjProductModel::factory()->find($this->_get->toInt('id'))->getData();
			
			$history_arr = pjHistoryModel::factory()
				->select("t1.*, t2.id AS sid, t3.content AS name,
						  (		SELECT GROUP_CONCAT(CONCAT_WS(': ', TML2.content, TML1.content) SEPARATOR '&nbsp;|&nbsp;') 
								FROM `".pjStockAttributeModel::factory()->getTable()."` AS `TSA`
									LEFT OUTER JOIN `".pjMultiLangModel::factory()->getTable()."` AS TML1 ON (TML1.model='pjAttribute' AND TML1.foreign_id=TSA.attribute_id AND TML1.field='name' AND TML1.locale='".$this->getLocaleId()."') 
									LEFT OUTER JOIN `".pjMultiLangModel::factory()->getTable()."` AS TML2 ON (TML2.model='pjAttribute' AND TML2.foreign_id=TSA.attribute_parent_id AND TML2.field='name' AND TML2.locale='".$this->getLocaleId()."')
								WHERE t1.record_id=TSA.stock_id) AS attributes	
						 ")
				->join('pjStock', 't2.id=t1.record_id', 'left outer')
				->join('pjMultiLang', "t3.model='pjProduct' AND t3.foreign_id=t2.product_id AND t3.locale='".$this->getLocaleId()."' AND t3.field='name'", 'left outer')
				->where('t1.table_name', pjStockModel::factory()->getTable())
				->where('t2.product_id', $this->_get->toInt('id'))
				->orderBy('t1.created ASC')
				->findAll()
				->getData();
			
			$this->set('product', $product);
			$this->set('history_arr', $history_arr);
		}
	}
	
	public function pjActionGetProducts()
	{
		$this->setAjax(true);
	
		if ($this->isXHR() && $this->isLoged())
		{
			$pjProductModel = pjProductModel::factory();
			if ($this->_get->check('product_id') && $this->_get->toInt('product_id') > 0)
			{
				$pjProductModel->where('t1.id !=', $this->_get->toInt('product_id'));
			}
			if ($this->_get->check('copy'))
			{
				switch ($this->_get->toString('copy'))
				{
					case 'Attr':
						$pjProductModel->where(sprintf("t1.id IN (SELECT `product_id` FROM `%s` WHERE 1)", pjAttributeModel::factory()->getTable()));
						break;
					case 'Extra':
						$pjProductModel->where(sprintf("t1.id IN (SELECT `product_id` FROM `%s` WHERE 1)", pjExtraModel::factory()->getTable()));
						break;
				}
			}
			$this->set('arr', $pjProductModel
				->select('t1.*, t2.content AS name')
				->join('pjMultiLang', "t2.model='pjProduct' AND t2.foreign_id=t1.id AND t2.locale='".$this->getLocaleId()."' AND t2.field='name'", 'left outer')
				->orderBy('name ASC')->findAll()->getData());
		}
	}
	
	public function pjActionGetSimilar()
	{
		$this->setAjax(true);
		
		if ($this->isXHR())
		{
			if ($this->_get->check('id') && $this->_get->toInt('id') > 0)
			{
				$pjProductSimilarModel = pjProductSimilarModel::factory()
					->join('pjProduct', 't2.id=t1.similar_id', 'inner')
					->join('pjMultiLang', "t3.model='pjProduct' AND t3.foreign_id=t2.id AND t3.locale='".$this->getLocaleId()."' AND t3.field='name'", 'left outer')
					->where('t1.product_id', $this->_get->toInt('id'))
				;
				
				$column = 'name';
				$direction = 'ASC';
				$allowed_similar_cols = array('id', 'name', 'sku', 'status', 'created', 'price');
				if ($this->_get->check('direction') && $this->_get->check('column') && in_array(strtoupper($this->_get->toString('direction')), array('ASC', 'DESC')) && in_array($this->_get->toString('column'), $allowed_similar_cols))
				{
					$column = $this->_get->toString('column');
					$direction = strtoupper($this->_get->toString('direction'));
				}
	
				$total = $pjProductSimilarModel->findCount()->getData();
				$rowCount = $this->_get->check('rowCount') && $this->_get->toInt('rowCount') > 0 ? $this->_get->toInt('rowCount') : 10;
				$pages = ceil($total / $rowCount);
				$page = $this->_get->check('page') && $this->_get->toInt('page') > 0 ? $this->_get->toInt('page') : 1;
				$offset = ((int) $page - 1) * $rowCount;
				if ($page > $pages)
				{
					$page = $pages;
				}
			
				$data = $pjProductSimilarModel
					->select('t1.id, t1.similar_id, t2.sku, t2.status, t3.content AS name')
					->orderBy("$column $direction")
					->findAll()
					->getData();
				
				pjAppController::jsonResponse(compact('data', 'total', 'pages', 'page', 'rowCount', 'column', 'direction'));
			}
		}
		exit;
	}
	
	public function pjActionSearchProducts()
	{
		$this->setAjax(true);
		
		$pjProductModel = pjProductModel::factory();
			
		if ($this->_get->check('query'))
		{
			$q = $pjProductModel->escapeStr($this->_get->toString('query'));
			$q = str_replace(array('%', '_'), array('\%', '\_'), $q);
			$pjProductModel->where("(t1.id LIKE '%$q%' OR t1.sku LIKE '%$q%' OR t1.id IN (SELECT `foreign_id` FROM `".pjMultiLangModel::factory()->getTable()."`
				WHERE `field` = 'name'
				AND `model` = 'pjProduct'
				AND `content` LIKE '%$q%'))");
		}
		$arr = $pjProductModel
			->select('t1.*, t2.content AS name')
			->join('pjMultiLang', "t2.model='pjProduct' AND t2.foreign_id=t1.id AND t2.field='name' AND t2.locale='".$this->getLocaleId()."'", 'left outer')
			->where('t1.id !=', $this->_get->toInt('id'))
			->where(sprintf("t1.id NOT IN (SELECT `similar_id` FROM `%s` WHERE `product_id` = '%u')", pjProductSimilarModel::factory()->getTable(), $this->_get->toInt('id')))
			->orderBy('`name` ASC')
			->limit($this->_get->toInt('limit'))->findAll()->getData();
		
		$_arr = array();
		foreach ($arr as $k => $v)
		{
			$_arr[$k]['id'] = $v['id'];
			$_arr[$k]['label'] = $v['name'];
		}			
		pjAppController::jsonResponse($_arr);
	}
	
	public function pjActionStock()
	{
		$this->checkLogin();
	    if (!pjAuth::factory()->hasAccess())
	    {
	        $this->sendForbidden();
	        return;
	    }
	    
		$this->set('has_update', pjAuth::factory('pjAdminProducts', 'pjActionUpdate')->hasAccess());
			
		$this->appendJs('jquery.datagrid.js', PJ_FRAMEWORK_LIBS_PATH . 'pj/js/');
		$this->appendJs('pjAdminProducts.js');
	}
	
	public function pjActionPrintStock()
	{
		$this->setLayout('pjActionPrint');
		
		$pjStockModel = pjStockModel::factory();
		if ($record = $this->_post->toArray('record'))
		{
			$pjStockModel->whereIn('t1.id', $record);
		}
		
		$arr = $pjStockModel
			->select(sprintf("t1.*, t2.content AS name,
				(SELECT `small_path` FROM `%1\$s`
					WHERE `id` = `t1`.`image_id`
					LIMIT 1) AS `pic`,
				(SELECT GROUP_CONCAT(CONCAT_WS('~:~', `tm2`.`content`, `tm1`.`content`) SEPARATOR '~|~')
					FROM `%2\$s` AS `tsa`
					LEFT JOIN `%3\$s` AS `tm1` ON `tm1`.`model` = 'pjAttribute' AND `tm1`.`foreign_id` = `tsa`.`attribute_id` AND `tm1`.`field` = 'name' AND `tm1`.`locale` = '%4\$u'
					LEFT JOIN `%3\$s` AS `tm2` ON `tm2`.`model` = 'pjAttribute' AND `tm2`.`foreign_id` = `tsa`.`attribute_parent_id` AND `tm2`.`field` = 'name' AND `tm2`.`locale` = '%4\$u'
					WHERE `tsa`.`product_id` = `t1`.`product_id`
					AND `tsa`.`stock_id` = `t1`.`id`
					LIMIT 1) AS `stock_attr`
			", pjGalleryModel::factory()->getTable(), pjStockAttributeModel::factory()->getTable(), pjMultiLangModel::factory()->getTable(), $this->getLocaleId()))
			->join('pjMultiLang', "t2.model='pjProduct' AND t2.foreign_id=t1.product_id AND t2.locale='".$this->getLocaleId()."' AND t2.field='name'", 'left outer')
			->join('pjProduct', 't3.id=t1.product_id', 'left outer')
			->orderBy("`name` ASC")
			->findAll()
			->toArray('stock_attr', '~|~')
			->getData();
		$this->set('arr', $arr);
		$this->resetCss()->appendCss('print.css');
	}
	
	public function pjActionAddSimilar()
	{
		$this->setAjax(true);
		
		if ($this->isXHR())
		{
			pjAppController::validateCsrfToken();
			if ($this->_post->check('product_id') && $this->_post->check('similar_id') && $this->_post->toInt('product_id') > 0 && $this->_post->toInt('similar_id') > 0)
			{
				$insert_id = pjProductSimilarModel::factory($this->_post->raw())->insert()->getInsertId();
				if ($insert_id !== FALSE && (int) $insert_id > 0)
				{
					pjAppController::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => 'Similar product has been added.'));
				}
				pjAppController::jsonResponse(array('status' => 'ERR', 'code' => 100, 'text' => 'Similar product has not been added.'));
			} else {
				pjAppController::jsonResponse(array('status' => 'ERR', 'code' => 101, 'text' => 'Missing parameters.'));
			}
		}
		exit;
	}
	
	public function pjActionIndex()
	{
		$this->checkLogin();
	    if (!pjAuth::factory()->hasAccess())
	    {
	        $this->sendForbidden();
	        return;
	    }
		
		$this->set('category_arr', pjCategoryModel::factory()->getNode($this->getLocaleId(), 1));
			
		$this->set('has_create', pjAuth::factory('pjAdminProducts', 'pjActionCreate')->hasAccess());
		$this->set('has_update', pjAuth::factory('pjAdminProducts', 'pjActionUpdate')->hasAccess());
		$this->set('has_delete', pjAuth::factory('pjAdminProducts', 'pjActionDeleteProduct')->hasAccess());
		$this->set('has_delete_bulk', pjAuth::factory('pjAdminProducts', 'pjActionDeleteProductBulk')->hasAccess());
		
		$this->appendJs('jquery.datagrid.js', PJ_FRAMEWORK_LIBS_PATH . 'pj/js/');
		$this->appendJs('pjAdminProducts.js');
	}
	
	public function pjActionSaveProduct()
	{
		$this->setAjax(true);
		
		if (!$this->isXHR())
		{
		    self::jsonResponse(array('status' => 'ERR', 'code' => 100, 'text' => 'Missing headers.'));
		}
		
		if (!self::isPost())
		{
		    self::jsonResponse(array('status' => 'ERR', 'code' => 101, 'text' => 'HTTP method not allowed.'));
		}
		
		if (!pjAuth::factory($this->_get->toString('controller'), 'pjActionUpdate')->hasAccess())
		{
		    self::jsonResponse(array('status' => 'ERR', 'code' => 102, 'text' => 'Access denied.'));
		}
		$pjProductModel = pjProductModel::factory();
		$arr = $pjProductModel->find($this->_get->toInt('id'))->getData();
		if (!$arr)
		{
		    self::jsonResponse(array('status' => 'ERR', 'code' => 103, 'text' => 'Product not found.'));
		}
		if (!in_array($this->_post->toString('column'), $pjProductModel->getI18n()))
		{
		    $pjProductModel->reset()->where('id', $this->_get->toInt('id'))->limit(1)->modifyAll(array($this->_post->toString('column') => $this->_post->toString('value')));
		} else {
		    pjMultiLangModel::factory()->updateMultiLang(array($this->getLocaleId() => array($this->_post->toString('column') => $this->_post->toString('value'))), $this->_get->toInt('id'), 'pjProduct', 'data');
		}
		self::jsonResponse(array('status' => 'OK', 'code' => 201, 'text' => 'Product has been updated.'));
		exit;
	}

	/**
	 * Inline-update a product's stock quantity directly from the product list.
	 * Only allowed for products that have exactly ONE stock row, because a total
	 * cannot be unambiguously split across multiple variants. Multi-variant
	 * products must be edited from the product's stock page.
	 */
	public function pjActionSaveStockQty()
	{
		$this->setAjax(true);

		if (!$this->isXHR())
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 100, 'text' => 'Missing headers.'));
		}
		if (!self::isPost())
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 101, 'text' => 'HTTP method not allowed.'));
		}
		if (!pjAuth::factory($this->_get->toString('controller'), 'pjActionUpdate')->hasAccess())
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 102, 'text' => 'Access denied.'));
		}

		$product_id = $this->_get->toInt('id');
		if ($product_id <= 0)
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 103, 'text' => 'Invalid product.'));
		}

		$qty = $this->_post->toInt('qty');
		if ($qty < 0)
		{
			$qty = 0;
		}

		$pjStockModel = pjStockModel::factory();
		$stocks = $pjStockModel->reset()->where('product_id', $product_id)->findAll()->getData();
		if (count($stocks) !== 1)
		{
			self::jsonResponse(array('status' => 'ERR', 'code' => 104, 'text' => 'This product has multiple stock variants. Edit it from the product\'s stock tab.'));
		}

		$pjStockModel->reset()->where('id', (int) $stocks[0]['id'])->limit(1)->modifyAll(array('qty' => $qty));
		self::jsonResponse(array('status' => 'OK', 'code' => 200, 'text' => 'Stock updated.', 'total_stock' => $qty));
		exit;
	}

	public function pjActionSaveStock()
	{
		$this->setAjax(true);
	
		if ($this->isXHR() && $this->isLoged())
		{
			pjAppController::validateCsrfToken();
			$pjStockModel = pjStockModel::factory();
			if (!in_array($this->_post->toString('column'), $pjStockModel->getI18n()))
			{
				if ($this->_post->toString('column') == 'qty')
				{
					$before = $pjStockModel->reset()->find($this->_get->toInt('id'))->getData();
				}
				$affected_rows = $pjStockModel->reset()->set('id', $this->_get->toInt('id'))->modify(array($this->_post->toString('column') => $this->_post->toString('value')))->getAffectedRows();
				if ($this->_post->toString('column') == 'qty' && $affected_rows == 1)
				{
					$after = $pjStockModel->reset()->find($this->_get->toInt('id'))->getData();
					pjAppController::addToHistory($this->_get->toInt('id'), $this->getUserId(), $pjStockModel->getTable(), $before, $after);
				}
			} else {
				pjMultiLangModel::factory()->updateMultiLang(array($this->getLocaleId() => array($this->_post->toString('column') => $this->_post->toString('value'))), $this->_get->toInt('id'), 'pjStock');
			}
		}
		exit;
	}
	
	public function pjActionUpdate()
	{
		$this->checkLogin();
	    if (!pjAuth::factory()->hasAccess())
	    {
	        $this->sendForbidden();
	        return;
	    }
		
		$pjExtraModel = pjExtraModel::factory();
		$pjExtraItemModel = pjExtraItemModel::factory();
		$pjAttributeModel = pjAttributeModel::factory();
		$pjStockModel = pjStockModel::factory();
		$pjStockAttributeModel = pjStockAttributeModel::factory();
		$pjProductCategoryModel = pjProductCategoryModel::factory();
		$pjMultiLangModel = pjMultiLangModel::factory();
		$pjProductModel = pjProductModel::factory();
		
		$post_max_size = pjUtil::getPostMaxSize();
		if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_SERVER['CONTENT_LENGTH']) && (int) $_SERVER['CONTENT_LENGTH'] > $post_max_size)
		{
			pjUtil::redirect(PJ_INSTALL_URL . "index.php?controller=pjAdminProducts&action=pjActionIndex&err=AP09");
		}
		
		if (self::isPost() && $this->_post->toInt('product_update'))
		{
			$err = 'AP01';
			
			if (!$this->_post->check('id') || $this->_post->toInt('id') <= 0)
			{
				pjUtil::redirect(PJ_INSTALL_URL . 'index.php?controller=pjAdminProducts&action=pjActionIndex&err=AP08');
			}
			$product_arr = $pjProductModel->find($this->_post->toInt('id'))->getData();
			if (empty($product_arr))
			{
				pjUtil::redirect(PJ_INSTALL_URL . 'index.php?controller=pjAdminProducts&action=pjActionIndex&err=AP08');
			}
			
			# Stock start ----------------------------------------------
			# Get/prepare
			
			$sa_arr = $pjStockAttributeModel
				->select("t1.stock_id, GROUP_CONCAT(CONCAT_WS(':', t2.image_id, t1.attribute_parent_id, t1.attribute_id) ORDER BY t1.attribute_parent_id ASC, t1.attribute_id ASC SEPARATOR '|') AS `str`")
				->join('pjStock', 't2.id=t1.stock_id AND t2.product_id=t1.product_id', 'inner')
				->where('t2.product_id', $this->_post->toInt('id'))
				->groupBy('t1.stock_id')
				->findAll()
				->getDataPair('stock_id', 'str');
			
			$i_arr = $u_arr = array();
			
			if ($stock_qty_arr = $this->_post->toArray('stock_qty'))
			{
				$stock_price_arr = $this->_post->toArray('stock_price');
				$stock_image_id_arr = $this->_post->toArray('stock_image_id');
				$stock_attribute_arr = $this->_post->toArray('stock_attribute');
				foreach ($stock_qty_arr as $k => $whatever)
				{
					if (strpos($k, 'x_') === 0)
					{
						if ((float) $stock_price_arr[$k] > 0 && (int) $stock_image_id_arr[$k] > 0)
						{
							# Insert new stock
							$tmp = array();
							if (isset($stock_attribute_arr[$k]))
							{
								foreach ($stock_attribute_arr[$k] as $attr_parent_id => $attr_id)
								{
								    $tmp[] = $stock_image_id_arr[$k] . ":" . $attr_parent_id . ":" . $attr_id;
								}
							}
							asort($tmp);
							$i_arr[$k] = join("|", $tmp);
						}
					} else {
						# Update attr
						if (isset($stock_attribute_arr[$k]))
						{
							# Add items again
							$tmp = array();
							foreach ($stock_attribute_arr[$k] as $attr_parent_id => $attr_id)
							{
							    $tmp[] = $stock_image_id_arr[$k] . ":" . $attr_parent_id . ":" . $attr_id;
							}
							asort($tmp);
							$u_arr[$k] = join("|", $tmp);
						}
					}
				}
				
				foreach ($stock_qty_arr as $k => $whatever)
				{
					if (strpos($k, 'x_') === 0)
					{
						if ((float) $stock_price_arr[$k] > 0 && (int) $stock_image_id_arr[$k] > 0 && !in_array($i_arr[$k], $sa_arr))
						{
							# Insert new stock
							$stock_id = $pjStockModel
								->reset()
								->set('product_id', $this->_post->toInt('id'))
								->set('image_id', $stock_image_id_arr[$k])
								->set('qty', $stock_qty_arr[$k])
								->set('price', $stock_price_arr[$k])
								->insert()
								->getInsertId();
	
							if ($stock_id !== false && (int) $stock_id > 0)
							{
								if (isset($stock_attribute_arr[$k]))
								{
									$pjStockAttributeModel->begin();
									foreach ($stock_attribute_arr[$k] as $attr_parent_id => $attr_id)
									{
										$pjStockAttributeModel
											->reset()
											->set('stock_id', $stock_id)
											->set('product_id', $this->_post->toInt('id'))
											->set('attribute_parent_id', $attr_parent_id)
											->set('attribute_id', $attr_id)
											->insert();
									}
									$pjStockAttributeModel->commit();
								}
							}
						}
					} else {
						# Update attr
						$before = $pjStockModel->reset()->find($k)->getData();
						if ($pjStockModel
							->reset()
							->set('id', $k)
							->modify(array(
								'image_id' => $stock_image_id_arr[$k],
								'qty' => $stock_qty_arr[$k],
								'price' => $stock_price_arr[$k]
							))
							->getAffectedRows() == 1)
						{
							$after = $pjStockModel->reset()->find($k)->getData();
							pjAppController::addToHistory($k, $this->getUserId(), $pjStockModel->getTable(), $before, $after);
						}
						//if (isset($sa_arr[$k]) && isset($u_arr[$k]) && $sa_arr[$k] != $u_arr[$k] && !in_array($u_arr[$k], $sa_arr))
						if (isset($u_arr[$k]) && !in_array($u_arr[$k], $sa_arr) && (
								!isset($sa_arr[$k]) ||
								(isset($sa_arr[$k]) && $sa_arr[$k] != $u_arr[$k])
							)
						)
						{
							# Delete items ------------------
							$pjStockAttributeModel->reset()->where('stock_id', $k)->eraseAll();
							if (isset($stock_attribute_arr[$k]))
							{
								# Add items again
								$pjStockAttributeModel->begin();
								foreach ($stock_attribute_arr[$k] as $attr_parent_id => $attr_id)
								{
									$pjStockAttributeModel
										->reset()
										->set('stock_id', $k)
										->set('product_id', $this->_post->toInt('id'))
										->set('attribute_parent_id', $attr_parent_id)
										->set('attribute_id', $attr_id)
										->insert();
								}
								$pjStockAttributeModel->commit();
							}
						}
					}
				}
			}
			# Stock end ----------------------------------------------

			$i18n_arr = $this->pjActionAttrHandle($this->_post->toInt('id'));
			if ($this->_post->check('is_digital'))
			{
				pjAppController::validateCsrfToken();
				$this->pjActionDeleteProductAttr($product_arr['id']);
				$this->pjActionDeleteStockAttr($product_arr['id']);
				
				$statement = sprintf("DELETE FROM `%1\$s`
					WHERE `product_id` = :product_id
					AND `id` NOT IN (
					  SELECT `id`
					  FROM (
					    SELECT `id`
					    FROM `%1\$s`
					    WHERE `product_id` = :product_id
					    ORDER BY `id` ASC
					    LIMIT 1
					  ) `foo`);", $pjStockModel->getTable());
				
				$pjStockModel->prepare($statement)->exec(array('product_id' => $product_arr['id']));
			}

			$i18n_arr = $this->_post->toArray('i18n');
			# Extras start -----------------------------------------
			# Get/Prepare
			$extra_items = array();
			$extra_arr = $pjExtraModel->where('t1.product_id', $this->_post->toInt('id'))->findAll()->getDataPair('id', 'type');
			foreach ($extra_arr as $id => $type)
			{
				if ($type == 'multi')
				{
					$extra_items[$id] = $pjExtraItemModel->reset()->where('t1.extra_id', $id)->findAll()->getDataPair('id', 'id');
				}
			}
			# Update ------------------
			if ($extra_type_arr = $this->_post->toArray('extra_type'))
			{
				$extra_price_arr = $this->_post->toArray('extra_price');
				$extra_is_mandatory_arr = $this->_post->toArray('extra_is_mandatory');
				foreach ($extra_type_arr as $k => $type)
				{
					# Insert new extra ------------------
					if (strpos($k, 'x_') === 0)
					{
						$data = array();
						$data['product_id'] = $this->_post->toInt('id');
						$data['type'] = $type;
						switch ($type)
						{
							case 'single':
								$data['price'] = $extra_price_arr[$k];
								break;
							case 'multi':
								break;
						}
						$data['is_mandatory'] = isset($extra_is_mandatory_arr[$k]) ? 1 : 0;
						$extra_id = $pjExtraModel->reset()->setAttributes($data)->insert()->getInsertId();
						if ($extra_id !== false && (int) $extra_id > 0)
						{
							switch ($type)
							{
								case 'single':
									$tmp = $this->pjActionTurnI18n($i18n_arr, 'extra_name', $k);
									$pjMultiLangModel->saveMultiLang($tmp, $extra_id, 'pjExtra');
									break;
								case 'multi':
									$tmp = $this->pjActionTurnI18n($i18n_arr, 'extra_title', $k);
									$pjMultiLangModel->saveMultiLang($tmp, $extra_id, 'pjExtra');
									
									$edata = array();
									$edata['extra_id'] = $extra_id;
									foreach ($extra_price_arr[$k] as $index => $price)
									{
											$edata['price'] = $price;
											$ei_id = $pjExtraItemModel->reset()->setAttributes($edata)->insert()->getInsertId();
											if ($ei_id !== false && (int) $ei_id > 0)
											{
												$tmp = $this->pjActionTurnI18n($i18n_arr, 'extra_name', $k, $index);
												$pjMultiLangModel->saveMultiLang($tmp, $ei_id, 'pjExtraItem');
											}
									}
									break;
							}
						}
					} else {
						# Update extra
						$pjExtraModel->reset()->set('id', $k)->modify(array(
							'type' => $type,
							'price' => $type == 'single' ? $extra_price_arr[$k] : ':NULL',
							'is_mandatory' => isset($extra_is_mandatory_arr[$k]) ? 1 : 0
						));
						### ---
						switch ($type)
						{
							case 'multi':
								# value of column 'type' in DB is single
								$tmp = $this->pjActionTurnI18n($i18n_arr, 'extra_title', $k);
								if ($extra_arr[$k] == 'single')
								{
									$pjMultiLangModel->reset()->where('model', 'pjExtra')->where('foreign_id', $k)->eraseAll();
									$pjMultiLangModel->saveMultiLang($tmp, $k, 'pjExtra');
								} else {
									$pjMultiLangModel->updateMultiLang($tmp, $k, 'pjExtra');
								}
								# Delete items ------------------
								$diff = array_diff($extra_items[$k], array_keys($extra_price_arr[$k]));
								if (count($diff) > 0)
								{
									$pjExtraItemModel->reset()->whereIn('id', $diff)->eraseAll();
									$pjMultiLangModel->reset()->where('model', 'pjExtraItem')->whereIn('foreign_id', $diff)->eraseAll();
								}
								
								foreach ($extra_price_arr[$k] as $index => $price)
								{
									if (strpos($index, 'y_') === 0)
									{
										# Add items
											$ei_id = $pjExtraItemModel->reset()->setAttributes(array(
												'extra_id' => $k,
												'price' => $price
											))->insert()->getInsertId();
											if ($ei_id !== false && (int) $ei_id > 0)
											{
												$tmp = $this->pjActionTurnI18n($i18n_arr, 'extra_name', $k, $index);
												$pjMultiLangModel->saveMultiLang($tmp, $ei_id, 'pjExtraItem');
											}
									} else {
										# Update items
											$pjExtraItemModel->reset()->set('id', $index)->modify(array(
												'price' => $price
											));
											$tmp = $this->pjActionTurnI18n($i18n_arr, 'extra_name', $k, $index);
											$pjMultiLangModel->updateMultiLang($tmp, $index, 'pjExtraItem');
									}
								}
								break;
							case 'single':
								# value of column 'type' in DB is multi
								if ($extra_arr[$k] == 'multi')
								{
									$ei_ids = $pjExtraItemModel->reset()->where('extra_id', $k)->findAll()->getDataPair(NULL, 'id');
									if (!empty($ei_ids))
									{
										$pjExtraItemModel->eraseAll();
										$pjMultiLangModel->reset()->where('model', 'pjExtraItem')->whereIn('foreign_id', $ei_ids)->eraseAll();
									}
									$pjMultiLangModel->reset()->where('model', 'pjExtra')->where('foreign_id', $k)->where('field', 'extra_title')->eraseAll();
								}
								$tmp = $this->pjActionTurnI18n($i18n_arr, 'extra_name', $k);
								$pjMultiLangModel->updateMultiLang($tmp, $k, 'pjExtra');
								break;
						}
						### ---
					}
				}
			}
			# Extras end -----------------------------------------

			# Categories start -----------------------------------
			$pjProductCategoryModel->where('product_id', $this->_post->toInt('id'))->eraseAll();
			if ($category_id_arr = $this->_post->toArray('category_id'))
			{
				$pjProductCategoryModel->begin();
				foreach ($category_id_arr as $category_id)
				{
					$pjProductCategoryModel
						->reset()
						->set('product_id', $this->_post->toInt('id'))
						->set('category_id', $category_id)
						->insert();
				}
				$pjProductCategoryModel->commit();
			}
			# Categories end -------------------------------------
				
			$data = array();
			$data['is_featured'] = $this->_post->check('is_featured') ? 1 : 0;
			$data['is_digital'] = $this->_post->check('is_digital') ? 1 : 0;
			if ($this->_post->check('is_digital'))
			{
				pjAppController::validateCsrfToken();
				if ($this->_post->check('digital_choose'))
				{
				pjAppController::validateCsrfToken();
					switch ($this->_post->toInt('digital_choose'))
					{
						case 1:
							if (isset($_FILES['digital_file']))
							{
								if($_FILES['digital_file']['error'] == 0)
								{
									$pjUpload = new pjUpload();
									$pjUpload->setAllowedTypes(array('*'));
									$pjUpload->setAllowedExt(array('*'));
									if ($pjUpload->load($_FILES['digital_file']))
									{
										$name = $pjUpload->getFile('name');
										$file = PJ_UPLOAD_PATH . 'digital/' . md5(uniqid(rand(), true)) . "." . $pjUpload->getExtension();
										if ($pjUpload->save($file))
										{
											$data['digital_file'] = $file;
											$data['digital_name'] = $name;
										}
									} else {
										$err = 'AP10';
									}
								}else if($_FILES['digital_file']['error'] != 4){
									$err = 'AP10';
									$data['is_digital'] = 0;
								}
							}
							break;
						case 2:
							if(file_exists($this->_post->toString('digital_file')))
							{
								$data['digital_file'] = $this->_post->toString('digital_file');
								$data['digital_name'] = basename($this->_post->toString('digital_file'));
							}else{
								$err = 'AP11';
								$data['is_digital'] = 0;
							}
							break;
					}
				}
				if($err == 'AP01')
				{
					$data['digital_expire'] = sprintf("%s:%s:00", $this->_post->toString('hour'), $this->_post->toString('minute'));
				}else{
					$data['digital_file'] = ':NULL';
					$data['digital_name'] = ':NULL';
					$data['digital_expire'] = ':NULL';
				}
			} else {
				$data['digital_file'] = ':NULL';
				$data['digital_name'] = ':NULL';
				$data['digital_expire'] = ':NULL';
			}
			
			$pjProductModel->reset()->set('id', $this->_post->toInt('id'))->modify(array_merge($this->_post->raw(), $data));
			
			$i18n_arr = $this->_post->toI18n('i18n');
			if ($i18n_arr)
			{
				foreach ($i18n_arr as $locale_id => $locale_arr)
				{
				    if (isset($i18n_arr[$locale_id]['extra_title'])) {
					   unset($i18n_arr[$locale_id]['extra_title']);
				    }
				    if (isset($i18n_arr[$locale_id]['extra_name'])) {
					   unset($i18n_arr[$locale_id]['extra_name']);
				    }
				    if (isset($i18n_arr[$locale_id]['attr_group'])) {
				        unset($i18n_arr[$locale_id]['attr_group']);
				    }
				    if (isset($i18n_arr[$locale_id]['attr_item'])) {
				        unset($i18n_arr[$locale_id]['attr_item']);
				    }
				}
				$pjMultiLangModel->updateMultiLang($i18n_arr, $this->_post->toInt('id'), 'pjProduct');
			}
			pjUtil::redirect(sprintf("%s?controller=pjAdminProducts&action=pjActionUpdate&id=%u&tab=%s&err=%s", $_SERVER['PHP_SELF'], $this->_post->toInt('id'), $this->_post->toString('tab'), $err));
		} else {
			$arr = $pjProductModel->find($this->_get->toInt('id'))->getData();
			if (count($arr) === 0)
			{
				pjUtil::redirect(sprintf("%s?controller=pjAdminProducts&action=pjActionIndex&err=%s", $_SERVER['PHP_SELF'], 'AP08'));
			}
			$pjMultiLangModel = pjMultiLangModel::factory();
			$arr['i18n'] = $pjMultiLangModel->getMultiLang($arr['id'], 'pjProduct');
			$this->set('arr', $arr);
			
			$this->set('category_arr', pjCategoryModel::factory()->getNode($this->getLocaleId(), 1));
			$this->set('pc_arr', pjProductCategoryModel::factory()->where('t1.product_id', $arr['id'])->orderBy('t1.category_id ASC')->findAll()->getDataPair('category_id', 'category_id'));
			$extra_arr = pjExtraModel::factory()->where('t1.product_id', $arr['id'])->findAll()->getData();
			$pjExtraItemModel = pjExtraItemModel::factory();
			foreach ($extra_arr as $k => $extra)
			{
				$extra_arr[$k]['i18n'] = $pjMultiLangModel->reset()->getMultiLang($extra['id'], 'pjExtra');
				$extra_arr[$k]['extra_items'] = $pjExtraItemModel->reset()->where('t1.extra_id', $extra['id'])->orderBy('t1.price ASC')->findAll()->getData();
				foreach ($extra_arr[$k]['extra_items'] as $key => $val)
				{
					$extra_arr[$k]['extra_items'][$key]['i18n'] = $pjMultiLangModel->reset()->getMultiLang($val['id'], 'pjExtraItem');
				}
			}
			$this->set('extra_arr', $extra_arr);
			$attr_arr = array();
			// Do not change col_name, direction
			$a_arr = pjAttributeModel::factory()
				->select('t1.id, t1.product_id, t1.parent_id, t1.hash, t2.content AS name')
				->join('pjMultiLang', "t2.model='pjAttribute' AND t2.foreign_id=t1.id AND t2.field='name' AND t2.locale='".$this->getLocaleId()."'", 'left outer')
				->where('t1.product_id', $arr['id'])
				->orderBy('t1.`order_group`, `order_item` ASC')->findAll()->getData();
			
			foreach ($a_arr as $attr)
			{
				$attr['i18n'] = $pjMultiLangModel->reset()->getMultiLang($attr['id'], 'pjAttribute');
				if ((int) $attr['parent_id'] === 0)
				{
					$attr_arr[$attr['id']] = $attr;
				} else {
					if (!isset($attr_arr[$attr['parent_id']]['child']))
					{
						$attr_arr[$attr['parent_id']]['child'] = array();
					}
					$attr_arr[$attr['parent_id']]['child'][] = $attr;
				}
			}
			$this->set('attr_arr', array_values($attr_arr));
			
			$stock_arr = pjStockModel::factory()
				->select('t1.*, t2.small_path')
				->join('pjGallery', 't2.id=t1.image_id', 'left outer')
				->where('t1.product_id', $arr['id'])
				->findAll()
				->getData();

			$pjStockAttributeModel = pjStockAttributeModel::factory();
			foreach ($stock_arr as $k => $stock)
			{
				$stock_arr[$k]['attrs'] = $pjStockAttributeModel->reset()
					->where('t1.stock_id', $stock['id'])
					->orderBy('t1.attribute_id ASC')
					->findAll()
					->getDataPair('attribute_parent_id', 'attribute_id');
			}
			$this->set('stock_arr', $stock_arr);
			
			$this->setLocalesData();
			
			$gallery_arr = pjGalleryModel::factory()
				->where('t1.foreign_id', $this->_get->toInt('id'))
				->orderBy('ISNULL(t1.sort), t1.sort ASC, t1.id ASC')
				->findAll()
				->getData();
			$this->set('gallery_arr', $gallery_arr);
			
			pjGalleryModel::factory()->where('foreign_id', $this->_get->toInt('id'))->modifyAll(array('model'=>'pjProduct'));
						
			$this->set('has_update', pjAuth::factory('pjAdminProducts', 'pjActionUpdate')->hasAccess());
			
			# Gallery plugin
            $this->appendCss('pj-gallery.css', pjObject::getConstant('pjGallery', 'PLUGIN_CSS_PATH'));
            $this->appendJs('ajaxupload.js', PJ_THIRD_PARTY_PATH . 'ajaxupload/');
            $this->appendJs('jquery.gallery.js', pjObject::getConstant('pjGallery', 'PLUGIN_JS_PATH'));
			
			$this->appendCss('css/select2.min.css', PJ_THIRD_PARTY_PATH . 'select2/');
			$this->appendJs('js/select2.full.min.js', PJ_THIRD_PARTY_PATH . 'select2/');
			
			$this->appendJs('jquery.datagrid.js', PJ_FRAMEWORK_LIBS_PATH . 'pj/js/');		
			$this->appendJs('tinymce.min.js', PJ_THIRD_PARTY_PATH . 'tinymce/');			
			$this->appendJs('jquery.multilang.js', PJ_FRAMEWORK_LIBS_PATH . 'pj/js/');
			$this->appendJs('pjQueryAutocompleter.min.js')
				->appendJs('pjAdminProducts.js');
		}
	}
	
	private function pjActionTurnI18n($data, $key, $id, $index=NULL, $new_key=NULL)
	{
		$arr = array();
		$arr_index = is_null($new_key) ? $key : $new_key;
		foreach ($data as $locale => $locale_arr)
		{
			$arr[$locale] = array(
				$arr_index => is_null($index) ?
					(isset($locale_arr[$key]) && isset($locale_arr[$key][$id]) ? $locale_arr[$key][$id] : NULL) :
					(isset($locale_arr[$key]) && isset($locale_arr[$key][$id]) && isset($locale_arr[$key][$id][$index]) ? $locale_arr[$key][$id][$index] : NULL)
			);
		}
		
		return $arr;
	}

	public function pjActionLoadImages()
	{
		$this->setAjax(true);
		
		if ($this->isXHR() && $this->isLoged())
		{
			$arr = array();
			if ($this->_get->check('product_id') && $this->_get->toInt('product_id') > 0)
			{
				$arr = pjGalleryModel::factory()->where('t1.foreign_id', $this->_get->toInt('product_id'))->orderBy('ISNULL(t1.sort), t1.sort ASC, t1.id ASC')->findAll()->getData();
			} elseif ($this->_get->check('hash') && $this->_get->toString('hash') != '') {
				$arr = pjGalleryModel::factory()->where('t1.hash', $this->_get->toString('hash'))->orderBy('ISNULL(t1.sort), t1.sort ASC, t1.id ASC')->findAll()->getData();
			}
			
			$this->set('arr', $arr);
		}
	}
	
	public function pjActionCheckStockAttributes()
	{
	    $this->setAjax(true);
	    
	    if ($this->isXHR() && $this->isLoged())
	    {   
	       $i_arr = array();	       
	       if ($stock_qty_arr = $this->_post->toArray('stock_qty'))
	       {
	           $stock_image_id_arr = $this->_post->toArray('stock_image_id');
	           $stock_attribute_arr = $this->_post->toArray('stock_attribute');
	           foreach ($stock_qty_arr as $k => $whatever)
	           {
	               $tmp = array();
	               if (isset($stock_attribute_arr[$k]))
	               {
	                   foreach ($stock_attribute_arr[$k] as $attr_parent_id => $attr_id)
	                   {
	                       $tmp[] = $stock_image_id_arr[$k] . ":" . $attr_parent_id . ":" . $attr_id;
	                   }
	               }
	               asort($tmp);
	               $str_key = join("|", $tmp);
	               if (in_array($str_key, $i_arr)) {
	                   pjAppController::jsonResponse(array('status' => 'ERR'));
	                   break;
	               }
	               $i_arr[] = $str_key;
	           }
	       }
	       pjAppController::jsonResponse(array('status' => 'OK'));
	    }
	}
}
?>