Sfoglia il codice sorgente

#1904 Classes module: modules and classes reordering, block with classes grouped by modules, module field and adding module popup

Alex Trofimov 6 anni fa
parent
commit
6aead354fd

+ 1 - 1
modules/base/general/classes/BxBaseModGeneralDb.php

@@ -83,7 +83,7 @@ class BxBaseModGeneralDb extends BxDolModuleDb
         $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $CNF['TABLE_ENTRIES'] . "` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
 		return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
     }
-    
+
     public function getEntriesNumByContext ($iProfileId)
     {
         $sQuery = $this->prepare ("SELECT COUNT(*) FROM `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` WHERE `" . $this->_oConfig->CNF['FIELD_ALLOW_VIEW_TO'] . "` = ?", - $iProfileId);

+ 2 - 0
modules/boonex/classes/classes/BxClssConfig.php

@@ -29,6 +29,7 @@ class BxClssConfig extends BxBaseModTextConfig
 
             // database tables
             'TABLE_ENTRIES' => $aModule['db_prefix'] . 'classes',
+            'TABLE_MODULES' => $aModule['db_prefix'] . 'modules',
             'TABLE_ENTRIES_FULLTEXT' => 'title_text',
 
             // database fields
@@ -62,6 +63,7 @@ class BxClssConfig extends BxBaseModTextConfig
             'FIELD_ANONYMOUS' => 'anonymous',
             'FIELD_CMTS_SETTINGS' => 'cmts',
             'FIELD_AVAIL_SETTINGS' => 'avail',
+            'FIELD_MODULE' => 'module_id',
             'FIELDS_WITH_KEYWORDS' => 'auto', // can be 'auto', array of fields or comma separated string of field names, works only when OBJECT_METATAGS is specified
             'FIELDS_DELAYED_PROCESSING' => 'videos', // can be array of fields or comma separated string of field names
 

+ 42 - 0
modules/boonex/classes/classes/BxClssDb.php

@@ -18,6 +18,48 @@ class BxClssDb extends BxBaseModTextDb
     {
         parent::__construct($oConfig);
     }
+
+    public function getEntriesByModule ($iModuleId)
+    {
+        $sQuery = $this->prepare ("SELECT * FROM `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` WHERE `module_id` = ? ORDER BY `order`", $iModuleId);
+        return $this->getAll($sQuery);
+    }
+
+    public function getEntriesModulesByContext ($iProfileId, $bAsPairs = false)
+    {
+        $sQuery = $this->prepare ("SELECT * FROM `" . $this->_oConfig->CNF['TABLE_MODULES'] . "` WHERE `profile_id` = ? ORDER BY `order`", $iProfileId);
+        if ($bAsPairs)
+            return $this->getPairs($sQuery, 'id', 'module_title');
+        else
+            return $this->getAll($sQuery);
+    }
+
+    public function updateModulesOrder ($iProfileId, $aModulesOrder)
+    {
+        $iAffected = 0;
+        foreach ($aModulesOrder as $iOrder => $iModuleId) {
+            $iAffected += ($this->query("UPDATE `" . $this->_oConfig->CNF['TABLE_MODULES'] . "` SET `order` = :order WHERE `profile_id` = :profile_id AND `id` = :module_id", array(
+                'order' => $iOrder,
+                'profile_id' => $iProfileId,
+                'module_id' => $iModuleId,
+            )) ? 1 : 0);
+        }        
+        return $iAffected;
+    }
+
+    public function updateClassesOrder($iProfileId, $iModuleId, $aClassesOrder)
+    {
+        $iAffected = 0;
+        foreach ($aClassesOrder as $iOrder => $iClassId) {
+            $iAffected += ($this->query("UPDATE `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` SET `order` = :order, `module_id` = :module_id WHERE `allow_view_to` = :profile_id AND `id` = :class_id", array(
+                'order' => $iOrder,
+                'profile_id' => -$iProfileId,
+                'module_id' => $iModuleId,
+                'class_id' => $iClassId,
+            )) ? 1 : 0);
+        }        
+        return $iAffected;
+    }
 }
 
 /** @} */

+ 23 - 0
modules/boonex/classes/classes/BxClssFormEntry.php

@@ -15,6 +15,7 @@
 class BxClssFormEntry extends BxBaseModTextFormEntry
 {
     protected $_sGhostTemplateCover = 'form_ghost_template_cover.html';
+    protected $_oProfileContext = null;
 	
     public function __construct($aInfo, $oTemplate = false)
     {
@@ -23,6 +24,18 @@ class BxClssFormEntry extends BxBaseModTextFormEntry
 
         $CNF = &$this->_oModule->_oConfig->CNF;
 
+        $iProfileId = (int)bx_get('allow_view_to');
+        if ($iProfileId >= 0)
+            $iProfileId = (int)bx_get('id') ? bx_get('id') : bx_get('profile_id');
+        $iProfileId = abs($iProfileId);
+
+        if ($iProfileId)
+            $this->_oProfileContext = BxDolProfile::getInstance($iProfileId);
+
+        if (isset($this->aInputs[$CNF['FIELD_MODULE']]) && $this->_oProfileContext) {
+            $this->aInputs[$CNF['FIELD_MODULE']]['values'] = $this->_oModule->_oDb->getEntriesModulesByContext($this->_oProfileContext->id(), true);
+        }
+
     	if(isset($CNF['FIELD_COVER']) && isset($this->aInputs[$CNF['FIELD_COVER']])) {
             if($this->_oModule->checkAllowedSetThumb() === CHECK_ACTION_RESULT_ALLOWED) {
                 $this->aInputs[$CNF['FIELD_COVER']]['storage_object'] = $CNF['OBJECT_STORAGE'];
@@ -175,6 +188,16 @@ class BxClssFormEntry extends BxBaseModTextFormEntry
             'editor_id' => isset($CNF['FIELD_TEXT_ID']) ? $CNF['FIELD_TEXT_ID'] : ''
     	);
     }
+
+    function getCode($bDynamicMode = false)
+    {
+        if (!$this->_oProfileContext)
+            return MsgBox(_t('_bx_classes_txt_err_cant_add_class_without_context'));
+
+        // TODO: check permission for adding to context
+
+        return parent::getCode($bDynamicMode);
+    }
 }
 
 /** @} */

+ 177 - 0
modules/boonex/classes/classes/BxClssModule.php

@@ -28,6 +28,128 @@ class BxClssModule extends BxBaseModTextModule
         ));
     }
 
+    public function actionReorderClasses($iProfileId = 0)
+    {
+        $oProfileContext = $iProfileId ? BxDolProfile::getInstance($iProfileId) : null;
+        if (!$oProfileContext) {
+            echo _t('_sys_txt_error_occured');
+            exit;
+        }
+
+        // TODO: check permission for reordering
+
+        foreach ($_REQUEST as $k => $v) {
+            if (0 !== strncmp($k, 'classes_order_', 14))
+                continue;
+            $iModuleId = (int)str_replace('classes_order_', '', $k);
+            if (!$iModuleId)
+                continue;
+            
+            $aClassesOrder = explode(',', $v);
+            if (!$aClassesOrder || !is_array($aClassesOrder))
+                continue;
+
+            $this->_oDb->updateClassesOrder($iProfileId, $iModuleId, $aClassesOrder);
+        }
+    }
+
+    public function actionReorderModules($iProfileId = 0)
+    {
+        $oProfileContext = $iProfileId ? BxDolProfile::getInstance($iProfileId) : null;
+        if (!$oProfileContext) {
+            echo _t('_sys_txt_error_occured');
+            exit;
+        }
+
+        // TODO: check permission for reordering
+
+        $aModulesOrder = bx_get('modules_order');
+        if (!$aModulesOrder || !is_array($aModulesOrder)) {
+            echo _t('_sys_txt_error_occured');
+            exit;
+        }
+
+        $this->_oDb->updateModulesOrder($iProfileId, $aModulesOrder);
+    }
+
+    public function actionAddModule($iProfileId = 0)
+    {
+        $oProfileContext = $iProfileId ? BxDolProfile::getInstance($iProfileId) : null;
+        if (!$oProfileContext) {
+            echoJson(array('action' => 'ShowMsg', 'msg' => _t('_sys_txt_error_occured')));
+            exit;
+        }
+
+        // TODO: check permission for adding to context
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'bx-classes-module-add',
+                'action' => BX_DOL_URL_ROOT . 'modules/index.php?r=classes/add_module/' . $iProfileId,
+                'method' => 'post',
+            ),
+            'params' => array (
+                'db' => array(
+                    'submit_name' => 'do_submit',
+                    'table' => $this->_oConfig->CNF['TABLE_MODULES'],
+                    'key' => 'id',
+                ),
+            ),
+            'inputs' => array(
+                'module_title' => array(
+                    'type' => 'text',
+                    'name' => 'module_title',
+                    'caption' => 'Module title', // TODO: translate
+                    'checker' => array(
+                        'func' => 'Avail',
+                        'error' => 'Something must be entered', // TODO: translate
+                    ),
+                    'db' => array('pass' => 'Xss'),
+                ),
+                'submit' => array(
+                    'type' => 'input_set',
+                    0 => array (
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_Submit'),
+                    ),
+                    1 => array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_Cancel'),
+                        'attrs' => array(
+                            'class' => 'bx-def-margin-sec-left',
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide();",
+                        ),
+                    ),
+                ),
+            ),
+        );
+        $oForm = new BxTemplFormView($aForm);
+        $oForm->initChecker();
+        if ($oForm->isSubmittedAndValid()) {
+            $iModuleId = $oForm->insert(array(
+                'profile_id' => $iProfileId,
+                'author' => bx_get_logged_profile_id(),
+                'added' => time(),
+                'changed' => time(),
+                'order' => 123, // TODO: calc max order
+            ));
+            if ($iModuleId) {
+                echoJson(array('action' => 'ReloadLessonsAndClosePopup'));
+            }
+            else {
+                echoJson(array('action' => 'ShowMsg', 'msg' => _t('_sys_txt_error_occured')));
+            }
+        } 
+        else {
+            echo $this->_oTemplate->parseHtmlByName('classes_add_module_form.html', array(
+                'form' => $oForm->getCode(),
+                'profile_id' => $iProfileId,
+            ));
+        }
+    }
+
     /**
      * Entry post for Timeline module
      */
@@ -65,6 +187,61 @@ class BxClssModule extends BxBaseModTextModule
 
         return parent::serviceCheckAllowedCommentsView($iContentId, $sObjectComments);
     }
+
+    public function serviceClassesInContext ($iProfileId = 0)
+    {
+        if (!$iProfileId)
+            $iProfileId = (int)bx_get('id') ? bx_get('id') : bx_get('profile_id');
+
+        $aInputs = array();
+        $aModules = $this->_oDb->getEntriesModulesByContext($iProfileId);
+        foreach ($aModules as $aModule) {
+            $aInputs['module' . $aModule['id']] = array(
+                'type' => 'block_header',
+                'caption' => $aModule['module_title'],
+                'collapsed' => false,
+                'attrs' => array('id' => 'module_' . $aModule['id'], 'class' => ''),
+            );
+            $aClasses = $this->_oDb->getEntriesByModule($aModule['id']);
+            $sContent = '';
+            if ($aClasses) {
+                foreach ($aClasses as $aClass) {
+                    $sContent .= $this->_oTemplate->parseHtmlByName('classes_class_row.html', array(
+                        'id' => $aClass['id'],
+                        'title' => $aClass['title'],
+                        'url' => BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $this->_oConfig->CNF['URI_VIEW_ENTRY'] . '&id=' . $aClass['id']),
+                    ));
+                }
+            }
+
+            $aInputs['class_module' . $aModule['id']] = array(
+                'type' => 'custom',
+                'name' => 'class_module' . $aModule['id'],
+                'caption' => '',
+                'content' => $sContent,
+            );
+
+        }
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'bx-classes-list-view',
+            ),
+            'inputs' => $aInputs,
+        );
+        $oForm = new BxTemplFormView($aForm);
+        $oForm->setShowEmptySections(true);
+
+        $this->_oTemplate->addCss('main.css');
+        $this->_oTemplate->addJs('main.js');
+
+        return $this->_oTemplate->parseHtmlByName('classes_in_context.html', array(
+            'profile_id' => $iProfileId,
+            'new_class_url' => BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=create-class&profile_id=' . $iProfileId),
+            'form' => $oForm->getCode(),
+        ));
+    }
+
 }
 
 /** @} */

+ 2 - 0
modules/boonex/classes/install/langs/en.xml

@@ -61,6 +61,7 @@
     <string name="_bx_classes_form_entry_input_sys_attachments"><![CDATA[Attachments]]></string>
     <string name="_bx_classes_form_entry_input_cmts"><![CDATA[Comments settings]]></string>
     <string name="_bx_classes_form_entry_input_avail"><![CDATA[Availability]]></string>
+    <string name="_bx_classes_form_entry_input_module"><![CDATA[Module]]></string>
 
     <string name="_bx_classes_form_poll"><![CDATA[Polls]]></string>
     <string name="_bx_classes_form_poll_display_add"><![CDATA[Add Poll]]></string>
@@ -225,6 +226,7 @@
     <string name="_bx_classes_txt_scheduled_awaiting"><![CDATA[Class publishing date is {date_publish_uf}]]></string>
 
     <string name="_bx_classes_txt_err_cannot_perform_action"><![CDATA[Cannot perform action. Please report.]]></string>
+    <string name="_bx_classes_txt_err_cant_add_class_without_context"><![CDATA[Class can be added into context only]]></string>
 
     <string name="_bx_classes_txt_poll_view_answers"><![CDATA[poll]]></string>
     <string name="_bx_classes_txt_poll_view_results"><![CDATA[results]]></string>

+ 4 - 0
modules/boonex/classes/install/sql/enable.sql

@@ -98,6 +98,10 @@ INSERT INTO `sys_objects_page`(`object`, `title_system`, `title`, `module`, `lay
 INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
 ('bx_classes_administration', 1, 'bx_classes', '_bx_classes_page_block_title_system_manage_administration', '_bx_classes_page_block_title_manage', 11, 192, 'service', 'a:3:{s:6:\"module\";s:10:\"bx_classes\";s:6:\"method\";s:12:\"manage_tools\";s:6:\"params\";a:1:{i:0;s:14:\"administration\";}}', 0, 1, 0);
 
+-- PAGES: add page block to profiles modules (trigger* page objects are processed separately upon modules enable/disable)
+SET @iPBCellGroup = 4;
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('trigger_page_group_view_entry', @iPBCellGroup, 'bx_classes', '_bx_classes_page_block_title_group_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_classes";s:6:"method";s:18:"classes_in_context";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 0);
 
 -- MENU: add to "add content" menu
 

+ 50 - 32
modules/boonex/classes/install/sql/install.sql

@@ -3,8 +3,10 @@ CREATE TABLE IF NOT EXISTS `bx_classes_classes` (
   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
   `author` int(11) NOT NULL,
   `added` int(11) NOT NULL,
-  `changed` int(11) NOT NULL,
+  `changed` int(11) NOT NULL,  
   `published` int(11) NOT NULL,
+  `module_id` int(10) unsigned NOT NULL,
+  `order` int(11) NOT NULL,
   `start_date` int(11) NOT NULL,
   `end_date` int(11) NOT NULL,
   `thumb` int(11) NOT NULL,
@@ -33,6 +35,18 @@ CREATE TABLE IF NOT EXISTS `bx_classes_classes` (
   FULLTEXT KEY `title_text` (`title`,`text`)
 );
 
+-- TABLE: modules
+CREATE TABLE IF NOT EXISTS `bx_classes_modules` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `module_title` varchar(255) NOT NULL,
+  `author` int(11) NOT NULL,
+  `added` int(11) NOT NULL,
+  `changed` int(11) NOT NULL,
+  `order` int(11) NOT NULL,
+  PRIMARY KEY (`id`)
+);
+
 -- TABLE: storages & transcoders
 CREATE TABLE IF NOT EXISTS `bx_classes_covers` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
@@ -404,6 +418,7 @@ INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `ch
 ('bx_classes', 'bx_classes', 'title', '', '', 0, 'text', '_bx_classes_form_entry_input_sys_title', '_bx_classes_form_entry_input_title', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_classes_form_entry_input_title_err', 'Xss', '', 1, 0),
 ('bx_classes', 'bx_classes', 'avail', '', '#!bx_classes_avail', 0, 'select', '', '_bx_classes_form_entry_input_avail', '', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
 ('bx_classes', 'bx_classes', 'cmts', '', '#!bx_classes_cmts', 0, 'select', '', '_bx_classes_form_entry_input_cmts', '', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_classes', 'bx_classes', 'module_id', '', '', 0, 'select', '', '_bx_classes_form_entry_input_module', '', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
 ('bx_classes', 'bx_classes', 'added', '', '', 0, 'datetime', '_bx_classes_form_entry_input_sys_date_added', '_bx_classes_form_entry_input_date_added', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('bx_classes', 'bx_classes', 'changed', '', '', 0, 'datetime', '_bx_classes_form_entry_input_sys_date_changed', '_bx_classes_form_entry_input_date_changed', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('bx_classes', 'bx_classes', 'start_date', '', '', 0, 'datetime', '', '_bx_classes_form_entry_input_date_start_date', '', 0, 0, 0, '', '', '', '', '', '', 'DateTimeTs', '', 1, 0),
@@ -416,42 +431,45 @@ INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `ch
 
 INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES 
 ('bx_classes_entry_add', 'covers', 2147483647, 1, 1),
-('bx_classes_entry_add', 'title', 2147483647, 1, 2),
-('bx_classes_entry_add', 'text', 2147483647, 1, 3),
-('bx_classes_entry_add', 'attachments', 2147483647, 1, 4),
-('bx_classes_entry_add', 'pictures', 2147483647, 1, 5),
-('bx_classes_entry_add', 'videos', 2147483647, 1, 6),
-('bx_classes_entry_add', 'files', 2147483647, 1, 7),
-('bx_classes_entry_add', 'polls', 2147483647, 1, 8),
-('bx_classes_entry_add', 'start_date', 2147483647, 1, 9),
-('bx_classes_entry_add', 'end_date', 2147483647, 1, 10),
-('bx_classes_entry_add', 'avail', 2147483647, 1, 11),
-('bx_classes_entry_add', 'cmts', 2147483647, 1, 12),
-('bx_classes_entry_add', 'allow_view_to', 2147483647, 1, 13),
-('bx_classes_entry_add', 'do_publish', 2147483647, 1, 14),
+('bx_classes_entry_add', 'module_id', 2147483647, 1, 2),
+('bx_classes_entry_add', 'title', 2147483647, 1, 3),
+('bx_classes_entry_add', 'text', 2147483647, 1, 4),
+('bx_classes_entry_add', 'attachments', 2147483647, 1, 5),
+('bx_classes_entry_add', 'pictures', 2147483647, 1, 6),
+('bx_classes_entry_add', 'videos', 2147483647, 1, 7),
+('bx_classes_entry_add', 'files', 2147483647, 1, 8),
+('bx_classes_entry_add', 'polls', 2147483647, 1, 9),
+('bx_classes_entry_add', 'start_date', 2147483647, 1, 10),
+('bx_classes_entry_add', 'end_date', 2147483647, 1, 11),
+('bx_classes_entry_add', 'avail', 2147483647, 1, 12),
+('bx_classes_entry_add', 'cmts', 2147483647, 1, 13),
+('bx_classes_entry_add', 'allow_view_to', 2147483647, 1, 14),
+('bx_classes_entry_add', 'do_publish', 2147483647, 1, 15),
 
 ('bx_classes_entry_delete', 'delete_confirm', 2147483647, 1, 1),
 ('bx_classes_entry_delete', 'do_submit', 2147483647, 1, 2),
 
 ('bx_classes_entry_edit', 'covers', 2147483647, 1, 1),
-('bx_classes_entry_edit', 'title', 2147483647, 1, 2),
-('bx_classes_entry_edit', 'text', 2147483647, 1, 3),
-('bx_classes_entry_edit', 'attachments', 2147483647, 1, 4),
-('bx_classes_entry_edit', 'pictures', 2147483647, 1, 5),
-('bx_classes_entry_edit', 'videos', 2147483647, 1, 6),
-('bx_classes_entry_edit', 'files', 2147483647, 1, 7),
-('bx_classes_entry_edit', 'polls', 2147483647, 1, 8),
-('bx_classes_entry_edit', 'start_date', 2147483647, 1, 9),
-('bx_classes_entry_edit', 'end_date', 2147483647, 1, 10),
-('bx_classes_entry_edit', 'avail', 2147483647, 1, 11),
-('bx_classes_entry_edit', 'cmts', 2147483647, 1, 12),
-('bx_classes_entry_edit', 'allow_view_to', 2147483647, 1, 13),
-('bx_classes_entry_edit', 'do_submit', 2147483647, 1, 14),
-
-('bx_classes_entry_view', 'added', 2147483647, 1, 1),
-('bx_classes_entry_view', 'changed', 2147483647, 1, 2),
-('bx_classes_entry_view', 'start_date', 2147483647, 1, 3),
-('bx_classes_entry_view', 'end_date', 2147483647, 1, 4);
+('bx_classes_entry_edit', 'module_id', 2147483647, 1, 2),
+('bx_classes_entry_edit', 'title', 2147483647, 1, 3),
+('bx_classes_entry_edit', 'text', 2147483647, 1, 4),
+('bx_classes_entry_edit', 'attachments', 2147483647, 1, 5),
+('bx_classes_entry_edit', 'pictures', 2147483647, 1, 6),
+('bx_classes_entry_edit', 'videos', 2147483647, 1, 7),
+('bx_classes_entry_edit', 'files', 2147483647, 1, 8),
+('bx_classes_entry_edit', 'polls', 2147483647, 1, 9),
+('bx_classes_entry_edit', 'start_date', 2147483647, 1, 10),
+('bx_classes_entry_edit', 'end_date', 2147483647, 1, 11),
+('bx_classes_entry_edit', 'avail', 2147483647, 1, 12),
+('bx_classes_entry_edit', 'cmts', 2147483647, 1, 13),
+('bx_classes_entry_edit', 'allow_view_to', 2147483647, 1, 14),
+('bx_classes_entry_edit', 'do_submit', 2147483647, 1, 15),
+
+('bx_classes_entry_view', 'module_id', 2147483647, 1, 1),
+('bx_classes_entry_view', 'added', 2147483647, 1, 2),
+('bx_classes_entry_view', 'changed', 2147483647, 1, 3),
+('bx_classes_entry_view', 'start_date', 2147483647, 1, 4),
+('bx_classes_entry_view', 'end_date', 2147483647, 1, 5);
 
 -- FORMS: poll
 INSERT INTO `sys_objects_form` (`object`, `module`, `title`, `action`, `form_attrs`, `submit_name`, `table`, `key`, `uri`, `uri_title`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES

+ 1 - 1
modules/boonex/classes/install/sql/uninstall.sql

@@ -1,6 +1,6 @@
 
 -- TABLES
-DROP TABLE IF EXISTS `bx_classes_classes`, `bx_classes_covers`, `bx_classes_files`, `bx_classes_photos`, `bx_classes_photos_resized`, `bx_classes_videos`, `bx_classes_videos_resized`, `bx_classes_cmts`, `bx_classes_cmts_notes`, `bx_classes_votes`, `bx_classes_votes_track`, `bx_classes_reactions`, `bx_classes_reactions_track`, `bx_classes_views_track`, `bx_classes_meta_keywords`, `bx_classes_meta_locations`, `bx_classes_meta_mentions`, `bx_classes_reports`, `bx_classes_reports_track`, `bx_classes_favorites_track`, `bx_classes_scores`, `bx_classes_scores_track`, `bx_classes_polls`, `bx_classes_polls_answers`, `bx_classes_polls_answers_votes`, `bx_classes_polls_answers_votes_track`;
+DROP TABLE IF EXISTS `bx_classes_classes`, `bx_classes_modules`, `bx_classes_covers`, `bx_classes_files`, `bx_classes_photos`, `bx_classes_photos_resized`, `bx_classes_videos`, `bx_classes_videos_resized`, `bx_classes_cmts`, `bx_classes_cmts_notes`, `bx_classes_votes`, `bx_classes_votes_track`, `bx_classes_reactions`, `bx_classes_reactions_track`, `bx_classes_views_track`, `bx_classes_meta_keywords`, `bx_classes_meta_locations`, `bx_classes_meta_mentions`, `bx_classes_reports`, `bx_classes_reports_track`, `bx_classes_favorites_track`, `bx_classes_scores`, `bx_classes_scores_track`, `bx_classes_polls`, `bx_classes_polls_answers`, `bx_classes_polls_answers_votes`, `bx_classes_polls_answers_votes_track`;
 
 -- STORAGES & TRANSCODERS
 DELETE FROM `sys_objects_storage` WHERE `object` LIKE 'bx_classes_%';

+ 102 - 0
modules/boonex/classes/js/main.js

@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Classes Classes
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function bx_classes_show_module_add_form(iProfileId)
+{
+    var sActionUrl = sUrlRoot + 'modules/index.php?r=classes/add_module/' + iProfileId;
+    $(window).dolPopupAjax({url: sActionUrl, closeOnOuterClick: false});
+}
+
+function bx_classes_reorder_classes(iProfileId, bEnable)
+{
+    var eCourse = jQuery('.bx-course-classes .bx-form-advanced-wrapper');
+    var eSortable = jQuery('.bx-course-classes .bx-form-input-wrapper-custom');
+    var sHandle = '<i class="handle sys-icon move bx-def-padding-right"></i>';
+    if (bEnable) {
+        jQuery('.bx-course-classes, .bx-course-controls').addClass('bx-course-reordering-mode');
+        if (!eCourse.find('.bx-classes-class-title .handle').size())
+            eSortable.find('.bx-classes-class-title').prepend(sHandle);
+        if (eSortable.hasClass('ui-sortable-disabled')) {
+            eSortable.sortable('enable');
+        }
+        else if (!eSortable.hasClass('ui-sortable')) {
+            eSortable.sortable({
+                items:'.bx-classes-class',
+                connectWith: ".bx-form-input-wrapper-custom",
+                handle: '.handle',
+                placeholder: 'bx-classes-placeholder sys-icon arrow-right',
+                forcePlaceholderSize: true, 
+                dropOnEmpty: true,
+                stop: function(oEvent, oUi) {
+                    var a = eCourse.find('.bx-form-section-wrapper > .bx-form-section'); 
+                    var s = '';
+                    var i = 0;
+                    $.each(a, function () {
+                        var aa = $(this).find('.bx-classes-class');
+                        var iModuleId = $(this).attr('id').replace('module_', '');
+                        s += '&classes_order_' +iModuleId + '=';
+                        $.each(aa, function () {
+                            s += $(this).attr('id').replace('class_', '') + ',';
+                        });
+                        s = s.replace(/,$/, '');
+                    });
+                    var sActionUrl = sUrlRoot + 'modules/index.php?r=classes/reorder_classes/' + iProfileId + s;
+                    $.post(sActionUrl);
+                }
+
+            });
+        }
+        glBxClassesSortableClassesInitialized = true;
+    }
+    else if (!bEnable && 'undefined' !== typeof(glBxClassesSortableClassesInitialized)) {
+        eSortable.sortable('disable');
+        eSortable.find('.bx-classes-class .handle').remove();
+        jQuery('.bx-course-classes, .bx-course-controls').removeClass('bx-course-reordering-mode');
+    }
+}
+
+function bx_classes_reorder_modules(iProfileId, bEnable)
+{
+    var eCourse = jQuery('.bx-course-classes .bx-form-advanced-wrapper');
+    var sHandle = '<i class="handle sys-icon move bx-def-padding-leftright"></i>';
+    if (bEnable) {
+        jQuery('.bx-course-classes, .bx-course-controls').addClass('bx-course-reordering-mode');
+        if (!eCourse.find('legend .handle').size())
+            eCourse.find('legend').prepend(sHandle);
+        if (eCourse.hasClass('ui-sortable-disabled')) {
+            eCourse.sortable('enable');
+        }
+        else if (!eCourse.hasClass('ui-sortable')) {
+            eCourse.sortable({
+                items:'.bx-form-section-wrapper',
+                handle: '.handle',
+                stop: function(oEvent, oUi) {
+                    var a = eCourse.find('.bx-form-section-wrapper > .bx-form-section'); 
+                    var s = '';
+                    var i = 0;
+                    $.each(a, function () {                        
+                        s += '&modules_order[]=' + $(this).attr('id').replace('module_', '');
+                    });
+                    var sActionUrl = sUrlRoot + 'modules/index.php?r=classes/reorder_modules/' + iProfileId + s;
+                    $.post(sActionUrl);
+                }
+
+            });
+        }
+        glBxClassesSortableModulesInitialized = true;
+    }
+    else if (!bEnable && 'undefined' !== typeof(glBxClassesSortableModulesInitialized)) {
+        eCourse.sortable('disable');
+        eCourse.find('legend .handle').remove();
+        jQuery('.bx-course-classes, .bx-course-controls').removeClass('bx-course-reordering-mode');
+    }
+}
+
+/** @} */

+ 31 - 0
modules/boonex/classes/template/classes_add_module_form.html

@@ -0,0 +1,31 @@
+<div id="bx-classes-add-module-form" class="bx-classes-add-page-form">
+    __form__
+</div>
+<script>
+    $(document).ready(function () {
+        $("#bx-classes-add-module-form form").ajaxForm({
+            url: sUrlRoot + 'modules/index.php?r=classes/add_module/__profile_id__',
+            beforeSubmit: function (formData, $form, options) {
+                bx_loading($("#bx-classes-add-module-form form"), true);
+            },
+            success: function (data) {
+                if ('{' == data[0]) {
+                    var oResponce = JSON.parse(data);
+                    if ('ReloadLessonsAndClosePopup' == oResponce.action) {
+                        $('.bx-popup-applied:visible').dolPopupHide();
+                        loadDynamicBlockAuto('.bx-course-controls');
+                    } 
+                    else if ('ShowMsg' == oResponce.action) {
+                        bx_alert(oResponce.msg);
+                    }
+                    else {
+                        bx_alert('<bx_text_js:_sys_txt_error_occured />');
+                    }
+                }
+                else {
+                    $("#bx-classes-add-module-form").parent().html(data).bxProcessHtml();
+                }
+            }
+        });
+    });
+</script>

+ 3 - 0
modules/boonex/classes/template/classes_class_row.html

@@ -0,0 +1,3 @@
+<div id="class___id__" class="bx-classes-class bx-def-margin-thd-topbottom">
+    <div class="bx-classes-class-title"><a href="__url__">__title__</a></div>
+</div>

+ 14 - 0
modules/boonex/classes/template/classes_in_context.html

@@ -0,0 +1,14 @@
+<div class="bx-course-controls bx-def-margin-bottom bx-clearfix">
+    <div class="bx-course-controls-1 bx-clearfix">
+        <a class="bx-btn bx-def-margin-right" href="__new_class_url__">Add Class</a>
+        <a class="bx-btn" href="javascript:void(0);" onclick="bx_classes_show_module_add_form('__profile_id__')">Add Module</a>
+    </div>
+    <div class="bx-course-controls-2 bx-clearfix">
+        <a class="bx-btn" href="javascript:void(0);" onclick="bx_classes_reorder_modules('__profile_id__', true)">Rearrange Moodules</a>
+        <a class="bx-btn bx-def-margin-left" href="javascript:void(0);" onclick="bx_classes_reorder_classes('__profile_id__', true)">Rearrange Classes</a>
+        <a class="bx-btn bx-def-margin-left bx-classes-btn-done" href="javascript:void(0);" onclick="bx_classes_reorder_classes('__profile_id__', false); bx_classes_reorder_modules('__profile_id__', false);">Done</a>
+    </div>
+</div>
+<div class="bx-course-classes">
+    __form__
+</div>

+ 22 - 0
modules/boonex/classes/template/css/main.css

@@ -1 +1,23 @@
 @import url(../../../../base/text/template/css/main.css);
+
+.bx-course-controls-1 {
+    float:left;
+}
+
+.bx-course-controls-2 {
+    float:right;
+}
+
+.bx-course-classes .bx-form-input-wrapper-custom {
+    min-height: 1.2rem;
+}
+
+.bx-classes-btn-done {
+    display:none;
+}
+.bx-course-reordering-mode .bx-classes-btn-done {
+    display:block;
+}
+.bx-course-reordering-mode .bx-btn:not(.bx-classes-btn-done) {
+    display:none;
+}

+ 3 - 1
modules/boonex/posts/install/sql/enable.sql

@@ -167,8 +167,10 @@ INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbo
 
 -- PAGES: add page block to profiles modules (trigger* page objects are processed separately upon modules enable/disable)
 SET @iPBCellProfile = 3;
+SET @iPBCellGroup = 4;
 INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
-('trigger_page_profile_view_entry', @iPBCellProfile, 'bx_posts', '_bx_posts_page_block_title_my_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:8:"bx_posts";s:6:"method";s:13:"browse_author";s:6:"params";a:2:{i:0;s:12:"{profile_id}";i:1;a:2:{s:8:"per_page";s:25:"bx_posts_per_page_profile";s:13:"empty_message";b:0;}}}', 0, 0, 0);
+('trigger_page_profile_view_entry', @iPBCellProfile, 'bx_posts', '_bx_posts_page_block_title_my_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:8:"bx_posts";s:6:"method";s:13:"browse_author";s:6:"params";a:2:{i:0;s:12:"{profile_id}";i:1;a:2:{s:8:"per_page";s:25:"bx_posts_per_page_profile";s:13:"empty_message";b:0;}}}', 0, 0, 0),
+('trigger_page_group_view_entry', @iPBCellGroup, 'bx_classes', '_bx_files_page_block_title_group_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:8:"bx_files";s:6:"method";s:19:"browse_group_author";s:6:"params";a:2:{i:0;s:12:"{profile_id}";i:1;a:2:{s:8:"per_page";s:25:"bx_files_per_page_profile";s:13:"empty_message";b:0;}}}', 0, 0, 0);
 
 -- PAGE: service blocks
 

+ 11 - 1
template/scripts/BxBaseFormView.php

@@ -70,6 +70,11 @@ class BxBaseFormView extends BxDolForm
      */
     protected $_bViewMode = false;
 
+    /**
+     * Show or not sections which have no fileds
+     */
+    protected $_bShowEmptySections = false;
+
     /**
      * Function name for generation close form section HTML.
      */
@@ -254,6 +259,11 @@ class BxBaseFormView extends BxDolForm
         ));
     }
 
+    function setShowEmptySections($b)
+    {
+        $this->_bShowEmptySections = $b;
+    }
+
     function setAjaxMode($bAjaxMode)
     {
         $this->_bAjaxMode = (bool)$bAjaxMode;
@@ -450,7 +460,7 @@ BLAH;
         $sFuncGenRow = isset($this->aParams['view_mode']) && $this->aParams['view_mode'] ? 'genViewRow' : 'genRow';
         foreach ($this->aInputs as $aInput) {
             if (!isset($aInput['visible_for_levels']) || self::isVisible($aInput)) {
-                if ('block_header' == $aInput['type']) {
+                if ('block_header' == $aInput['type'] && !$this->_bShowEmptySections) {
                     // don't show section with no fields or with empty fields
                     if ($sContHeader) {                        
                         if ($sContFields)