energine
[ class tree: energine ] [ index: energine ] [ all elements ]

Source for file Grid.class.php

Documentation is available at Grid.class.php

  1. <?php
  2.  
  3. /**
  4.  * Содержит класс Grid
  5.  *
  6.  * @package energine
  7.  * @subpackage share
  8.  * @author dr.Pavka
  9.  * @copyright Energine 2006
  10.  * @version $Id$
  11.  */
  12.  
  13. /**
  14.  * Сетка
  15.  *
  16.  * @package energine
  17.  * @subpackage share
  18.  * @author dr.Pavka
  19.  */
  20. class Grid extends DBDataSet {
  21.     /**
  22.      * Направление вверх
  23.      *
  24.      */
  25.     const DIR_UP = '<';
  26.     /**
  27.      * Направление вниз
  28.      *
  29.      */
  30.     const DIR_DOWN = '>';
  31.     /**
  32.      * Количество записей в гриде по умолчанию
  33.      *
  34.      */
  35.     const RECORD_PER_PAGE = 30;
  36.  
  37.     /**
  38.      * Компонент: менеджер изображений
  39.      *
  40.      * @var ImageManager 
  41.      * @access private
  42.      */
  43.     private $imageManager;
  44.  
  45.     /**
  46.      * Компонент: библиотека изображений
  47.      *
  48.      * @var FileLibrary 
  49.      * @access private
  50.      */
  51.     private $fileLibrary;
  52.  
  53.     /**
  54.      * сейвер
  55.      *
  56.      * @var Saver 
  57.      * @access protected
  58.      */
  59.     protected $saver;
  60.  
  61.     /**
  62.      * Имя колонки для определения порядка пользовательскорй сортировки
  63.      *
  64.      * @var string 
  65.      * @access private
  66.      */
  67.     private $orderColumn = false;
  68.  
  69.  
  70.     /**
  71.      * Конструктор класса
  72.      *
  73.      * @param string $name 
  74.      * @param string $module 
  75.      * @param Document $document 
  76.      * @param array $params 
  77.      * @access public
  78.      */
  79.     public function __construct($name$moduleDocument $document,  array $params null{
  80.         parent::__construct($name$module$document,  $params);
  81.         $this->setProperty('exttype''grid');
  82.         if (!$this->getParam('recordsPerPage')) {
  83.             $this->setParam('recordsPerPage'self::RECORD_PER_PAGE);
  84.         }
  85.         $this->setTitle($this->translate('TXT_'.strtoupper($this->getName())));
  86.     }
  87.     /**
  88.      * Переопределен параметр configFilename
  89.      *
  90.      * @return int 
  91.      * @access protected
  92.      */
  93.  
  94.     protected function defineParams({
  95.         $params array();
  96.         if (!$this->params['configFilename']{
  97.             $fileConf ComponentConfig::SITE_CONFIG_DIR.get_class($this).'.component.xml';
  98.             $coreConf sprintf(ComponentConfig::CORE_CONFIG_DIR$this->module).get_class($this).'.component.xml';
  99.             if (file_exists($fileConf)) {
  100.                 $params['configFilename'$fileConf;
  101.             }
  102.             elseif (file_exists($coreConf)) {
  103.                 $params['configFilename'$coreConf;
  104.             }
  105.             else {
  106.                 $params['configFilename'sprintf(ComponentConfig::CORE_CONFIG_DIR'share/').'Grid.component.xml';
  107.             }
  108.         }
  109.         $params['active'true;
  110.         $params['thumbnail'array($this->getConfigValue('thumbnail.width')$this->getConfigValue('thumbnail.height'));
  111.         
  112.         return array_merge(parent::defineParams(),$params);
  113.     }
  114.     /**
  115.      * Метод выводящий форму добавления
  116.      *
  117.      * @return void 
  118.      * @access protected
  119.      */
  120.  
  121.     protected function add({
  122.         $this->setType(self::COMPONENT_TYPE_FORM_ADD);
  123.         $this->prepare();
  124.         $this->addToolbarTranslations();
  125.         //$this->addCrumb('TXT_ADD_ITEM');
  126.     }
  127.  
  128.     /**
  129.      * Метод выводящий форму редактирования
  130.      *
  131.      * @return void 
  132.      * @access protected
  133.      */
  134.  
  135.     protected function edit({
  136.         $this->setType(self::COMPONENT_TYPE_FORM_ALTER);
  137.  
  138.         $id $this->getActionParams();
  139.         list($id$id;
  140.         if (!$this->recordExists($id)) {
  141.             throw new SystemException('ERR_404'SystemException::ERR_404);
  142.         }
  143.         $this->setFilter($id);
  144.  
  145.         $this->prepare();
  146.         $fieldDescriptions $this->getDataDescription()->getFieldDescriptions();
  147.         foreach ($fieldDescriptions as $fieldName => $fieldDescription{
  148.             if (($fieldDescription->getType(== FieldDescription::FIELD_TYPE_PRFILE || $fieldDescription->getType(== FieldDescription::FIELD_TYPE_PFILE&& ($fieldData $this->getData()->getFieldByName($fieldName)->getData())) {
  149.                 $fieldData $fieldData[0];
  150.                 if (file_exists($fieldData&& @getimagesize($fieldData)) {
  151.                     $this->getDataDescription()->getFieldDescriptionByName($fieldName)->setProperty('is_image''is_image');
  152.                 }
  153.             }
  154.         }
  155.         $this->addToolbarTranslations();
  156.     }
  157.  
  158.     /**
  159.      * Добавлены переводы для фильтра
  160.      *
  161.      * @return void 
  162.      * @access protected
  163.      */
  164.  
  165.     protected function main({
  166.         parent::main();
  167.         $this->addTranslation('TXT_FILTER');
  168.         $this->addTranslation('BTN_APPLY_FILTER');
  169.         $this->addTranslation('TXT_RESET_FILTER');
  170.     }
  171.  
  172.     /**
  173.      * Внешний метод удаления
  174.      *
  175.      * @return mixed 
  176.      * @access protected
  177.      * @see Grid::save()
  178.      */
  179.  
  180.     protected function delete({
  181.         $transactionStarted $this->dbh->beginTransaction();
  182.         try {
  183.             list($id$this->getActionParams();
  184.             if (!$this->recordExists($id)) {
  185.                 throw new SystemException('ERR_404'SystemException::ERR_404);
  186.             }
  187.  
  188.             $this->deleteData($id);
  189.  
  190.             $JSONResponse array(
  191.             'result'=>true
  192.             );
  193.             $this->dbh->commit();
  194.         }
  195.         catch (SystemException $e){
  196.             if ($transactionStarted{
  197.                 $this->dbh->rollback();
  198.             }
  199.             $JSONResponse $this->generateError($e->getCode()$e->getMessage());
  200.  
  201.         }
  202.         $this->response->setHeader('Content-Type''text/javascript; charset=utf-8');
  203.         $this->response->write(json_encode($JSONResponse));
  204.         $this->response->commit();
  205.     }
  206.  
  207.     /**
  208.      * Внутренний метод удаления записи
  209.      *
  210.      * @param int идентификаотр записи
  211.      * @return void 
  212.      * @access protected
  213.      */
  214.  
  215.     protected function deleteData($id{
  216.         if ($orderColumn $this->getOrderColumn()) {
  217.             $deletedOrderNum simplifyDBResult($this->dbh->select($this->getTableName()$this->getOrderColumn()array($this->getPK()=>$id))$this->getOrderColumn()true);
  218.  
  219.             $ids simplifyDBResult($this->dbh->select($this->getTableName()array($this->getPK())array_merge($this->getFilter()array($orderColumn.' > '.$deletedOrderNum))array($orderColumn=>QAL::ASC))$this->getPK());
  220.  
  221.         }
  222.  
  223.         $this->dbh->modify(QAL::DELETE $this->getTableName()nullarray($this->getPK()=>$id));
  224.  
  225.         //если определен порядок следования перестраиваем индекс сортировки
  226.         if ($orderColumn && $ids{
  227.             $this->addFilterCondition(array($this->getPK()=>$ids));
  228.             $request 'UPDATE '.$this->getTableName().' SET '.$orderColumn.' = '.$orderColumn.' - 1 '.$this->dbh->buildWhereCondition($this->getFilter());
  229.  
  230.             $this->dbh->modifyRequest($request);
  231.         }
  232.     }
  233.  
  234.     /**
  235.      * переписан родительский метод
  236.      *
  237.      * @return int 
  238.      * @access protected
  239.      */
  240.  
  241.     protected function getDataLanguage({
  242.         if (isset($_POST['languageID']&& $this->getAction()=='getRawData'{
  243.             $langID $_POST['languageID'];
  244.             if (!Language::getInstance()->isValidLangID($langID)) {
  245.                 throw new SystemException('ERR_BAD_LANG_ID'SystemException::ERR_WARNING);
  246.             }
  247.             $result $langID;
  248.         }
  249.         else $result parent::getDataLanguage();
  250.  
  251.         return $result;
  252.     }
  253.  
  254.     /**
  255.      * Выводит данные в JSON формате для AJAX
  256.      *
  257.      * @return void 
  258.      * @access protected
  259.      */
  260.  
  261.     protected function getRawData($baseMethod self::DEFAULT_ACTION_NAME{
  262.         try {
  263.             $this->setParam('onlyCurrentLang'true);
  264.             $this->config->setCurrentMethod($baseMethod);
  265.             $this->setBuilder(new JSONBuilder());
  266.  
  267.             $this->setDataDescription($this->createDataDescription());
  268.             $this->createPager();
  269.             $this->getBuilder()->setDataDescription($this->getDataDescription());
  270.             $data $this->createData();
  271.             if ($data instanceof Data{
  272.                 $this->setData($data);
  273.                 $this->getBuilder()->setData($this->getData());
  274.             }
  275.  
  276.             if ($this->getBuilder()->build()) {
  277.                 if ($this->pager$this->getBuilder()->setPager($this->pager);
  278.                 $result $this->getBuilder()->getResult();
  279.             }
  280.             else {
  281.                 $result $this->getBuilder()->getErrors();
  282.             }
  283.  
  284.         }
  285.         catch (Exception $e){
  286.             $message['errors'][array('message'=>$e->getMessage().current($e->getCustomMessage()));
  287.             $result json_encode(array_merge(array('result'=>false'header'=>$this->translate('TXT_SHIT_HAPPENS'))$message));
  288.         }
  289.         $this->response->setHeader('Content-Type''text/javascript; charset=utf-8');
  290.         $this->response->write($result);
  291.         $this->response->commit();
  292.     }
  293.  
  294.     /**
  295.      * Внешний метод сохранения
  296.      * Вызывает внутренний метод сохранения saveData(), который и производит собственно все действия
  297.      *
  298.      * @return void 
  299.      * @access protected
  300.      */
  301.  
  302.     protected function save({
  303.         $transactionStarted $this->dbh->beginTransaction();
  304.         try {
  305.             $result $this->saveData();
  306.  
  307.             $transactionStarted !($this->dbh->commit());
  308.             $JSONResponse array(
  309.             'data'=>json_encode((is_int($result))?$result:(int)$_POST[$this->getTableName()][$this->getPK()]),
  310.             'result' => true,
  311.             'mode' => (is_int($result))?'insert':'update'
  312.             );
  313.         }
  314.         catch (FormException $formError{
  315.             $this->dbh->rollback();
  316.             //Формируем JS массив ошибок который будет разбираться на клиенте
  317.             $errors $this->saver->getErrors();
  318.             foreach ($errors as $errorFieldName{
  319.                 $message['errors'][array(
  320.                 'field'=>$this->translate('FIELD_'.strtoupper($errorFieldName)),
  321.                 'message'=>$this->translate($this->saver->getDataDescription()->getFieldDescriptionByName($errorFieldName)->getPropertyValue('message'))
  322.                 );
  323.             }
  324.             $JSONResponse array_merge(array('result'=>false'header'=>$this->translate('TXT_SHIT_HAPPENS'))$message);
  325.         }
  326.         catch (SystemException $e){
  327.             if ($transactionStarted{
  328.                 $this->dbh->rollback();
  329.             }
  330.             $message['errors'][array('message'=>$e->getMessage().current($e->getCustomMessage()));
  331.             $JSONResponse array_merge(array('result'=>false'header'=>$this->translate('TXT_SHIT_HAPPENS'))$message);
  332.  
  333.         }
  334.  
  335.         $this->response->setHeader('Content-Type''text/javascript; charset=utf-8');
  336.         $this->response->write(json_encode($JSONResponse));
  337.         $this->response->commit();
  338.     }
  339.  
  340.     /**
  341.      * Если поле OrderColumn присутствует в списке, убираем его
  342.      *
  343.      * @return DataDescription 
  344.      * @access protected
  345.      */
  346.  
  347.     protected function createDataDescription({
  348.         if (in_array($this->getAction()array('printData''exportCSV'))) {
  349.             $previousAction $this->getAction();
  350.             $this->config->setCurrentMethod(self::DEFAULT_ACTION_NAME);
  351.             $result parent::createDataDescription();
  352.             $this->config->setCurrentMethod($previousAction);
  353.         }
  354.         else {
  355.             $result parent::createDataDescription();
  356.             if (in_array($this->getAction()array('main''edit''add''save''getRawData')) && ($col $this->getOrderColumn()) && ($field $result->getFieldDescriptionByName($col))) {
  357.                 $result->removeFieldDescription($field);
  358.             }
  359.         }
  360.         return $result;
  361.     }
  362.  
  363.     /**
  364.      * Переписан родительский метод генерации ошибки, поскольку для AJAX такая не подходит
  365.      *
  366.      * @param string тип ошибки
  367.      * @param string сообщение об ошибке
  368.      * @param mixed  необязательная дополнительная информация об ошибке
  369.      *
  370.      * @return void 
  371.      * @access protected
  372.      */
  373.     protected function generateError($errorType$errorMessage$errorCustomInfo false{
  374.         $message['errors'][array('message'=>$errorMessage);
  375.         $response array_merge(array('result'=>false'header'=>$this->translate('TXT_SHIT_HAPPENS'))$message);
  376.         return $response;
  377.     }
  378.     /**
  379.      * Возвращает объект Saver
  380.      * Есть смысл вызывать эту функцию только внутри save/saveSata
  381.      * во всех остальных случаях она возвращает false
  382.      * 
  383.      * @return Saver 
  384.      * @access protected
  385.      * @final
  386.      */
  387.     final protected function getSaver(){
  388.         if(is_null($this->saver)){
  389.             $this->saver new Saver();
  390.         }
  391.  
  392.         return $this->saver;
  393.     }
  394.  
  395.     final protected function setSaver(Saver $saver){
  396.         $this->saver $saver;
  397.     }
  398.  
  399.     /**
  400.      * Внутренний метод сохранения
  401.      *
  402.      * @return mixed 
  403.      * @access protected
  404.      */
  405.  
  406.     protected function saveData ({
  407.         $result false;
  408.         //если в POST не пустое значение значение первичного ключа - значит мы находимся в режиме редактирования
  409.         if (isset($_POST[$this->getTableName()][$this->getPK()]&& !empty($_POST[$this->getTableName()][$this->getPK()])) {
  410.             $mode self::COMPONENT_TYPE_FORM_ALTER;
  411.             $this->setFilter(array($this->getPK()=>$_POST[$this->getTableName()][$this->getPK()]));
  412.         }
  413.         else {
  414.             $mode self::COMPONENT_TYPE_FORM_ADD;
  415.         }
  416.  
  417.         //создаем объект описания данных
  418.         $dataDescriptionObject new DataDescription();
  419.  
  420.         if (!method_exists($this$this->getPreviousAction())) {
  421.             throw new SystemException('ERR_NO_ACTION'SystemException::ERR_CRITICAL);
  422.         }
  423.  
  424.         //получаем описание полей для метода
  425.         $configDataDescription $this->config->getMethodConfig($this->getPreviousAction());
  426.         //если в конфиге есть описание полей для метода - загружаем их
  427.         if (isset($configDataDescription->fields)) {
  428.             $dataDescriptionObject->loadXML($configDataDescription->fields);
  429.         }
  430.  
  431.         //Создаем объект описания данных взятых из БД
  432.         $DBDataDescription new DataDescription();
  433.         //Загружаем в него инфу о колонках
  434.         $DBDataDescription->load($this->loadDataDescription());
  435.         $this->setDataDescription($dataDescriptionObject->intersect($DBDataDescription));
  436.         //Поле с порядком следования убираем из списка
  437.         /**
  438.          * @todo  Надо бы это как то переделать, потому что разбросано получилось
  439.          * часть кода относящаяся к обработке колонки с нумерацией здесь, часть
  440.          * @see Grid::createDataDescription
  441.          */
  442.         if (($col $this->getOrderColumn()) && ($field $this->getDataDescription()->getFieldDescriptionByName($col))) {
  443.             $this->getDataDescription()->removeFieldDescription($field);
  444.         }
  445.  
  446.         $dataObject new Data();
  447.         $dataObject->load($this->loadData());
  448.         $this->setData($dataObject);
  449.  
  450.         //Создаем сейвер
  451.         $saver $this->getSaver();
  452.  
  453.         //Устанавливаем его режим
  454.         $saver->setMode($mode);
  455.         $saver->setDataDescription($this->getDataDescription());
  456.         $saver->setData($this->getData());
  457.  
  458.         if($saver->validate(=== true{
  459.             $saver->setFilter($this->getFilter());
  460.             $saver->save();
  461.             $result $saver->getResult();
  462.  
  463.         }
  464.         else {
  465.             //выдвигается пустой exception который перехватывается в методе save
  466.             throw new FormException();
  467.         }
  468.  
  469.         //Если у нас режим вставки и определена колонка для порядка следования, изменяем порядок следования
  470.         if (($orderColumn $this->getOrderColumn()) && ($mode == self::COMPONENT_TYPE_FORM_ADD)) {
  471.             $this->addFilterCondition(array($this->getPK().'!='.$result));
  472.             $request 'UPDATE '.$this->getTableName().' SET '.$orderColumn.'='.$orderColumn.'+1 '.$this->dbh->buildWhereCondition($this->getFilter());
  473.             $this->dbh->modifyRequest($request);
  474.         }
  475.  
  476.         return $result;
  477.     }
  478.  
  479.  
  480.     /**
  481.      * Переопределенный метод построения
  482.      * Перед построением - добавляется перевод
  483.      * После построения добавляется информация о закладках
  484.      *
  485.      * @return void 
  486.      * @access public
  487.      */
  488.  
  489.     public function build({
  490.         switch ($this->getAction()) {
  491.             case 'imageManager':
  492.                 return $this->imageManager->build();
  493.                 break;
  494.             case 'fileLibrary':
  495.                 return $this->fileLibrary->build();
  496.                 break;
  497.             default:
  498.                 // do nothing
  499.         }
  500.  
  501.         if ($this->getType(== self::COMPONENT_TYPE_LIST{
  502.             $this->addTranslation('MSG_CONFIRM_DELETE');
  503.         }
  504.  
  505.         $result parent::build();
  506.         return $result;
  507.     }
  508.     /**
  509.      * Для действия main не выводим данные
  510.      * Для действия save определяем другой формат данных
  511.      *
  512.      * @return mixed 
  513.      * @access protected
  514.      */
  515.  
  516.     protected function loadData({
  517.         if ($this->getAction(== self::DEFAULT_ACTION_NAME{
  518.             $result false;
  519.         }
  520.         elseif ($this->getAction(== 'save'{
  521.             if (!isset($_POST[$this->getTableName()])) {
  522.                 throw new SystemException('ERR_NO_DATA'SystemException::ERR_CRITICAL);
  523.             }
  524.  
  525.             $data $_POST[$this->getTableName()];
  526.             //Приводим данные к стандартному виду
  527.             $result array($data);
  528.             if ($this->getTranslationTableName()) {
  529.                 if (!isset($_POST[$this->getTranslationTableName()])) {
  530.                     throw new SystemException('ERR_NO_DATA'SystemException::ERR_CRITICAL);
  531.                 }
  532.                 $result array();
  533.                 $multidata $_POST[$this->getTranslationTableName()];
  534.                 foreach ($multidata as $langID => $langValues{
  535.                     $idx arrayPush($result$data);
  536.                     $result[$idx]['lang_id'$langID;
  537.                     foreach ($langValues as $fieldName => $fieldValue{
  538.                         $result[$idx][$fieldName$fieldValue;
  539.                     }
  540.                 }
  541.             }
  542.  
  543.         }
  544.         elseif ($this->getAction(=='getRawData'{
  545.             $this->applyUserFilter();
  546.             $result parent::loadData();
  547.         }
  548.         else {
  549.             $result parent::loadData();
  550.         }
  551.  
  552.         return $result;
  553.     }
  554.  
  555.     
  556.     final protected function uploadVideo(){
  557.         try{
  558.         $uploadPath 'uploads/protected/';
  559.         $js =
  560.             'var doc = window.parent.document;'."\n".
  561.             'var pb = doc.getElementById(\'progress_bar\'); '."\n".
  562.             'var iframe = doc.getElementById(\'uploader\'); '."\n".
  563.             'var fieldId = iframe.getAttribute(\'field\');'."\n".
  564.             'var preview = doc.getElementById(iframe.getAttribute(\'preview\')); '."\n".
  565.             'var path = doc.getElementById(fieldId);'."\n";
  566.         
  567.             if (empty($_FILES|| !isset($_FILES['file'])) {
  568.                 throw new SystemException('ERR_NO_FILE'SystemException::ERR_CRITICAL);
  569.             }
  570.             
  571.             $uploader new VideoUploader();
  572.             $uploader->setFile($_FILES['file']);
  573.             $uploader->upload($uploadPath);
  574.             $fileName $uploader->getFileObjectName();
  575.             $js .= 
  576.                 "doc.window.insertVideo('".Request::getInstance()->getRootPath().$fileName."', fieldId);\n";
  577.             $js .= sprintf(
  578.             'path.value ="%s";'.
  579.             'pb.parentNode.removeChild(pb); '.
  580.             'iframe.parentNode.removeChild(iframe);',
  581.             $fileName,
  582.             $uploadPath.basename($fileName)
  583.             );
  584.         }
  585.         catch (SystemException $e{
  586.             $js .=
  587.             'pb.parentNode.removeChild(pb); '."\n".
  588.             'alert(\''.$this->translate('TXT_SHIT_HAPPENS').': '.$e->getMessage().'\'); '.
  589.             'iframe.parentNode.removeChild(iframe); '."\n";
  590.         }
  591.         
  592.         $responseText '<html><head/><body><script type="text/javascript">'.$js.'</script></body></html>';
  593.         $response Response::getInstance();
  594.         $response->setHeader('Content-Type''text/html; charset=UTF-8');
  595.         $response->setHeader('Cache-Control''no-cache');
  596.         $response->write($responseText);
  597.         $response->commit();        
  598.         
  599.     }
  600.  
  601.     /**
  602.      * Метод для заливки файла
  603.      * Вызывается в невидимом фрейме и должен отдать HTML страницу включающаю скрипт
  604.      *
  605.      * @return void 
  606.      * @access protected
  607.      * @final
  608.      */
  609.     final protected function upload({
  610.         try {
  611.             if (isset($_GET['protected'])) {
  612.                 $uploadPath 'uploads/protected/';
  613.             }
  614.             else {
  615.                 $uploadPath 'uploads/private/';
  616.             }
  617.  
  618.             $js =
  619.             'var doc = window.parent.document;'."\n".
  620.             'var pb = doc.getElementById(\'progress_bar\'); '."\n".
  621.             'var iframe = doc.getElementById(\'uploader\'); '."\n".
  622.             'var path = doc.getElementById(iframe.getAttribute(\'field\'));'."\n".
  623.             'var link = doc.getElementById(iframe.getAttribute(\'link\')); '."\n".
  624.             'var preview = doc.getElementById(iframe.getAttribute(\'preview\')); '."\n";
  625.  
  626.             if (empty($_FILES|| !isset($_FILES['file'])) {
  627.                 throw new SystemException('ERR_NO_FILE'SystemException::ERR_CRITICAL);
  628.             }
  629.  
  630.             $uploader new FileUploader();
  631.             $uploader->setFile($_FILES['file']);
  632.             $uploader->upload($uploadPath);
  633.             $fileName $uploader->getFileObjectName();
  634.             if (in_array($uploader->getExtension()array('gif''png''jpg''jpeg'))) {
  635.                 $js .= "preview.setAttribute('src', '".$fileName."');\n".
  636.                 "preview.style.display = 'block';\n";
  637.             }
  638.             else {
  639.                 $js .= "preview.removeAttribute('src');\n".
  640.                 "preview.style.display = 'none';\n";
  641.             }
  642.  
  643.             $js .= "link.innerHTML = '".$fileName."';\n".
  644.             "link.href = '".$fileName."';\n";
  645.  
  646.             $js .= sprintf(
  647.             'path.value ="%s";'.
  648.             'pb.parentNode.removeChild(pb); '.
  649.             'iframe.parentNode.removeChild(iframe);',
  650.             $fileName,
  651.             $uploadPath.basename($fileName)
  652.             );
  653.         }
  654.         catch (SystemException $e{
  655.             $js .=
  656.             'pb.parentNode.removeChild(pb); '."\n".
  657.             'alert(\''.$this->translate('TXT_SHIT_HAPPENS').': '.$e->getMessage().'\'); '.
  658.             'iframe.parentNode.removeChild(iframe); '."\n";
  659.         }
  660.  
  661.         $responseText '<html><head/><body><script type="text/javascript">'.$js.'</script></body></html>';
  662.         $response Response::getInstance();
  663.         $response->setHeader('Content-Type''text/html; charset=UTF-8');
  664.         $response->write($responseText);
  665.         $response->commit();
  666.     }
  667.  
  668.  
  669.     /**
  670.      * Выводит компонент: менеджер изображений
  671.      *
  672.      * @return void 
  673.      * @access protected
  674.      */
  675.     protected function imageManager({
  676.         $this->imageManager  = $this->document->componentManager->createComponent('imagemanager''image''ImageManager'null);
  677.         //$this->imageManager->getAction();
  678.         $this->imageManager->run();
  679.     }
  680.  
  681.     /**
  682.      * Выводит компонент: библиотека изображений
  683.      *
  684.      * @return void 
  685.      * @access protected
  686.      */
  687.     protected function fileLibrary({
  688.         $this->request->setPathOffset($this->request->getPathOffset(1);
  689.         $this->fileLibrary = $this->document->componentManager->createComponent('filelibrary''share''FileLibrary'null);
  690.         //$this->fileLibrary->getAction();
  691.         $this->fileLibrary->run();
  692.     }
  693.  
  694.     /**
  695.      * Метод генерящий thumbnail и сохраняющий его в БД
  696.      *
  697.      * @param $sourceFileName string имя исходного файла
  698.      * @param $destFieldName string имя поля
  699.      * @param $width int ширина
  700.      * @param $height int высота
  701.      * @param $filter array фильтр
  702.      * @param $rewrite boolean переписывать ли если уже существует
  703.      * @return имя файла - превьюхи
  704.      * @access protected
  705.      */
  706.  
  707.     protected function generateThumbnail($sourceFileName$destFieldName$width$height$filter$rewrite true{
  708.         $destFileName false;
  709.         if (!empty($sourceFileName)) {
  710.             list($dirname$basename$extension$filenamearray_values(pathinfo($sourceFileName));
  711.             $destFileName $dirname.'/'.'.'.$filename.'.'.$width.'-'.$height.'.'.$extension;
  712.             if (
  713.             (
  714.             file_exists($fullDestFileName dirname($_SERVER['SCRIPT_FILENAME']).'/'.$destFileName)
  715.             && $rewrite
  716.             )
  717.             || !file_exists($fullDestFileName)
  718.             {
  719.                 $image new Image();
  720.                 $image->loadFromFile($sourceFileName);
  721.                 $image->resize($width,$height);
  722.                 $image->saveToFile($destFileName);
  723.                 
  724.                 //Сохраняем в БД
  725.                 $this->dbh->modify(QAL::UPDATE$this->getTableName()array($destFieldName=>$destFileName)$filter);
  726.             }
  727.         }
  728.  
  729.         return $destFileName;
  730.     }
  731.  
  732.     /**
  733.      * Устанавливает имя колонки для пользовательской сортировки
  734.      *
  735.      * @return void 
  736.      * @access protected
  737.      */
  738.  
  739.     protected function setOrderColumn($columnName{
  740.         $this->orderColumn $columnName;
  741.         $this->setOrder(array($columnName=>QAL::ASC));
  742.     }
  743.  
  744.     /**
  745.       * Возвращает имя колонки для пользовательской сортировки
  746.       *
  747.       * @return string 
  748.       * @access protected
  749.       */
  750.  
  751.     protected function getOrderColumn({
  752.         return $this->orderColumn;
  753.     }
  754.  
  755.     /**
  756.      * Метод для изменения порядка следования  - вверх
  757.      *
  758.      * @return void 
  759.      * @access protected
  760.      */
  761.  
  762.     protected function up({
  763.         $this->response->setHeader('Content-Type''text/javascript; charset=utf-8');
  764.         $this->response->write($this->changeOrder(Grid::DIR_UP));
  765.         $this->response->commit();
  766.     }
  767.  
  768.     /**
  769.      * Метод для изменения порядка следования  - вниз
  770.      *
  771.      * @return void 
  772.      * @access protected
  773.      */
  774.  
  775.     protected function down({
  776.         $this->response->setHeader('Content-Type''text/javascript; charset=utf-8');
  777.         $this->response->write($this->changeOrder(Grid::DIR_DOWN));
  778.         $this->response->commit();
  779.     }
  780.  
  781.     /**
  782.      * Изменяет порядок следования
  783.      *
  784.      * @param string  - направление
  785.      * @return JSON String
  786.      * @access protected
  787.      */
  788.  
  789.     protected function changeOrder($direction{
  790.  
  791.         try {
  792.  
  793.             if (!$this->getOrderColumn()) {
  794.                 //Если не задана колонка для пользовательской сортировки то на выход
  795.                 throw new SystemException('ERR_NO_ORDER_COLUMN'SystemException::ERR_DEVELOPER);
  796.             }
  797.  
  798.             $currentID $this->getActionParams();
  799.             list($currentID$currentID;
  800.  
  801.             //Определяем order_num текущей страницы
  802.             $currentOrderNum simplifyDBResult(
  803.                 $this->dbh->selectRequest(
  804.                     'SELECT '.$this->getOrderColumn().' '.
  805.                     'FROM '.$this->getTableName().' '.
  806.                     'WHERE '.$this->getPK().' = %s',
  807.                     $currentID
  808.                 ),
  809.                 $this->getOrderColumn(),
  810.                 true
  811.             );
  812.  
  813.             $orderDirection ($direction == Grid::DIR_DOWN)?QAL::ASC:QAL::DESC;
  814.  
  815.             $baseFilter $this->getFilter();
  816.  
  817.             if(!empty($baseFilter)){
  818.                 $baseFilter ' AND '.str_replace('WHERE'''$this->dbh->buildWhereCondition($this->getFilter()));
  819.             }
  820.             else{
  821.                 $baseFilter '';
  822.             }
  823.  
  824.             //Определяем идентификатор записи которая находится рядом с текущей
  825.             $request =
  826.                 'SELECT '.$this->getPK().' as neighborID, '.$this->getOrderColumn().' as neighborOrderNum '.
  827.                 'FROM '.$this->getTableName().' '.
  828.                 'WHERE '.$this->getOrderColumn().' '.$direction.' '.$currentOrderNum.' '.$baseFilter.
  829.                 'ORDER BY '.$this->getOrderColumn().' '.$orderDirection.' Limit 1';
  830.  
  831.             $data convertDBResult($this->dbh->selectRequest($request)'neighborID');
  832.             if ($data{
  833.                 extract(current($data));
  834.                 $this->dbh->beginTransaction();
  835.                 $this->dbh->modify(
  836.                     QAL::UPDATE,
  837.                     $this->getTableName(),
  838.                     array($this->getOrderColumn()=>$neighborOrderNum),
  839.                     array($this->getPK()=>$currentID)
  840.                 );
  841.                 $this->dbh->modify(
  842.                     QAL::UPDATE,
  843.                     $this->getTableName(),
  844.                     array($this->getOrderColumn()=>$currentOrderNum),
  845.                     array($this->getPK()=>$neighborID)
  846.                 );
  847.                 $this->dbh->commit();
  848.             }
  849.  
  850.             $JSONResponse array(
  851.             'result' => true,
  852.             'dir' => $direction
  853.             );
  854.         }
  855.         catch (SystemException $e){
  856.             $JSONResponse $this->generateError($e->getCode()$e->getMessage());
  857.  
  858.         }
  859.         return json_encode($JSONResponse);
  860.     }
  861.  
  862.     /**
  863.      * Метод применеющий фильтр
  864.      *
  865.      * @return void 
  866.      * @access protected
  867.      */
  868.  
  869.     protected function applyUserFilter({
  870.         //Формат фильтра
  871.         //$_POST['filter'][$tableName][$fieldName] = значение фильтра
  872.         if (isset($_POST['filter'])) {
  873.             $tableName key($_POST['filter']);
  874.             $fieldName key($_POST['filter'][$tableName]);
  875.             $value trim($_POST['filter'][$tableName][$fieldName]);
  876.             //получили текущий фильтр
  877.             $currentFilter $this->getFilter();
  878.             if ($currentFilter{
  879.                 $currentFilter =str_replace('WHERE''',$this->dbh->buildWhereCondition($currentFilter)).' AND ';
  880.             }
  881.             else {
  882.                 $currentFilter '';
  883.             }
  884.             //к текущему фильтру присоединяем пользовательский
  885.             $this->setFilter($currentFilter.$tableName.'.'.$fieldName.' LIKE \'%'.$value.'%\' ');
  886.         }
  887.     }
  888.  
  889.     /**
  890.       * Метод выводящий данные для печати
  891.       *
  892.       * @return void 
  893.       * @access protected
  894.       */
  895.  
  896.     protected function printData({
  897.         $this->setParam('recordsPerPage'false);
  898.         $this->setProperty('exttype''print');
  899.         $this->prepare();
  900.     }
  901.  
  902.     /**
  903.      * Выводит список в файл в формате CSV
  904.      *
  905.      * @return void 
  906.      * @access protected
  907.      */
  908.  
  909.     protected function exportCSV({
  910.         //Если у нас есть таблица с переводами то експортить не получится
  911.         if ($this->getTranslationTableName()) {
  912.             throw new SystemException('ERR_CANT_EXPORT'SystemException::ERR_DEVELOPER);
  913.         }
  914.  
  915.         $this->setParam('recordsPerPage'false);
  916.  
  917.         $this->prepare();
  918.         $data array();
  919.         $filename $this->getTitle().'.csv';
  920.         $MIMEType 'application/csv';
  921.  
  922.         foreach ($this->getDataDescription(as $fieldName => $fieldInfo{
  923.               $data[0][$fieldInfo->getPropertyValue('title');
  924.         }
  925.  
  926.         for ($i=0$i $this->getData()->getRowCount()$i++){
  927.             foreach ($this->getDataDescription(as $fieldName => $fieldInfo{
  928.                 $data[$i+1][]$this->getData()->getFieldByName($fieldName)->getRowData($i);
  929.             }
  930.         }
  931.         $data array_reduce($dataarray($this'prepareCSVString'));
  932.         $this->downloadFile($data$MIMEType$filename);
  933.     }
  934.  
  935.     /**
  936.      * Формирует стоку в формате CSV из массива
  937.      * Callback для array_reduce
  938.      *
  939.      * @param $result результирующая строка
  940.      * @param $nextValue array
  941.      * @return string 
  942.      * @access private
  943.      * @see Grid::exportCSV()
  944.      */
  945.  
  946.     private function prepareCSVString($resultArray $nextValue{
  947.         $separator '"';
  948.         $delimiter ',';
  949.         $rowDelimiter "\r\n";
  950.         if (!empty($result)) {
  951.             $result .= $rowDelimiter;
  952.         }
  953.         $row '';
  954.         foreach ($nextValue as $fieldValue{
  955.             $row .= $separator.mb_convert_encoding(str_replace(array($separator$delimiter),array("''",';'),$fieldValue)'Windows-1251''UTF-8').$separator.$delimiter;
  956.         }
  957.         $row substr($row0-1);
  958.  
  959.         return $result.$row;
  960.     }
  961.     /**
  962.      * Добавляет переводы для WYSIWYG при необходимости
  963.      * 
  964.      * @access private
  965.      * @return void 
  966.      */
  967.     private function addToolbarTranslations(){
  968.         $this->addTranslation('TXT_OPEN_FIELD');
  969.         $this->addTranslation('TXT_CLOSE_FIELD');
  970.         
  971.         foreach($this->getDataDescription(as $fd){
  972.             if(($fd->getType(== FieldDescription::FIELD_TYPE_HTML_BLOCK)){
  973.                 $this->addWYSIWYGTranslations();       
  974.                 break;
  975.             }
  976.         }
  977.     }
  978.     
  979.     /**
  980.      * Строит список дополнительных файлов
  981.      * Используется в тех случаях когда необходимо создать дополнительную вкладку с приаттачеными к записи файлами
  982.      * 
  983.      * Сохранение приаттаченных данных должно происходить в методе saveData на общих основаниях
  984.      * @see DivisionEditor
  985.      * @see ProductEditor
  986.      *  
  987.      * @access protected
  988.      * @return void 
  989.      */
  990.     protected function addAttFilesField($tableName$data true){
  991.             $field new FieldDescription('attached_files');
  992.             $field->setType(FieldDescription::FIELD_TYPE_CUSTOM);
  993.             $field->setProperty('tabName'$this->translate('TAB_ATTACHED_FILES'));
  994.             $field->setProperty('tableName'$tableName);
  995.             $this->getDataDescription()->addFieldDescription($field);
  996.             
  997.            //Добавляем поле с дополнительными файлами
  998.             $field new Field('attached_files');
  999.  
  1000.             //Ссылки на добавление и удаление файла
  1001.             $this->addTranslation('BTN_ADD_FILE');
  1002.             $this->addTranslation('BTN_DEL_FILE');
  1003.             $attachedFilesData $this->buildAttachedFiles($data)
  1004.             for ($i 0$i count(Language::getInstance()->getLanguages())$i++{
  1005.                 $field->addRowData($attachedFilesData);
  1006.             }
  1007.             $this->getData()->addField($field);
  1008.     }
  1009.     /**
  1010.      * @param $data Данные
  1011.      * 
  1012.      * @access private
  1013.      * @return DOMNode 
  1014.      */
  1015.     
  1016.     private function buildAttachedFiles($data){
  1017.         $builder new SimpleBuilder();
  1018.         $dd new DataDescription();
  1019.         $f new FieldDescription('upl_id');
  1020.         $dd->addFieldDescription($f);
  1021.         /*
  1022.         $f = new FieldDescription('upl_is_main');
  1023.         $f->setType(FieldDescription::FIELD_TYPE_BOOL);
  1024.         $dd->addFieldDescription($f);
  1025. */
  1026.         $f new FieldDescription('upl_name');
  1027.         $dd->addFieldDescription($f);
  1028.  
  1029.         $f new FieldDescription('upl_path');
  1030.         $f->setProperty('title'$this->translate('FIELD_UPL_FILE'));
  1031.         $dd->addFieldDescription($f);
  1032.  
  1033.         $d new Data();
  1034.             
  1035.         if(is_array($data)){
  1036.             $d->load($data);
  1037.             $pathField $d->getFieldByName('upl_path');
  1038.             foreach ($pathField as $i => $path{
  1039.                 if(@file_exists($path&& @getimagesize($path)){
  1040.                     $thumbnailPath dirname($path).'/.'.basename($path);
  1041.                     $pathField->setRowProperty($i'real_image'$path);
  1042.                     if(@file_exists($thumbnailPath&& @getimagesize($thumbnailPath)){
  1043.                         $path $thumbnailPath;
  1044.                     }
  1045.                     $pathField->setRowData($i$path);
  1046.                     $pathField->setRowProperty($i'is_image'true);
  1047.                 }
  1048.             }
  1049.         }
  1050.  
  1051.         $this->addTranslation('MSG_NO_ATTACHED_FILES');
  1052.  
  1053.         $builder->setData($d);
  1054.         $builder->setDataDescription($dd);
  1055.  
  1056.         $builder->build();
  1057.  
  1058.         return $builder->getResult();    
  1059.     }
  1060.     
  1061. }
В создании документации нам помог: phpDocumentor