Просмотр исходного кода

Ticket #5425 - Tasks: Export reported time. (#5864)

Anton 6 дней назад
Родитель
Сommit
8e9fe6dcfa

+ 15 - 0
modules/boonex/russian/data/langs/bx_tasks/ru.xml

@@ -5,6 +5,10 @@
     <string name="_bx_tasks_cmts"><![CDATA[Задания - Комментарии]]></string>
     <string name="_bx_tasks_reactions"><![CDATA[Задания - Реакции]]></string>
 
+
+    <!-- Options -->
+    <string name="_bx_tasks_options_category_export"><![CDATA[Экспорт]]></string>
+
     <string name="_bx_tasks_option_enable_auto_approve"><![CDATA[Автоматически активировать задание после создания]]></string>
     <string name="_bx_tasks_option_summary_chars"><![CDATA[Количество символов в автогенерируемой сводке к публикациям]]></string>
     <string name="_bx_tasks_option_plain_summary_chars"><![CDATA[Количество символов в описании публикации при отсутствующей картинке публикации]]></string>
@@ -13,6 +17,16 @@
     <string name="_bx_tasks_option_rss_num"><![CDATA[Количество элементов в ленте RSS]]></string>
     <string name="_bx_tasks_option_searchable_fields"><![CDATA[Поля для поиска]]></string>
     <string name="_bx_tasks_option_auto_activation_for_categories"><![CDATA[Автоактивация для категорий]]></string>
+    <string name="_bx_tasks_option_export_time_to"><![CDATA[Экспортировать учтенное время в]]></string>
+    <string name="_bx_tasks_option_export_time_to_csv"><![CDATA[Файл CSV (значения, разделённые запятыми)]]></string>
+    <string name="_bx_tasks_option_export_time_to_xml"><![CDATA[Файл XML (расширяемый язык разметки)]]></string>
+    <string name="_bx_tasks_option_export_time_fields"><![CDATA[Поля, включаемые в экспортируемый отчете о затраченном времени]]></string>
+    <string name="_bx_tasks_option_export_time_field_author_id"><![CDATA[Профиль]]></string>
+    <string name="_bx_tasks_option_export_time_field_object_id"><![CDATA[Задача]]></string>
+    <string name="_bx_tasks_option_export_time_field_value"><![CDATA[Время]]></string>
+    <string name="_bx_tasks_option_export_time_field_value_date"><![CDATA[Дата]]></string>
+    <string name="_bx_tasks_option_export_time_field_text"><![CDATA[Примечание]]></string>
+    <string name="_bx_tasks_option_export_time_field_date"><![CDATA[Дата добавления]]></string>
 
     <!-- Forms -->
     <string name="_bx_tasks_form_entry"><![CDATA[Задания]]></string>
@@ -465,6 +479,7 @@
     <string name="_bx_tasks_grid_action_title_tm_edit"><![CDATA[Редактировать]]></string>
     <string name="_bx_tasks_grid_action_title_tm_delete"><![CDATA[Удалить]]></string>
     <string name="_bx_tasks_grid_action_title_tm_calculate"><![CDATA[Рассчитать]]></string>
+    <string name="_bx_tasks_grid_action_title_tm_export"><![CDATA[Экспортировать]]></string>
     <string name="_bx_tasks_grid_filter_item_title_tm_select_one_author_id"><![CDATA[Любой исполнитель]]></string>
     <string name="_bx_tasks_grid_filter_item_title_tm_select_one_context_module"><![CDATA[Любой модуль]]></string>
     <string name="_bx_tasks_grid_filter_item_title_tm_select_one_object_id"><![CDATA[Любая задача]]></string>

+ 3 - 0
modules/boonex/tasks/classes/BxTasksConfig.php

@@ -123,6 +123,8 @@ class BxTasksConfig extends BxBaseModTextConfig
             'PARAM_NUM_RSS' => 'bx_tasks_rss_num',
             'PARAM_SEARCHABLE_FIELDS' => 'bx_tasks_searchable_fields',
             'PARAM_PER_PAGE_BROWSE_SHOWCASE' => 'bx_tasks_per_page_browse_showcase',
+            'PARAM_EXPORT_TIME_TO' => 'bx_tasks_export_time_to',
+            'PARAM_EXPORT_TIME_FIELDS' => 'bx_tasks_export_time_fields',
 
             // objects
             'OBJECT_STORAGE' => 'bx_tasks_covers',
@@ -130,6 +132,7 @@ class BxTasksConfig extends BxBaseModTextConfig
             'OBJECT_STORAGE_PHOTOS' => 'bx_tasks_photos',
             'OBJECT_STORAGE_VIDEOS' => 'bx_tasks_videos',
             'OBJECT_STORAGE_CMTS' => 'bx_tasks_files_cmts', // for comments
+            'OBJECT_STORAGE_EXPORT_FILES' => 'sys_files',
             'OBJECT_IMAGES_TRANSCODER_PREVIEW' => 'bx_tasks_preview',
             'OBJECT_IMAGES_TRANSCODER_GALLERY' => 'bx_tasks_gallery',
             'OBJECT_IMAGES_TRANSCODER_COVER' => 'bx_tasks_cover',

+ 5 - 0
modules/boonex/tasks/classes/BxTasksDb.php

@@ -24,6 +24,11 @@ class BxTasksDb extends BxBaseModTextDb
         ];
     }
 
+    public function getExportTimeFields()
+    {
+        return $this->getFields('bx_tasks_time_track');
+    }
+
     public function getContexts($aParams = []) 
     {
         $CNF = &$this->_oConfig->CNF;

+ 164 - 0
modules/boonex/tasks/classes/BxTasksGridTimeContextAdministration.php

@@ -18,6 +18,8 @@ class BxTasksGridTimeContextAdministration extends BxTasksGridTime
     {
         parent::__construct ($aOptions, $oTemplate);
 
+        $this->_sPaginateType = 'load_more';
+
         if(($iContextPid = bx_get('context_pid')) !== false) 
             $this->setContextPid($iContextPid);
     }
@@ -52,6 +54,55 @@ class BxTasksGridTimeContextAdministration extends BxTasksGridTime
         return parent::performActionCalculate();
     }
 
+    public function performActionExport()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $aFields = explode(',', getParam($CNF['PARAM_EXPORT_TIME_FIELDS']));
+        if(!$aFields || !is_array($aFields)) {
+            echoJson([]);
+            return;
+        }
+
+        $aTitles = $aConverters = [];
+        foreach($aFields as $sField) {
+            $aTitles[$sField] = _t('_bx_tasks_option_export_time_field_' . $sField);
+            
+            $sConverter = '_exportFieldConverter' . bx_gen_method_name($sField);
+            if(method_exists($this, $sConverter))
+                $aConverters[$sField] = $sConverter;
+        }
+
+        $sMethod = '_exportTo' . bx_gen_method_name(getParam($CNF['PARAM_EXPORT_TIME_TO']));
+        if(!method_exists($this, $sMethod)) {
+            echoJson([]);
+            return;
+        }
+
+        $aIds = $this->_getIds();
+        if(!$aIds || !is_array($aIds)) {
+            echoJson([]);
+            return;
+        }
+
+        if(($aFile = $this->$sMethod($aIds, $aFields, $aTitles, $aConverters)) === false) {
+            echoJson([]);
+            return;
+        }
+
+        list($sFilePath, $sFileUrl) = $aFile;
+
+        $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE_EXPORT_FILES']);
+        if($oStorage && ($iFileId = $oStorage->storeFileFromPath($sFilePath, true, bx_get_logged_profile_id())) !== false) {
+            $sFileUrl = $oStorage->getFileUrlById($iFileId);
+
+            @unlink($sFilePath);
+        }
+
+        echoJson(['redirect' => $sFileUrl]);
+        return;
+    }
+
     public function getCode ($isDisplayHeader = true)
     {
         if($this->_isAdministration() && !$this->_isAllowedByConext())
@@ -100,6 +151,119 @@ class BxTasksGridTimeContextAdministration extends BxTasksGridTime
 
         return parent::__getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
     }
+
+    protected function _exportToCsv($aIds, $aFields, $aTitles, $aConverters)
+    {
+        $sFileDiv = ';';
+        $sFileName = 'tasks-reported-time-' . date('Y.m.d') . '.csv';
+        $sFilePath = BX_DIRECTORY_PATH_TMP . $sFileName;
+        $oFileHandler = fopen($sFilePath, 'w');
+        if(!$oFileHandler)
+            return false;
+
+        fputcsv($oFileHandler, $aTitles, $sFileDiv);
+
+        $aFieldsFlip = array_flip($aFields);
+        foreach($aIds as $iId) {
+            $aTrack = $this->_oModule->_oDb->getTimeTracks(['sample' => 'id', 'id' => $iId]);
+            if(!$aTrack || !is_array($aTrack))
+                continue;
+
+            $aConvert = array_intersect_key($aConverters, $aTrack);
+            foreach($aConvert as $sField => $sMethod)
+                $aTrack[$sField] = $this->$sMethod($aTrack[$sField]);
+
+            fputcsv($oFileHandler, array_intersect_key($aTrack, $aFieldsFlip), $sFileDiv);
+        }
+
+        fclose($oFileHandler);
+
+        return [$sFilePath, BX_DOL_URL_ROOT . 'tmp/' . $sFileName];
+    }
+
+    protected function _exportToXml($aIds, $aFields, $aTitles, $aConverters)
+    {
+        $sFileName = 'tasks-reported-time-' . date('Y.m.d') . '.xml';
+        $sFilePath = BX_DIRECTORY_PATH_TMP . $sFileName;
+        $oFileHandler = fopen($sFilePath, 'w');
+        if(!$oFileHandler)
+            return false;
+
+        $aFieldsFlip = array_flip($aFields);
+
+        $oXML = xmlwriter_open_memory();
+        xmlwriter_set_indent($oXML, 1);
+        xmlwriter_set_indent_string($oXML, '  ');
+        
+        xmlwriter_start_document($oXML, '1.0', 'UTF-8');
+        xmlwriter_start_element($oXML, 'items');
+
+        foreach($aIds as $iId) {
+            $aTrack = $this->_oModule->_oDb->getTimeTracks(['sample' => 'id', 'id' => $iId]);
+            if(!$aTrack || !is_array($aTrack))
+                continue;
+
+            $aConvert = array_intersect_key($aConverters, $aTrack);
+            foreach($aConvert as $sField => $sMethod)
+                $aTrack[$sField] = $this->$sMethod($aTrack[$sField]);
+
+            xmlwriter_start_element($oXML, 'item');
+            $aTrackFields = array_intersect_key($aTrack, $aFieldsFlip);
+            foreach($aTrackFields as $sTrackField => $sTrackValue) {
+                xmlwriter_start_element($oXML, $sTrackField);
+                xmlwriter_write_cdata($oXML, $sTrackValue);
+                xmlwriter_end_element($oXML);
+            }
+            xmlwriter_end_element($oXML);
+        }
+
+        xmlwriter_end_document($oXML);
+
+        fwrite($oFileHandler, xmlwriter_output_memory($oXML));
+        fclose($oFileHandler);
+
+        return [$sFilePath, BX_DOL_URL_ROOT . 'tmp/' . $sFileName];
+    }
+
+    protected function _exportFieldConverterAuthorId($mixedValue)
+    {
+        return BxDolProfile::getInstanceMagic($mixedValue)->getDisplayName();
+    }
+
+    protected function _exportFieldConverterObjectId($mixedValue)
+    {
+        return $this->_oModule->serviceGetTitle($mixedValue);
+    }
+
+    protected function _exportFieldConverterValue($mixedValue)
+    {
+        return $this->__exportFieldConverterTime($mixedValue);
+    }
+
+    protected function _exportFieldConverterValueDate($mixedValue)
+    {
+        return $this->__exportFieldConverterDate($mixedValue);
+    }
+
+    protected function _exportFieldConverterDate($mixedValue)
+    {
+        return $this->__exportFieldConverterDateTime($mixedValue);
+    }
+
+    protected function __exportFieldConverterDate($mixedValue)
+    {
+        return date('Y.m.d', $mixedValue);
+    }
+
+    protected function __exportFieldConverterDateTime($mixedValue)
+    {
+        return date('Y.m.d H:i', $mixedValue);
+    }
+
+    protected function __exportFieldConverterTime($mixedValue)
+    {
+        return $this->_oModule->_oConfig->timeI2S($mixedValue);
+    }
 }
 
 /** @} */

+ 25 - 0
modules/boonex/tasks/classes/BxTasksModule.php

@@ -190,6 +190,31 @@ class BxTasksModule extends BxBaseModTextModule implements iBxDolCalendarService
         ]);
     }
 
+    public function serviceGetOptionsExportTimeTo()
+    {
+        return [
+            'csv' => _t('_bx_tasks_option_export_time_to_csv'),
+            'xml' => _t('_bx_tasks_option_export_time_to_xml'),
+        ];
+    }
+    
+    public function serviceGetOptionsExportTimeFields()
+    {
+        $aResult = [];
+        $sLkPrefix = '_bx_tasks_option_export_time_field_';
+
+        $aFields = $this->_oDb->getExportTimeFields();
+        if($aFields && ($aOriginal = $aFields['original'] ?? false) && is_array($aOriginal)) {
+            foreach($aOriginal as $sField) {
+                if(($sLk = $sLkPrefix . $sField) && ($_sLk = _t($sLk)) && strcmp($sLk, $_sLk) != 0) {
+                    $aResult[] = ['key' => $sField, 'value' => $_sLk];
+                }
+            }
+        }
+
+        return $aResult;
+    }
+
     public function serviceGetInitialMembers($iContextPid, $sTerm)
     {
         if($this->_bIsApi) {

+ 14 - 0
modules/boonex/tasks/install/langs/en.xml

@@ -5,6 +5,9 @@
     <string name="_bx_tasks_cmts"><![CDATA[Tasks' Comments]]></string>
     <string name="_bx_tasks_reactions"><![CDATA[Tasks' Reactions]]></string>
 
+    <!-- Options -->
+    <string name="_bx_tasks_options_category_export"><![CDATA[Export]]></string>
+
     <string name="_bx_tasks_option_enable_auto_approve"><![CDATA[Auto approve a task after creation]]></string>
     <string name="_bx_tasks_option_summary_chars"><![CDATA[Number of characters in auto-cropped task summary]]></string>
     <string name="_bx_tasks_option_plain_summary_chars"><![CDATA[Number of characters in plain task description when task hasn't thumb]]></string>
@@ -13,6 +16,16 @@
     <string name="_bx_tasks_option_rss_num"><![CDATA[Number of items in RSS feed]]></string>
     <string name="_bx_tasks_option_searchable_fields"><![CDATA[Searchable fields for keyword search]]></string>
     <string name="_bx_tasks_option_auto_activation_for_categories"><![CDATA[Auto-Activation For Categories]]></string>
+    <string name="_bx_tasks_option_export_time_to"><![CDATA[Export reported time to]]></string>
+    <string name="_bx_tasks_option_export_time_to_csv"><![CDATA[CSV (Comma-Separated Values) file]]></string>
+    <string name="_bx_tasks_option_export_time_to_xml"><![CDATA[XML (Extensible Markup Language) file]]></string>
+    <string name="_bx_tasks_option_export_time_fields"><![CDATA[Fields to include in exporting time report]]></string>
+    <string name="_bx_tasks_option_export_time_field_author_id"><![CDATA[Profile]]></string>
+    <string name="_bx_tasks_option_export_time_field_object_id"><![CDATA[Task]]></string>
+    <string name="_bx_tasks_option_export_time_field_value"><![CDATA[Time]]></string>
+    <string name="_bx_tasks_option_export_time_field_value_date"><![CDATA[Date]]></string>
+    <string name="_bx_tasks_option_export_time_field_text"><![CDATA[Note]]></string>
+    <string name="_bx_tasks_option_export_time_field_date"><![CDATA[Date Added]]></string>
 
     <!-- Forms -->
     <string name="_bx_tasks_form_entry"><![CDATA[Tasks]]></string>
@@ -468,6 +481,7 @@
     <string name="_bx_tasks_grid_action_title_tm_edit"><![CDATA[Edit]]></string>
     <string name="_bx_tasks_grid_action_title_tm_delete"><![CDATA[Remove]]></string>
     <string name="_bx_tasks_grid_action_title_tm_calculate"><![CDATA[Calculate]]></string>
+    <string name="_bx_tasks_grid_action_title_tm_export"><![CDATA[Export]]></string>
     <string name="_bx_tasks_grid_filter_item_title_tm_select_one_author_id"><![CDATA[Any Assignee]]></string>
     <string name="_bx_tasks_grid_filter_item_title_tm_select_one_context_module"><![CDATA[Any Module]]></string>
     <string name="_bx_tasks_grid_filter_item_title_tm_select_one_object_id"><![CDATA[Any Task]]></string>

+ 15 - 4
modules/boonex/tasks/install/sql/enable.sql

@@ -21,6 +21,14 @@ INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `c
 ('bx_tasks_searchable_fields', 'title,text', @iCategId, '_bx_tasks_option_searchable_fields', 'list', '', '', 'a:2:{s:6:"module";s:8:"bx_tasks";s:6:"method";s:21:"get_searchable_fields";}', 30),
 ('bx_tasks_auto_activation_for_categories', 'on', @iCategId, '_bx_tasks_option_auto_activation_for_categories', 'checkbox', '', '', '', 35);
 
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, 'bx_tasks_export', '_bx_tasks_options_category_export', 2);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `extra`, `order`) VALUES
+('bx_tasks_export_time_to', 'csv', @iCategId, '_bx_tasks_option_export_time_to', 'select', '', '', 'a:2:{s:6:"module";s:8:"bx_tasks";s:6:"method";s:26:"get_options_export_time_to";}', 10),
+('bx_tasks_export_time_fields', 'author_id,object_id,value,value_date', @iCategId, '_bx_tasks_option_export_time_fields', 'list', '', '', 'a:2:{s:6:"module";s:8:"bx_tasks";s:6:"method";s:30:"get_options_export_time_fields";}', 12);
+
 -- PAGE: create entry
 
 INSERT INTO `sys_objects_page`(`object`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `uri`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
@@ -108,6 +116,7 @@ INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module
 
 INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
 ('bx_tasks_context_time_administration', 2, 'bx_tasks', '_bx_tasks_page_block_title_sys_menu_in_context', '_bx_tasks_page_block_title_menu_in_context', 13, 2147483647, 'service', 'a:3:{s:6:"module";s:8:"bx_tasks";s:6:"method";s:22:"get_block_menu_context";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 1),
+('bx_tasks_context_time_administration', 2, 'bx_tasks', '_bx_tasks_page_block_title_sys_entries_budget_in_context_summary', '_bx_tasks_page_block_title_entries_budget_in_context_summary', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:8:"bx_tasks";s:6:"method";s:16:"get_block_budget";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 2),
 ('bx_tasks_context_time_administration', 3, 'bx_tasks', '_bx_tasks_page_block_title_sys_entries_time_in_context_administration', '_bx_tasks_page_block_title_entries_time_in_context_administration', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:8:"bx_tasks";s:6:"method";s:21:"get_block_manage_time";s:6:"params";a:2:{i:0;s:14:"administration";i:1;s:12:"{profile_id}";}}', 0, 0, 1, 1);
 
 -- PAGE: entries' budget in context (all)
@@ -479,8 +488,8 @@ INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `fie
 ('bx_tasks_time_administration', 'Sql', 'SELECT `ttt`.*, `tt`.`title`, `tp`.`id` AS `context_id`, `tp`.`type` AS `context_module` FROM `bx_tasks_time_track` AS `ttt` INNER JOIN `bx_tasks_tasks` AS `tt` ON `ttt`.`object_id`=`tt`.`id` INNER JOIN `sys_profiles` AS `tp` ON ABS(`tt`.`allow_view_to`)=`tp`.`id` WHERE 1 ', 'bx_tasks_time_track', 'id', 'value_date', '', '', 100, NULL, 'start', '', 'ttt`.`text,tt`.`title,tt`.`text', '', 'like', 'date', '', 192, 'BxTasksGridTimeAdministration', 'modules/boonex/tasks/classes/BxTasksGridTimeAdministration.php'),
 ('bx_tasks_time_common', 'Sql', 'SELECT `ttt`.*, `tt`.`title`, `tp`.`id` AS `context_id`, `tp`.`type` AS `context_module` FROM `bx_tasks_time_track` AS `ttt` INNER JOIN `bx_tasks_tasks` AS `tt` ON `ttt`.`object_id`=`tt`.`id` INNER JOIN `sys_profiles` AS `tp` ON ABS(`tt`.`allow_view_to`)=`tp`.`id` WHERE 1 ', 'bx_tasks_time_track', 'id', 'value_date', '', '', 100, NULL, 'start', '', 'ttt`.`text,tt`.`title,tt`.`text', '', 'like', 'date', '', 2147483647, 'BxTasksGridTimeCommon', 'modules/boonex/tasks/classes/BxTasksGridTimeCommon.php'),
 
-('bx_tasks_time_context_administration', 'Sql', 'SELECT `ttt`.*, `tt`.`title` FROM `bx_tasks_time_track` AS `ttt` INNER JOIN `bx_tasks_tasks` AS `tt` ON `ttt`.`object_id`=`tt`.`id` WHERE 1 ', 'bx_tasks_time_track', 'id', 'value_date', '', '', 50, NULL, 'start', '', 'ttt`.`text,tt`.`title,tt`.`text', '', 'like', 'date', '', 2147483647, 'BxTasksGridTimeContextAdministration', 'modules/boonex/tasks/classes/BxTasksGridTimeContextAdministration.php'),
-('bx_tasks_time_context_common', 'Sql', 'SELECT `ttt`.*, `tt`.`title` FROM `bx_tasks_time_track` AS `ttt` INNER JOIN `bx_tasks_tasks` AS `tt` ON `ttt`.`object_id`=`tt`.`id` WHERE 1 ', 'bx_tasks_time_track', 'id', 'value_date', '', '', 50, NULL, 'start', '', 'ttt`.`text,tt`.`title,tt`.`text', '', 'like', 'date', '', 2147483647, 'BxTasksGridTimeContextCommon', 'modules/boonex/tasks/classes/BxTasksGridTimeContextCommon.php');
+('bx_tasks_time_context_administration', 'Sql', 'SELECT `ttt`.*, `tt`.`title` FROM `bx_tasks_time_track` AS `ttt` INNER JOIN `bx_tasks_tasks` AS `tt` ON `ttt`.`object_id`=`tt`.`id` WHERE 1 ', 'bx_tasks_time_track', 'id', 'value_date', '', '', 20, NULL, 'start', '', 'ttt`.`text,tt`.`title,tt`.`text', '', 'like', 'date', '', 2147483647, 'BxTasksGridTimeContextAdministration', 'modules/boonex/tasks/classes/BxTasksGridTimeContextAdministration.php'),
+('bx_tasks_time_context_common', 'Sql', 'SELECT `ttt`.*, `tt`.`title` FROM `bx_tasks_time_track` AS `ttt` INNER JOIN `bx_tasks_tasks` AS `tt` ON `ttt`.`object_id`=`tt`.`id` WHERE 1 ', 'bx_tasks_time_track', 'id', 'value_date', '', '', 20, NULL, 'start', '', 'ttt`.`text,tt`.`title,tt`.`text', '', 'like', 'date', '', 2147483647, 'BxTasksGridTimeContextCommon', 'modules/boonex/tasks/classes/BxTasksGridTimeContextCommon.php');
 
 INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
 ('bx_tasks_time_administration', 'checkbox', '_sys_select', '2%', 0, 0, '', 1),
@@ -522,11 +531,13 @@ INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon
 ('bx_tasks_time_common', 'bulk', 'delete', '_bx_tasks_grid_action_title_tm_delete', '', 0, 1, 2),
 
 ('bx_tasks_time_context_administration', 'bulk', 'calculate', '_bx_tasks_grid_action_title_tm_calculate', '', 0, 0, 1),
-('bx_tasks_time_context_administration', 'bulk', 'delete', '_bx_tasks_grid_action_title_tm_delete', '', 0, 1, 2),
+('bx_tasks_time_context_administration', 'bulk', 'export', '_bx_tasks_grid_action_title_tm_export', '', 0, 0, 2),
+('bx_tasks_time_context_administration', 'bulk', 'delete', '_bx_tasks_grid_action_title_tm_delete', '', 0, 1, 3),
 
 ('bx_tasks_time_context_common', 'independent', 'add', '_bx_tasks_grid_action_title_tm_add', '', 0, 0, 1),
 ('bx_tasks_time_context_common', 'bulk', 'calculate', '_bx_tasks_grid_action_title_tm_calculate', '', 0, 0, 1),
-('bx_tasks_time_context_common', 'bulk', 'delete', '_bx_tasks_grid_action_title_tm_delete', '', 0, 1, 2),
+('bx_tasks_time_context_common', 'bulk', 'export', '_bx_tasks_grid_action_title_tm_export', '', 0, 0, 2),
+('bx_tasks_time_context_common', 'bulk', 'delete', '_bx_tasks_grid_action_title_tm_delete', '', 0, 1, 3),
 ('bx_tasks_time_context_common', 'single', 'edit', '_bx_tasks_grid_action_title_tm_edit', 'pencil-alt', 1, 0, 1),
 ('bx_tasks_time_context_common', 'single', 'delete', '_bx_tasks_grid_action_title_tm_delete', 'remove', 1, 1, 2);