Explorar el Código

Ticket #4558 - Build-in API support: Courses.

AntonLV hace 2 años
padre
commit
6bdf12fe12

+ 6 - 0
modules/boonex/courses/classes/BxCoursesConfig.php

@@ -351,6 +351,12 @@ class BxCoursesConfig extends BxBaseModGroupsConfig
         
         return $aResult;
     }
+    
+    public function getContentNodeTitle($iLevel, $bSingle = true)
+    {
+        $aLevelToNode = $this->getContentLevel2Node($bSingle);
+        return isset($aLevelToNode[$iLevel]) ? $aLevelToNode[$iLevel] : _t('_undefined');
+    }
 
     public function getUsageI2S($iUsage, $bLowerCase = true)
     {

+ 16 - 2
modules/boonex/courses/classes/BxCoursesDb.php

@@ -116,7 +116,7 @@ class BxCoursesDb extends BxBaseModGroupsDb
         $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
 
         $sSelectClause = "`tcs`.*";
-        $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+        $sJoinClause = $sWhereClause = $sGroupClause = $sOrderClause = $sLimitClause = "";
 
         switch($aParams['sample']) {
             case 'id':
@@ -182,6 +182,17 @@ class BxCoursesDb extends BxBaseModGroupsDb
                     $sLimitClause = $aParams['start'] . ', ' . $aParams['per_page'];
                 break;
                 
+            case 'entry_id_counters':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'entry_id' => $aParams['entry_id']
+                ];
+
+                $sSelectClause = "COUNT(`tcs`.`id`) AS `cn_l1`, SUM(`tcs`.`cn_l2`) AS `cn_l2`, SUM(`tcs`.`cn_l3`) AS `cn_l3`";
+                $sWhereClause = "AND `tcs`.`entry_id`=:entry_id AND `tcs`.`level`='1'";
+                $sGroupClause = "`tcs`.`entry_id`";
+                break;
+                
             case 'parent_id':
                 $aMethod['params'][1] = [
                     'parent_id' => $aParams['parent_id']
@@ -213,13 +224,16 @@ class BxCoursesDb extends BxBaseModGroupsDb
                 break;
         }
 
+        if(!empty($sGroupClause))
+            $sGroupClause = "GROUP BY " . $sGroupClause;
+
         if(!empty($sOrderClause))
             $sOrderClause = "ORDER BY " . $sOrderClause;
         
         if(!empty($sLimitClause))
             $sLimitClause = "LIMIT " . $sLimitClause;
 
-        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $CNF['TABLE_CNT_STRUCTURE'] . "` AS `tcs` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $CNF['TABLE_CNT_STRUCTURE'] . "` AS `tcs` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " " . $sGroupClause . " " . $sOrderClause . " " . $sLimitClause;
         return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
     }
 

+ 28 - 5
modules/boonex/courses/classes/BxCoursesGridCntStructureManage.php

@@ -292,14 +292,23 @@ class BxCoursesGridCntStructureManage extends BxTemplGrid
         if($this->_iLevel != $this->_iLevelMax)
             return $this->_bIsApi ? [] : '';
 
-        $sCounters = '';
+        $mixedCounters = '';
         if(!empty($mixedValue) && ($aCounters = json_decode(html_entity_decode($mixedValue), true)))
-            $sCounters = $this->_oModule->_oTemplate->getCounters($aCounters);
+            $mixedCounters = $this->_oModule->_oTemplate->getCounters($aCounters);
+
+        if($this->_bIsApi) {
+            $sResult = '';
+            foreach($mixedCounters as $sUsage => $aCounters) {
+                foreach($aCounters as $iKey => $aCounter)
+                    $aCounters[$iKey] = $aCounter['value'] . ' ' . $aCounter['title'];
+                
+                $sResult .= ucfirst(_t('_bx_courses_txt_' . $sUsage)) . ': ' . implode(', ', $aCounters) . ' ';
+            }
 
-        if($this->_bIsApi)
-            return ['type' => 'text', 'data' => $sCounters];
+            return ['type' => 'text', 'value' => trim($sResult)];
+        }
 
-        return parent::_getCellDefault($sCounters, $sKey, $aField, $aRow);
+        return parent::_getCellDefault($mixedCounters, $sKey, $aField, $aRow);
     }
 
     protected function _getCellAdded($mixedValue, $sKey, $aField, $aRow)
@@ -355,6 +364,20 @@ class BxCoursesGridCntStructureManage extends BxTemplGrid
         return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
     }
 
+    protected function _updateOrder($mixedId, $iOrder)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sTable = $CNF['TABLE_CNT_STRUCTURE'];
+        $sFieldId = 'node_id';
+        $sFieldOrder = $this->_aOptions['field_order'];
+
+        return BxDolDb::getInstance()->query("UPDATE `{$sTable}` SET `{$sFieldOrder}` = :order WHERE `{$sFieldId}` = :id", [
+            'id' => $mixedId,
+            'order' => $iOrder
+        ]);
+    }
+
     protected function _switcherChecked2State($isChecked)
     {
         return $isChecked ? 'active' : 'hidden';

+ 49 - 22
modules/boonex/courses/classes/BxCoursesModule.php

@@ -329,7 +329,7 @@ class BxCoursesModule extends BxBaseModGroupsModule
             'level' => 1,
             'selected' => ($iNodeId = bx_get('parent_id')) !== false ? (int)$iNodeId : 0,
             'start' => ($iStart = bx_get('start')) !== false ? (int)$iStart : 0,
-            'per_page' => ($iPerPage = bx_get('per_page')) !== false ? (int)$iPerPage : 0,
+            'per_page' => ($iPerPage = bx_get('per_page')) !== false ? (int)$iPerPage : 2,
         ]);
 
         return $this->_bIsApi ? [bx_api_get_block('course_structure', $mixedResult)] : $mixedResult;
@@ -459,7 +459,7 @@ class BxCoursesModule extends BxBaseModGroupsModule
         $sPassStatus = $sPassTitle = '';
         $iLevelMax = $this->_oConfig->getContentLevelMax();
         if(isset($aPassDetails[$iLevelMax]) && ($aPassLevelMax = $aPassDetails[$iLevelMax])) {
-            $iPassPercent = (int)round(100 * $aPassLevelMax['passed']/$aPassLevelMax['total']);
+            $iPassPercent = ($iTotal = (int)$aPassLevelMax['total']) != 0 ? (int)round(100 * $aPassLevelMax['passed']/$iTotal) : 0;
 
             if($aPassLevelMax['passed'] != 0) {
                 if($aPassLevelMax['passed'] != $aPassLevelMax['total']) {
@@ -586,32 +586,59 @@ class BxCoursesModule extends BxBaseModGroupsModule
         $CNF = $this->_oConfig->CNF;
 
         $aResult = parent::decodeDataAPI($aData, $aParams);
+        $aResultAdd = [];
+
+        $iEntryId = (int)$aData[$CNF['FIELD_ID']];
+        $aLevelToNode = $this->_oConfig->getContentLevel2Node(false);        
+        if($this->isFan($iEntryId, $this->_iProfileId)) {
+            $sTxtProgress = _t('_bx_courses_txt_n_m_progress');
+
+            list($iPassPercent, $aPassDetails, $sPassStatus, $sPassTitle) = $this->getEntryPass($this->_iProfileId, $iEntryId);
+
+            $aCounters = [];
+            foreach($aPassDetails as $iLevel => $aDetails) {
+                $aCounters[] = [
+                    'level' => $iLevel,
+                    'title' => $aLevelToNode[$iLevel],
+                    'passed' => $aDetails['passed'],
+                    'total' => $aDetails['total'],
+                    'progress' => bx_replace_markers($sTxtProgress, $aDetails)
+                ];
+            }
 
-        $aLevelToNodePl = $this->_oConfig->getContentLevel2Node(false);
-        $sTxtProgress = _t('_bx_courses_txt_n_m_progress');
+            $aResultAdd = [
+                'percent' => $iPassPercent,
+                'counters' => $aCounters,
+                'status' => $sPassStatus,
+                'show_pass' => $iPassPercent > 0 && $iPassPercent < 100,
+            ];
 
-        list($iPassPercent, $aPassDetails, $sPassStatus, $sPassTitle) = $this->getEntryPass($this->_iProfileId, $aData[$CNF['FIELD_ID']]);
+            if($aResultAdd['show_pass'])
+                $aResultAdd = array_merge($aResultAdd, [
+                    'pass_link' => $aResult['url'],
+                    'pass_title' => $sPassTitle,
+                ]);
+            
+        }
+        else {
+            $iLevelMax = $this->_oConfig->getContentLevelMax();
+            $aEntryCounters = $this->_oDb->getContentStructure(['sample' => 'entry_id_counters', 'entry_id' => $iEntryId]);
+
+            $aCounters = [];
+            for($i = 1; $i <= $iLevelMax; $i++) {
+                $aCounters[] = [
+                    'level' => $i,
+                    'title' => $aLevelToNode[$i],
+                    'total' => $aEntryCounters['cn_l' . $i]
+                ];
+            }
 
-        $aCounters = [];
-        foreach($aPassDetails as $iLevel => $aDetails) {
-            $aCounters[] = [
-                'level' => $iLevel,
-                'title' => $aLevelToNodePl[$iLevel],
-                'passed' => $aDetails['passed'],
-                'total' => $aDetails['total'],
-                'progress' => bx_replace_markers($sTxtProgress, $aDetails)
+            $aResultAdd = [
+                'counters' => $aCounters,
             ];
         }
 
-        $bShowPass = $iPassPercent > 0 && $iPassPercent < 100;
-        return array_merge($aResult, [
-            'percent' => $iPassPercent,
-            'counters' => $aCounters,
-            'status' => $sPassStatus,
-            'show_pass' => $bShowPass,
-            'pass_link' => $bShowPass ? $aResult['url'] : '',
-            'pass_title' => $bShowPass ? $sPassTitle : '',
-        ]);
+        return array_merge($aResult, $aResultAdd);
     }
 
     protected function _getOptionsContentModules()

+ 98 - 77
modules/boonex/courses/classes/BxCoursesTemplate.php

@@ -42,7 +42,7 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
         $bVarsShowProgress = $bVarsShoStats = false;
         $aVarsShowProgress = $aVarsShowStats = [true];
         if(!empty($aParams['context']) && in_array($aParams['context'], ['favorite', 'joined_entries'])) {
-            $aLevelToNodePl = $this->_oConfig->getContentLevel2Node(false);
+            $aLevelToNode = $this->_oConfig->getContentLevel2Node(false);
             $sTxtProgress = _t('_bx_courses_txt_n_m_progress');
 
             list($iPassPercent, $aPassDetails, $sPassStatus, $sPassTitle) = $this->_oModule->getEntryPass($this->_iProfileId, $aData[$CNF['FIELD_ID']]);
@@ -55,7 +55,7 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
             $aTmplVarsCounters = [];
             foreach($aPassDetails as $iLevel => $aDetails) {
                 $aTmplVarsCounters[] = [
-                    'cn_title' => $aLevelToNodePl[$iLevel],
+                    'cn_title' => $aLevelToNode[$iLevel],
                     'cn_passed' => $aDetails['passed'],
                     'cn_total' => $aDetails['total'],
                     'cn_progress' => bx_replace_markers($sTxtProgress, $aDetails)
@@ -105,7 +105,7 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
                     ];
 
                     if($this->_bIsApi)
-                        $aTmplVars[$sUsage] = $aCounter;
+                        $aTmplVars[$sUsage][] = $aCounter;
                     else
                         $aTmplVarsCounters['bx_repeat:counters_' . $sUsage][] = $aCounter;
                 }
@@ -138,7 +138,7 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
         $iLevel = (int)$aParams['level'];
         $iSelected = (int)$aParams['selected'];
         $iStart = isset($aParams['start']) ? (int)$aParams['start'] : 0;
-        $iPerPage = isset($aParams['per_page']) ? (int)$aParams['per_page'] : 0;
+        $iPerPage = isset($aParams['per_page']) ? (int)$aParams['per_page'] : 2;
 
         $aNodes = $this->_oDb->getContentStructure([
             'sample' => 'entry_id_full', 
@@ -153,7 +153,7 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
             return '';
 
         $iLevelMax = $this->_oConfig->getContentLevelMax();
-        $aLevelToNodePl = $this->_oConfig->getContentLevel2Node(false);
+        $aLevelToNode = $this->_oConfig->getContentLevel2Node(false);
 
         $sTxtProgress = _t('_bx_courses_txt_n_m_progress');
         $sTmplKeysSelected = $this->_bIsApi ? 'selected' : 'bx_if:selected';
@@ -170,7 +170,7 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
                     continue;
 
                 $aTmplVarsCounters[] = [
-                    'cn_title' => $aLevelToNodePl[$i],
+                    'cn_title' => $aLevelToNode[$i],
                     'cn_passed' => $aNodeStats[$i]['passed'],
                     'cn_total' => $aNodeStats[$i]['total'],
                     'cn_progress' => bx_replace_markers($sTxtProgress, $aNodeStats[$i])
@@ -186,9 +186,9 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
                     'condition' => $bSelected,
                     'content' => [true]
                 ],
-                'index' => $iKey,
+                'index' => $iKey + 1,
                 'link' => $this->_bIsApi ? bx_api_get_relative_url($sLink) : $sLink,
-                'progress' => round(100 * $aNodeStats[$iLevelMax]['passed']/$aNodeStats[$iLevelMax]['total']),
+                'percent' => isset($aNodeStats[$iLevelMax]) && ($aStats = $aNodeStats[$iLevelMax]) && $aStats['total'] ? round(100 * $aStats['passed']/$aStats['total']) : 0,
                 'status' => $this->_getNodeStatus($iProfileId, $iContentId, $aNode['node_id']),
                 $sTmplKeysCounters => $aTmplVarsCounters
             ]);
@@ -255,7 +255,7 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
 
             $aNode = array_merge($aNode, [
                 'level_max' => $iLevelMax,
-                'index' => $iKey,
+                'index' => $iKey + 1,
                 'link' => $sLink,
                 'pass_percent' => $iPassPercent,
                 'pass_progress' => $sPassProgress,
@@ -363,7 +363,7 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
                     $bShowPass = !empty($sPassTitle);
                     
                     $aSubNode = array_merge($aSubNode, [
-                        'index' => $iKey,
+                        'index' => $iKey + 1,
                         'link' => $this->_bIsApi ? bx_api_get_relative_url($sLink) : $sLink,
                         'pass_percent' => $iPassPercent,
                         'pass_progress' => $sPassProgress,
@@ -415,8 +415,6 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
 
         if(!isset($aParams['node_id']))
             return '';
-        
-        $sJsObject = $this->_oConfig->getJsObject('entry');
 
         $iContentId = (int)$aContentInfo[$CNF['FIELD_ID']];
         $iNodeId = (int)$aParams['node_id'];
@@ -428,16 +426,58 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
             'id' => $iNodeId,
         ]);
 
+        $aTmplVars = [
+            'index' => $aNode['order'],
+            'sample' => $this->_oConfig->getContentNodeTitle($aNode['level']),
+            'title' => $aNode['title'],
+            'text' => $aNode['text']
+        ];
+
+        if($this->_bIsApi)
+            return array_merge($aTmplVars, [
+                'steps' => $this->_entryNodeItems($iProfileId, $iContentId, $aNode, BX_COURSES_CND_USAGE_ST),
+                'attachments' => $this->_entryNodeItems($iProfileId, $iContentId, $aNode, BX_COURSES_CND_USAGE_AT) 
+            ]);
+        else
+            $aTmplVars['bx_repeat:items'] = $this->_entryNodeItems($iProfileId, $iContentId, $aNode, $iUsage);
+            
+
+        $sMiName = 'node-data-';
+        $sMiLink = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY_NODE'] . '&id=' . $iContentId, [
+            'node_id' => $iNodeId,
+            'usage' => ''
+        ]);
+
+        $oMenu = new BxTemplMenu([
+            'template' => 'menu_block_submenu_ver.html', 
+            'menu_id'=> 'node-data', 
+            'menu_items' => [
+                ['id' => $sMiName . 'st', 'name' => $sMiName . 'st', 'class' => '', 'link' => $sMiLink . '0', 'target' => '_self', 'title' => _t('_bx_courses_menu_item_title_node_data_steps')],
+                ['id' => $sMiName . 'at', 'name' => $sMiName . 'at', 'class' => '', 'link' => $sMiLink . '1', 'target' => '_self', 'title' => _t('_bx_courses_menu_item_title_node_data_attachments')]
+            ]
+        ]);
+        $oMenu->setSelected('', $sMiName . $this->_oConfig->getUsageI2S($iUsage));
+
+        return [
+            'content' => $this->parseHtmlByName('node_view.html', $aTmplVars),
+            'menu' => $oMenu
+        ];
+    }
+
+    protected function _entryNodeItems($iProfileId, $iContentId, $aNode, $iUsage)
+    {
+        $sJsObject = $this->_oConfig->getJsObject('entry');
+
         $aDataItems = $this->_oDb->getContentData([
             'sample' => 'entry_node_ids', 
             'entry_id' => $iContentId,
-            'node_id' => $iNodeId,
+            'node_id' => $aNode['id'],
             'usage' => $iUsage
         ]);
 
         $sTxtUndefined = _t('_undefined');
 
-        $aTmplVarsItems = [];
+        $aResults = [];
         if(!empty($aDataItems) && is_array($aDataItems))
             foreach($aDataItems as $iIndex => $aDataItem) {
                 $sImageUrl = '';
@@ -474,72 +514,53 @@ class BxCoursesTemplate extends BxBaseModGroupsTemplate
                     'title' => $sTitle
                 ] : [true];
 
-                $aTmplVarsItems[] = $this->_bIsApi ? [
-                    'type' => $sType,
-                    'title' => $sTitle,
-                    'image' => $sImageUrl,
-                    'link_view' => $bTmplVarsShowLink ? bx_api_get_relative_url($sLink) : '',
-                    'link_pass' => $bTmplVarsPass ? bx_api_get_relative_url($sLink) : '',
-                ] : [
-                    'bx_if:show_image' => [
-                        'condition' => $sImageUrl,
-                        'content' => [
-                            'image' => $sImageUrl
-                        ]
-                    ],
-                    'bx_if:show_image_empty' => [
-                        'condition' => !$sImageUrl,
-                        'content' => [
-                            'type' => $sType
-                        ]
-                    ],
-                    'type' => $sType,
-                    'bx_if:show_link' => [
-                        'condition' => $bTmplVarsShowLink,
-                        'content' => $aTmplVarsShowLink
-                    ],
-                    'bx_if:show_text' => [
-                        'condition' => !$bTmplVarsShowLink,
-                        'content' => [
-                            'title' => $sTitle,
+                if($this->_bIsApi) {
+                    $sText = '';
+                    if(($sMethod = 'get_text') &&  bx_is_srv($aDataItem['content_type'], $sMethod))
+                        $sText = bx_srv($aDataItem['content_type'], $sMethod, [$aDataItem['content_id']]);
+
+                    $aResults[] = [
+                        'type' => $sType,
+                        'title' => $sTitle,
+                        'text' => $sText,
+                        'image' => $sImageUrl,
+                        'link_view' => $bTmplVarsShowLink ? bx_api_get_relative_url($sLink) : '',
+                        'link_pass' => $bTmplVarsPass ? bx_api_get_relative_url($sLink) : '',
+                    ];
+                }
+                else 
+                    $aResults[] = [
+                        'bx_if:show_image' => [
+                            'condition' => $sImageUrl,
+                            'content' => [
+                                'image' => $sImageUrl
+                            ]
+                        ],
+                        'bx_if:show_image_empty' => [
+                            'condition' => !$sImageUrl,
+                            'content' => [
+                                'type' => $sType
+                            ]
+                        ],
+                        'type' => $sType,
+                        'bx_if:show_link' => [
+                            'condition' => $bTmplVarsShowLink,
+                            'content' => $aTmplVarsShowLink
+                        ],
+                        'bx_if:show_text' => [
+                            'condition' => !$bTmplVarsShowLink,
+                            'content' => [
+                                'title' => $sTitle,
+                            ]
+                        ],
+                        'bx_if:show_pass' => [
+                            'condition' => $bTmplVarsPass,
+                            'content' => $aTmplVarsPass
                         ]
-                    ],
-                    'bx_if:show_pass' => [
-                        'condition' => $bTmplVarsPass,
-                        'content' => $aTmplVarsPass
-                    ]
-                ];
+                    ];
             }
 
-        if($this->_bIsApi)
-            return $aTmplVarsItems;
-
-        $sMiName = 'node-data-';
-        $sMiLink = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY_NODE'] . '&id=' . $iContentId, [
-            'node_id' => $iNodeId,
-            'usage' => ''
-        ]);
-
-        $oMenu = new BxTemplMenu([
-            'template' => 'menu_block_submenu_ver.html', 
-            'menu_id'=> 'node-data', 
-            'menu_items' => [
-                ['id' => $sMiName . 'st', 'name' => $sMiName . 'st', 'class' => '', 'link' => $sMiLink . '0', 'target' => '_self', 'title' => _t('_bx_courses_menu_item_title_node_data_steps')],
-                ['id' => $sMiName . 'at', 'name' => $sMiName . 'at', 'class' => '', 'link' => $sMiLink . '1', 'target' => '_self', 'title' => _t('_bx_courses_menu_item_title_node_data_attachments')]
-            ]
-        ]);
-        $oMenu->setSelected('', $sMiName . $this->_oConfig->getUsageI2S($iUsage));
-
-        return [
-            'content' => $this->parseHtmlByName('node_view.html', [
-                'index' => $aNode['order'],
-                'sample' => _t('_bx_courses_txt_sample_l' . $aNode['level'] . '_single'),
-                'title' => $aNode['title'],
-                'text' => $aNode['text'],
-                'bx_repeat:items' => $aTmplVarsItems
-            ]),
-            'menu' => $oMenu
-        ];
+        return $aResults;
     }
 
     protected function _getNodeStatus($iProfileId, $iContentId, $iNodeId)

+ 2 - 2
modules/boonex/courses/install/langs/en.xml

@@ -447,10 +447,10 @@
     <string name="_bx_courses_txt_pass_again"><![CDATA[Pass again]]></string>
     <string name="_bx_courses_txt_pass"><![CDATA[Pass]]></string>
     <string name="_bx_courses_txt_passed"><![CDATA[Passed]]></string>
-    <string name="_bx_courses_txt_steps"><![CDATA[steps]]></string>
     <string name="_bx_courses_txt_n_m_steps"><![CDATA[{0}/{1} steps]]></string>
     <string name="_bx_courses_txt_n_m_progress"><![CDATA[{passed}/{total}]]></string>
-    <string name="_bx_courses_txt_attachments"><![CDATA[attachments]]></string>
+    <string name="_bx_courses_txt_st"><![CDATA[steps]]></string>
+    <string name="_bx_courses_txt_at"><![CDATA[attachments]]></string>
     <string name="_bx_courses_txt_data_type_bx_files"><![CDATA[file]]></string>
     <string name="_bx_courses_txt_data_type_bx_photos"><![CDATA[image]]></string>
     <string name="_bx_courses_txt_data_type_bx_polls"><![CDATA[poll]]></string>

+ 3 - 3
modules/boonex/courses/install/sql/enable.sql

@@ -640,14 +640,14 @@ INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `fie
 ('bx_courses_cnt_structure_manage', 'Sql', 'SELECT `tcn`.*, `tcs`.`parent_id`, `tcs`.`level`, `tcs`.`cn_l2`, `tcs`.`cn_l3` FROM `bx_courses_content_nodes` AS `tcn` LEFT JOIN `bx_courses_content_structure` AS `tcs` ON `tcn`.`id`=`tcs`.`node_id` WHERE 1 ', 'bx_courses_content_nodes', 'id', 'order', 'status', '', 20, NULL, 'start', '', 'title', '', 'like', '', '', 2147483647, 'BxCoursesGridCntStructureManage', 'modules/boonex/courses/classes/BxCoursesGridCntStructureManage.php');
 
 INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
-('bx_courses_cnt_structure_manage', 'order', '', '1%', 0, 0, '', 1),
-('bx_courses_cnt_structure_manage', 'checkbox', '_sys_select', '2%', 0, 0, '', 2),
+('bx_courses_cnt_structure_manage', 'order', '', '4%', 0, 0, '', 1),
+('bx_courses_cnt_structure_manage', 'checkbox', '_sys_select', '4%', 0, 0, '', 2),
 ('bx_courses_cnt_structure_manage', 'switcher', '_bx_courses_grid_column_title_cnt_active', '7%', 0, 0, '', 3),
 ('bx_courses_cnt_structure_manage', 'title', '_bx_courses_grid_column_title_cnt_title', '35%', 0, 0, '', 4),
 ('bx_courses_cnt_structure_manage', 'cn_l2', '_bx_courses_grid_column_title_cnt_cn_l2', '10%', 0, 0, '', 5),
 ('bx_courses_cnt_structure_manage', 'cn_l3', '_bx_courses_grid_column_title_cnt_cn_l3', '10%', 0, 0, '', 6),
 ('bx_courses_cnt_structure_manage', 'counters', '_bx_courses_grid_column_title_cnt_counters', '20%', 0, 0, '', 7),
-('bx_courses_cnt_structure_manage', 'added', '_bx_courses_grid_column_title_cnt_added', '15%', 0, 0, '', 8),
+('bx_courses_cnt_structure_manage', 'added', '_bx_courses_grid_column_title_cnt_added', '10%', 0, 0, '', 8),
 ('bx_courses_cnt_structure_manage', 'actions', '', '20%', 0, 0, '', 9);
 
 INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES

+ 2 - 2
modules/boonex/courses/template/counters.html

@@ -1,6 +1,6 @@
 <div class="bx-courses-counters flex flex-col">
     <bx_if:show_st>
-        <div class="bx-courses-c-title capitalize"><bx_text:_bx_courses_txt_steps />:</div>
+        <div class="bx-courses-c-title capitalize"><bx_text:_bx_courses_txt_st />:</div>
         <div class="bx-courses-c-list flex flex-row flex-wrap">
             <bx_repeat:counters_st>
                 <div class="m-px px-2 bg-gray-200 border rounded-md whitespace-nowrap">__value__ <span class="lowercase">__title__</span></div>
@@ -8,7 +8,7 @@
         </div>
     </bx_if:show_st>
     <bx_if:show_at>
-        <div class="bx-courses-c-title mt-2 capitalize"><bx_text:_bx_courses_txt_attachments />:</div>
+        <div class="bx-courses-c-title mt-2 capitalize"><bx_text:_bx_courses_txt_at />:</div>
         <div class="bx-courses-c-list flex flex-row flex-wrap">
             <bx_repeat:counters_at>
                 <div class="m-px px-2 bg-gray-200 border rounded-md whitespace-nowrap">__value__ <span class="lowercase">__title__</span></div>

+ 2 - 2
modules/boonex/courses/template/node_l1.html

@@ -1,10 +1,10 @@
-<div class="bx-courses-node-l__level__ bx-def-box bx-def-box-round-corners bx-def-color-bg-box bx-def-color-bg-box-hover relative w-1/3 m-2<bx_if:selected> bg-gray-100</bx_if:selected>">
+<div class="bx-courses-node-l__level__ bx-def-box bx-def-box-round-corners bx-def-color-bg-box bx-def-color-bg-box-hover relative w-1/2 m-2<bx_if:selected> bg-gray-100</bx_if:selected>">
     <a class="bx-courses-node-l__level__-link hover:no-underline" href="__link__">
         <div class="bx-courses-node-l__level__-gap p-4">
             <div class="bx-courses-node-l__level__-cnt relative flex flex-col">
                 <div class="bx-courses-progress">
                     <div class="bx-courses-progress-bar border rounded-full opacity-50">
-                        <div class="h-1 bg-gray-300 rounded-full" style="width:__progress__%"></div>
+                        <div class="h-1 bg-gray-300 rounded-full" style="width:__percent__%"></div>
                     </div>
                     <!--
                     <div class="bx-courses-progress-percent text-xs"><bx_text:_bx_courses_txt_passed />: __progress__%</div>

+ 2 - 0
modules/boonex/russian/data/langs/bx_courses/ru.xml

@@ -448,6 +448,8 @@
     <string name="_bx_courses_txt_passed"><![CDATA[Пройдено]]></string>
     <string name="_bx_courses_txt_n_m_steps"><![CDATA[{0}/{1} шагов]]></string>
     <string name="_bx_courses_txt_n_m_progress"><![CDATA[{passed}/{total}]]></string>
+    <string name="_bx_courses_txt_st"><![CDATA[шаги]]></string>
+    <string name="_bx_courses_txt_at"><![CDATA[вложения]]></string>
     <string name="_bx_courses_txt_data_type_bx_files"><![CDATA[файл]]></string>
     <string name="_bx_courses_txt_data_type_bx_photos"><![CDATA[изображение]]></string>
     <string name="_bx_courses_txt_data_type_bx_polls"><![CDATA[опрос]]></string>

+ 3 - 2
template/scripts/BxBaseGrid.php

@@ -74,7 +74,7 @@ class BxBaseGrid extends BxDolGrid
         foreach ($aOrder as $mixedId)
             $this->_updateOrder($mixedId, ++$iOrder);
 
-        echoJson(array());
+        return $this->_getActionResult([]);
     }
 
     public function performActionDelete()
@@ -361,7 +361,8 @@ class BxBaseGrid extends BxDolGrid
                 if(!method_exists($this, $sMethod))
                     $sMethod = $sMethodDefault;
 
-                $aDataRv[$iKey][$sKey] = $this->$sMethod(isset($aData[$iKey][$sKey]) ? $aData[$iKey][$sKey] : _t('_undefined'), $sKey, $aField, $aRow);
+                if(($aCell = $this->$sMethod(isset($aData[$iKey][$sKey]) ? $aData[$iKey][$sKey] : _t('_undefined'), $sKey, $aField, $aRow)))
+                    $aDataRv[$iKey][$sKey] = $aCell;
             }
         }