浏览代码

Merge branch 'master' of https://github.com/unacms/una

AntonLV 2 年之前
父节点
当前提交
4cc2ba9f85
共有 100 个文件被更改,包括 16867 次插入 和 0 次删除
  1. 2 0
      inc/classes/BxDolLocationFieldNominatim.php
  2. 51 0
      modules/boonex/accounts/updates/14.0.2_14.0.3/install/config.php
  3. 13 0
      modules/boonex/accounts/updates/14.0.2_14.0.3/install/installer.php
  4. 118 0
      modules/boonex/accounts/updates/14.0.2_14.0.3/source/classes/BxAccntTemplate.php
  5. 71 0
      modules/boonex/accounts/updates/14.0.2_14.0.3/source/install/config.php
  6. 19 0
      modules/boonex/accounts/updates/14.0.2_14.0.3/source/template/create.html
  7. 51 0
      modules/boonex/contact/updates/13.0.2_14.0.0/install/config.php
  8. 13 0
      modules/boonex/contact/updates/13.0.2_14.0.0/install/installer.php
  9. 71 0
      modules/boonex/contact/updates/13.0.2_14.0.0/source/install/config.php
  10. 22 0
      modules/boonex/contact/updates/13.0.2_14.0.0/source/template/images/icons/std-icon.svg
  11. 51 0
      modules/boonex/courses/updates/14.0.2_14.0.3/install/config.php
  12. 13 0
      modules/boonex/courses/updates/14.0.2_14.0.3/install/installer.php
  13. 88 0
      modules/boonex/courses/updates/14.0.2_14.0.3/install/langs/en.xml
  14. 101 0
      modules/boonex/courses/updates/14.0.2_14.0.3/install/sql/enable.sql
  15. 93 0
      modules/boonex/courses/updates/14.0.2_14.0.3/install/sql/install.sql
  16. 378 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesConfig.php
  17. 461 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesDb.php
  18. 66 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesFormContentNode.php
  19. 64 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesFormsEntryHelper.php
  20. 239 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesGridCntDataManage.php
  21. 420 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesGridCntStructureManage.php
  22. 78 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesMenuContentAdd.php
  23. 102 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesMenuSnippetMeta.php
  24. 680 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesModule.php
  25. 34 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesPageEntry.php
  26. 677 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesTemplate.php
  27. 152 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/install/config.php
  28. 541 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/install/langs/en.xml
  29. 60 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/install/sql/disable.sql
  30. 740 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/install/sql/enable.sql
  31. 590 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/install/sql/install.sql
  32. 64 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/install/sql/uninstall.sql
  33. 66 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/js/entry.js
  34. 20 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/js/main.js
  35. 18 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/counters.html
  36. 24 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml1_node_l1.html
  37. 3 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml1_nodes_l1.html
  38. 1 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml2_node_l2.html
  39. 1 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml2_nodes_l2.html
  40. 1 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml3_node_l3.html
  41. 1 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml3_nodes_l3.html
  42. 18 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/node_data_view.html
  43. 29 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/node_l1.html
  44. 39 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/node_view.html
  45. 4 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/nodes_l1.html
  46. 19 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/popup_content_node.html
  47. 56 0
      modules/boonex/courses/updates/14.0.2_14.0.3/source/template/unit_personal.html
  48. 51 0
      modules/boonex/credits/updates/14.0.3_14.0.4/install/config.php
  49. 13 0
      modules/boonex/credits/updates/14.0.3_14.0.4/install/installer.php
  50. 7 0
      modules/boonex/credits/updates/14.0.3_14.0.4/install/langs/en.xml
  51. 2 0
      modules/boonex/credits/updates/14.0.3_14.0.4/install/sql/install.sql
  52. 338 0
      modules/boonex/credits/updates/14.0.3_14.0.4/source/classes/BxCreditsConfig.php
  53. 157 0
      modules/boonex/credits/updates/14.0.3_14.0.4/source/classes/BxCreditsFormCredit.php
  54. 105 0
      modules/boonex/credits/updates/14.0.3_14.0.4/source/classes/BxCreditsGridHistoryAdministration.php
  55. 82 0
      modules/boonex/credits/updates/14.0.3_14.0.4/source/classes/BxCreditsGridWithdrawalsCommon.php
  56. 77 0
      modules/boonex/credits/updates/14.0.3_14.0.4/source/install/config.php
  57. 343 0
      modules/boonex/credits/updates/14.0.3_14.0.4/source/install/langs/en.xml
  58. 213 0
      modules/boonex/credits/updates/14.0.3_14.0.4/source/install/sql/install.sql
  59. 35 0
      modules/boonex/credits/updates/14.0.3_14.0.4/source/template/css/main.css
  60. 51 0
      modules/boonex/events/updates/14.0.5_14.0.6/install/config.php
  61. 13 0
      modules/boonex/events/updates/14.0.5_14.0.6/install/installer.php
  62. 4 0
      modules/boonex/events/updates/14.0.5_14.0.6/install/langs/en.xml
  63. 9 0
      modules/boonex/events/updates/14.0.5_14.0.6/install/sql/enable.sql
  64. 339 0
      modules/boonex/events/updates/14.0.5_14.0.6/source/classes/BxEventsConfig.php
  65. 44 0
      modules/boonex/events/updates/14.0.5_14.0.6/source/classes/BxEventsCronPruning.php
  66. 315 0
      modules/boonex/events/updates/14.0.5_14.0.6/source/classes/BxEventsDb.php
  67. 152 0
      modules/boonex/events/updates/14.0.5_14.0.6/source/install/config.php
  68. 693 0
      modules/boonex/events/updates/14.0.5_14.0.6/source/install/langs/en.xml
  69. 797 0
      modules/boonex/events/updates/14.0.5_14.0.6/source/install/sql/enable.sql
  70. 51 0
      modules/boonex/files/updates/14.0.2_14.0.3/install/config.php
  71. 13 0
      modules/boonex/files/updates/14.0.2_14.0.3/install/installer.php
  72. 220 0
      modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesConfig.php
  73. 39 0
      modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesFormsEntryHelper.php
  74. 647 0
      modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesModule.php
  75. 546 0
      modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesSearchResult.php
  76. 231 0
      modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesTemplate.php
  77. 142 0
      modules/boonex/files/updates/14.0.2_14.0.3/source/install/config.php
  78. 51 0
      modules/boonex/groups/updates/14.0.3_14.0.4/install/config.php
  79. 13 0
      modules/boonex/groups/updates/14.0.3_14.0.4/install/installer.php
  80. 152 0
      modules/boonex/groups/updates/14.0.3_14.0.4/source/install/config.php
  81. 20 0
      modules/boonex/groups/updates/14.0.3_14.0.4/source/js/main.js
  82. 51 0
      modules/boonex/invites/updates/14.0.3_14.0.4/install/config.php
  83. 13 0
      modules/boonex/invites/updates/14.0.3_14.0.4/install/installer.php
  84. 302 0
      modules/boonex/invites/updates/14.0.3_14.0.4/source/classes/BxInvTemplate.php
  85. 80 0
      modules/boonex/invites/updates/14.0.3_14.0.4/source/install/config.php
  86. 51 0
      modules/boonex/lucid/updates/14.0.0_14.0.1/install/config.php
  87. 26 0
      modules/boonex/lucid/updates/14.0.0_14.0.1/install/installer.php
  88. 630 0
      modules/boonex/lucid/updates/14.0.0_14.0.1/source/data/template/system/css/menu.less
  89. 68 0
      modules/boonex/lucid/updates/14.0.0_14.0.1/source/install/config.php
  90. 51 0
      modules/boonex/market/updates/14.0.4_14.0.5/install/config.php
  91. 13 0
      modules/boonex/market/updates/14.0.4_14.0.5/install/installer.php
  92. 1457 0
      modules/boonex/market/updates/14.0.4_14.0.5/source/classes/BxMarketModule.php
  93. 532 0
      modules/boonex/market/updates/14.0.4_14.0.5/source/classes/BxMarketTemplate.php
  94. 140 0
      modules/boonex/market/updates/14.0.4_14.0.5/source/install/config.php
  95. 51 0
      modules/boonex/notifications/updates/14.0.4_14.0.5/install/config.php
  96. 13 0
      modules/boonex/notifications/updates/14.0.4_14.0.5/install/installer.php
  97. 855 0
      modules/boonex/notifications/updates/14.0.4_14.0.5/source/classes/BxNtfsModule.php
  98. 82 0
      modules/boonex/notifications/updates/14.0.4_14.0.5/source/install/config.php
  99. 51 0
      modules/boonex/organizations/updates/14.0.5_14.0.6/install/config.php
  100. 13 0
      modules/boonex/organizations/updates/14.0.5_14.0.6/install/installer.php

+ 2 - 0
inc/classes/BxDolLocationFieldNominatim.php

@@ -84,6 +84,7 @@ class BxDolLocationFieldNominatim extends BxDolLocationField
                 $sLocationString = $aVars['lat'] . ', ' . $aVars['lng'];
             }
 
+            $aVars['form_id'] = $oForm->id;
             $aVars['name'] = $aInput['name'];
             $aVars['input'] = $oForm->genInputSwitcher($aInputField);
             $aVars['id_status'] = $oForm->getInputId($aInput) . '_status';
@@ -133,6 +134,7 @@ class BxDolLocationFieldNominatim extends BxDolLocationField
                 }
             }
             $sRet = $oForm->getTemplate()->parseHtmlByName('location_field_plain.html', array(
+                'form_id' => $oForm->id,
                 'name' => $aInput['name'],
                 'minimized' => $bMinimized ? 'minimized' : '',
                 'nominatim_server' => $this->getNominatimServer(),

+ 51 - 0
modules/boonex/accounts/updates/14.0.2_14.0.3/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Accounts Manager',
+    'version_from' => '14.0.2',
+    'version_to' => '14.0.3',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/accounts/updates/update_14.0.2_14.0.3/',
+    'home_uri' => 'accounts_update_1402_1403',
+
+    'module_dir' => 'boonex/accounts/',
+    'module_uri' => 'accounts',
+
+    'db_prefix' => 'bx_accnt_',
+    'class_prefix' => 'BxAccnt',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Accounts',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/accounts/updates/14.0.2_14.0.3/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxAccntUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 118 - 0
modules/boonex/accounts/updates/14.0.2_14.0.3/source/classes/BxAccntTemplate.php

@@ -0,0 +1,118 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAccntTemplate extends BxBaseModGeneralTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+    	$this->MODULE = 'bx_accounts';
+        parent::__construct($oConfig, $oDb);
+    }
+
+    public function getProfilesByAccount($aContentInfo, $iMaxVisible = 2, $iMaxInPopup = 20)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $iMax = $iMaxVisible + $iMaxInPopup;
+
+        $aProfiles = BxDolAccount::getInstance($aContentInfo['id'])->getProfiles();
+        $iProfiles = count($aProfiles);
+
+        $aTmplVars = [
+            'class_cnt' => '',
+            'bx_repeat:profiles' => [],
+            'bx_if:profiles_more' => [
+                'condition' => $iProfiles > $iMaxVisible,
+                'content' => [
+                    'html_id' => $this->_oConfig->getHtmlIds('profile_more_popup') . $aContentInfo['id'],
+                    'more' => _t('_bx_accnt_txt_more', $iProfiles - $iMaxVisible),
+                    'more_attr' => bx_html_attribute(_t('_bx_accnt_txt_see_more')),
+                    'popup' => '',
+                ],
+            ],
+        ];
+
+        $aTmplVarsPopup = [
+            'bx_repeat:profiles' => [],
+        ];
+
+        if($iProfiles > $iMax)
+            $aProfiles = array_slice($aProfiles, 0, $iMax);
+
+        $i = 0;
+        foreach ($aProfiles as $aProfile) {
+            $oProfile = BxDolProfile::getInstance((int)$aProfile['id']);
+            if(!$oProfile)
+                continue;
+
+            $iId = $oProfile->id();
+            $sName = $oProfile->getDisplayName();
+            $aTmplVarsProfile = [
+                'html_id' => $this->_oConfig->getHtmlIds('profile') . $iId,
+                'id' => $iId,
+                'url' => $oProfile->getUrl(),
+                'name' => strmaxtextlen($sName, $CNF['PARAM_PROFILE_NAME_LENGTH_MAX'], '...'),
+                'name_attr' => bx_html_attribute($sName)
+            ];
+
+            if($i < $iMaxVisible)
+                $aTmplVars['bx_repeat:profiles'][] = $aTmplVarsProfile;
+            if($i >= $iMaxVisible)
+                $aTmplVarsPopup['bx_repeat:profiles'][] = $aTmplVarsProfile;
+
+            ++$i;
+        }
+
+        if($aTmplVarsPopup['bx_repeat:profiles'])
+            $aTmplVars['bx_if:profiles_more']['content']['popup'] = BxTemplFunctions::getInstance()->transBox('', $this->parseHtmlByName('profiles_more.html', $aTmplVarsPopup));
+
+        return $this->parseHtmlByName('profiles.html', $aTmplVars);
+    }
+
+    public function getPopupSetRole($aRoles, $iAccountId, $iAccountRole)
+    {
+        $sJsObject = $this->_oConfig->getJsObject('manage_tools');
+        $sHtmlIdPrefix = str_replace('_', '-', $this->_oConfig->getName()) . '-role';
+        $sMaskOnClick = '%s.onClickSetOperatorRoleSubmit(this, %d, %d)';
+
+        $aRoles = array_merge(array(array('id' => 0, 'title' => '_bx_accnt_txt_not_operator')), $aRoles);
+
+        $aTmplVarsRoles = array();
+        foreach($aRoles as $aRole) {
+            $iRole = (int)$aRole['id'];
+            $bRole = $iRole != 0;
+
+            if($iRole == BX_DOL_STUDIO_ROLE_MASTER)
+                continue;
+
+            $aTmplVarsRoles[] = array(
+                'id' => $sHtmlIdPrefix . '-' . $iRole, 
+                'type' => !$bRole ? 'radio' : 'checkbox',
+                'value' => $iRole,
+                'onclick' => sprintf($sMaskOnClick, $sJsObject, $iAccountId, $iRole),
+                'title' => _t($aRole['title']), 
+                'bx_if:show_checked' => array(
+                    'condition' => ($bRole && $iAccountRole & (1 << ($iRole - 1))) || (!$bRole && $iAccountRole == 0),
+                    'content' => array()
+                ),
+                'bx_if:show_divider' => array(
+                    'condition' => !$bRole,
+                    'content' => array()
+                )
+            );
+        }
+
+        return $this->parseHtmlByName('set_role_popup.html', array(
+            'bx_repeat:roles' => $aTmplVarsRoles
+        ));
+    }
+}
+
+/** @} */

+ 71 - 0
modules/boonex/accounts/updates/14.0.2_14.0.3/source/install/config.php

@@ -0,0 +1,71 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Accounts Accounts
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_accounts',
+    'title' => 'Accounts Manager',
+    'note' => 'Accounts Manager module.',
+    'version' => '14.0.3',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/accounts/',
+    'home_uri' => 'accounts',
+
+    'db_prefix' => 'bx_accnt_',
+    'class_prefix' => 'BxAccnt',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Accounts',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+);
+
+/** @} */

+ 19 - 0
modules/boonex/accounts/updates/14.0.2_14.0.3/source/template/create.html

@@ -0,0 +1,19 @@
+<div class="bx-accnt-create">
+    <div class="bx-accnt-create-cnt">
+        __form__
+        <script language="javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function (formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function (data) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        glGrids.__object__.processJson(data, '__action__');
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 51 - 0
modules/boonex/contact/updates/13.0.2_14.0.0/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Contact',
+    'version_from' => '13.0.2',
+    'version_to' => '14.0.0',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/contact/updates/update_13.0.2_14.0.0/',
+    'home_uri' => 'contact_update_1302_1400',
+
+    'module_dir' => 'boonex/contact/',
+    'module_uri' => 'contact',
+
+    'db_prefix' => 'bx_contact_',
+    'class_prefix' => 'BxContact',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Contact',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/contact/updates/13.0.2_14.0.0/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxContactUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 71 - 0
modules/boonex/contact/updates/13.0.2_14.0.0/source/install/config.php

@@ -0,0 +1,71 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Contact Contact
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_contact',
+    'title' => 'Contact',
+    'note' => 'Contact module.',
+    'version' => '14.0.0',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/contact/',
+    'home_uri' => 'contact',
+
+    'db_prefix' => 'bx_contact_',
+    'class_prefix' => 'BxContact',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Contact',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+);
+
+/** @} */

+ 22 - 0
modules/boonex/contact/updates/13.0.2_14.0.0/source/template/images/icons/std-icon.svg

@@ -0,0 +1,22 @@
+<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="80" height="80" fill="url(#paint0_linear_1007_3943)"/>
+<path d="M14.041 44.8192C14.211 52.7537 14.296 56.7207 17.2369 59.6596C20.1777 62.5982 24.2705 62.7005 32.4558 62.9052L32.4569 62.9053C37.5021 63.0316 42.4978 63.0316 47.5431 62.9053L47.5441 62.9052C55.7295 62.7005 59.8222 62.5982 62.7632 59.6596C65.704 56.7207 65.7891 52.7537 65.9588 44.8192C66.0137 42.268 66.0137 39.732 65.9588 37.1808C65.9404 36.3205 65.923 35.5069 65.9029 34.7362C65.8642 33.2577 64.2761 32.3791 62.9864 33.103L55.8089 37.1317C55.0024 37.5843 54.6258 38.5292 54.7825 39.4407C54.9255 40.2724 55 41.1275 55 42C55 50.2843 48.2843 57 40 57C31.7157 57 25 50.2843 25 42C25 41.1275 25.0745 40.2723 25.2175 39.4406C25.3742 38.5291 24.9976 37.5843 24.1911 37.1316L17.0135 33.1028C15.7238 32.379 14.1357 33.2575 14.097 34.736C14.0768 35.5067 14.0594 36.3203 14.041 37.1806C13.9863 39.7318 13.9863 42.268 14.041 44.8192Z" fill="black" fill-opacity="0.2"/>
+<path d="M54.3173 33.0351C53.3913 33.5548 52.2307 33.2497 51.5529 32.4323C48.8015 29.1137 44.6476 27 40 27C35.3524 27 31.1985 29.1137 28.4471 32.4322C27.7694 33.2497 26.6088 33.5547 25.6828 33.035L16.1102 27.662C15.3322 27.2253 14.9163 26.3174 15.2285 25.4816C15.6794 24.2749 16.3191 23.2576 17.2369 22.3405C20.1778 19.4017 24.2708 19.2994 32.4569 19.0946C37.5021 18.9685 42.4978 18.9685 47.5431 19.0947C55.7291 19.2994 59.8221 19.4018 62.7632 22.3405C63.681 23.2577 64.3206 24.275 64.7715 25.4817C65.0838 26.3175 64.6678 27.2253 63.8898 27.662L54.3173 33.0351Z" fill="black" fill-opacity="0.2"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M28 42C28 35.3725 33.3725 30 40 30C46.6275 30 52 35.3725 52 42V43.1429C52 45.9831 49.6974 48.2857 46.8571 48.2857C45.2119 48.2857 43.747 47.5131 42.8056 46.3108C41.9988 46.837 41.0351 47.1429 40 47.1429C37.1597 47.1429 34.8571 44.8403 34.8571 42C34.8571 39.1597 37.1597 36.8571 40 36.8571C42.8403 36.8571 45.1429 39.1597 45.1429 42V43.1429C45.1429 44.0896 45.9104 44.8571 46.8571 44.8571C47.8039 44.8571 48.5714 44.0896 48.5714 43.1429V42C48.5714 37.2661 44.7339 33.4286 40 33.4286C35.2661 33.4286 31.4286 37.2661 31.4286 42C31.4286 46.7339 35.2661 50.5714 40 50.5714C40.9468 50.5714 41.7143 51.3389 41.7143 52.2857C41.7143 53.2325 40.9468 54 40 54C33.3725 54 28 48.6275 28 42ZM41.7143 42C41.7143 41.0533 40.9467 40.2857 40 40.2857C39.0533 40.2857 38.2857 41.0533 38.2857 42C38.2857 42.9467 39.0533 43.7143 40 43.7143C40.9467 43.7143 41.7143 42.9467 41.7143 42Z" fill="black" fill-opacity="0.2"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M14.041 43.8192C14.211 51.7537 14.296 55.7207 17.2369 58.6596C20.1777 61.5982 24.2705 61.7005 32.4558 61.9052L32.4569 61.9053C37.5021 62.0316 42.4978 62.0316 47.5431 61.9053L47.5441 61.9052C55.7295 61.7005 59.8222 61.5982 62.7632 58.6596C65.7041 55.7207 65.7891 51.7537 65.9589 43.8192C66.0137 41.268 66.0137 38.732 65.9589 36.1808C65.9404 35.3205 65.923 34.5069 65.9029 33.7362C65.8642 32.2577 64.2761 31.3791 62.9864 32.103L55.8089 36.1317C55.0024 36.5843 54.6258 37.5292 54.7825 38.4407C54.9255 39.2724 55 40.1275 55 41C55 49.2843 48.2843 56 40 56C31.7157 56 25 49.2843 25 41C25 40.1275 25.0745 39.2723 25.2175 38.4406C25.3742 37.5291 24.9976 36.5843 24.1911 36.1316L17.0135 32.1028C15.7238 31.379 14.1357 32.2575 14.097 33.736C14.0768 34.5067 14.0594 35.3203 14.041 36.1806L14.041 36.1808C13.9863 38.732 13.9864 41.268 14.041 43.8192ZM54.3173 32.0351C53.3913 32.5548 52.2307 32.2497 51.5529 31.4323C48.8015 28.1137 44.6476 26 40 26C35.3524 26 31.1985 28.1137 28.4471 31.4322C27.7694 32.2497 26.6088 32.5547 25.6828 32.035L16.1102 26.662C15.3322 26.2253 14.9163 25.3174 15.2285 24.4816C15.6794 23.2749 16.3191 22.2576 17.2369 21.3405C20.1778 18.4017 24.2708 18.2994 32.4569 18.0946C37.5021 17.9685 42.4978 17.9685 47.5431 18.0947L47.5432 18.0947C55.7292 18.2994 59.8221 18.4018 62.7632 21.3405C63.681 22.2577 64.3206 23.275 64.7715 24.4817C65.0838 25.3175 64.6678 26.2253 63.8898 26.662L54.3173 32.0351Z" fill="url(#paint1_linear_1007_3943)"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M28 41C28 34.3725 33.3725 29 40 29C46.6275 29 52 34.3725 52 41V42.1429C52 44.9831 49.6974 47.2857 46.8571 47.2857C45.2119 47.2857 43.747 46.5131 42.8056 45.3108C41.9988 45.837 41.0351 46.1429 40 46.1429C37.1597 46.1429 34.8571 43.8403 34.8571 41C34.8571 38.1597 37.1597 35.8571 40 35.8571C42.8403 35.8571 45.1429 38.1597 45.1429 41V42.1429C45.1429 43.0896 45.9104 43.8571 46.8571 43.8571C47.8039 43.8571 48.5714 43.0896 48.5714 42.1429V41C48.5714 36.2661 44.7339 32.4286 40 32.4286C35.2661 32.4286 31.4286 36.2661 31.4286 41C31.4286 45.7339 35.2661 49.5714 40 49.5714C40.9468 49.5714 41.7143 50.3389 41.7143 51.2857C41.7143 52.2325 40.9468 53 40 53C33.3725 53 28 47.6275 28 41ZM41.7143 41C41.7143 40.0533 40.9467 39.2857 40 39.2857C39.0533 39.2857 38.2857 40.0533 38.2857 41C38.2857 41.9467 39.0533 42.7143 40 42.7143C40.9467 42.7143 41.7143 41.9467 41.7143 41Z" fill="url(#paint2_linear_1007_3943)"/>
+<defs>
+<linearGradient id="paint0_linear_1007_3943" x1="40" y1="0" x2="40" y2="80" gradientUnits="userSpaceOnUse">
+<stop stop-color="#0891B2"/>
+<stop offset="1" stop-color="#155E75"/>
+</linearGradient>
+<linearGradient id="paint1_linear_1007_3943" x1="40" y1="18" x2="40" y2="62" gradientUnits="userSpaceOnUse">
+<stop stop-color="#F9FAFB"/>
+<stop offset="1" stop-color="#9CA3AF"/>
+</linearGradient>
+<linearGradient id="paint2_linear_1007_3943" x1="40" y1="29" x2="40" y2="53" gradientUnits="userSpaceOnUse">
+<stop stop-color="#FDE68A"/>
+<stop offset="1" stop-color="#D97706"/>
+</linearGradient>
+</defs>
+</svg>

+ 51 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Courses',
+    'version_from' => '14.0.2',
+    'version_to' => '14.0.3',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/courses/updates/update_14.0.2_14.0.3/',
+    'home_uri' => 'courses_update_1402_1403',
+
+    'module_dir' => 'boonex/courses/',
+    'module_uri' => 'courses',
+
+    'db_prefix' => 'bx_courses_',
+    'class_prefix' => 'BxCourses',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Courses',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxCoursesUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 88 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/install/langs/en.xml

@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_add name="_bx_courses_option_enable_content"><![CDATA[Enable possibility to add content in a course (experimental)]]></string_add>
+	<string_add name="_bx_courses_option_content_level_max"><![CDATA[Max level for a node in tree based structure (choose before start making courses)]]></string_add>
+	<string_add name="_bx_courses_option_content_modules_step"><![CDATA[Modules from which the content can be added in a course as step]]></string_add>
+	<string_add name="_bx_courses_option_content_modules_attachment"><![CDATA[Modules from which the content can be posted in a course as attachment]]></string_add>
+	<string_add name="_bx_courses_form_content_node"><![CDATA[Content Node]]></string_add>
+	<string_add name="_bx_courses_form_content_node_display_add"><![CDATA[Add Node]]></string_add>
+	<string_add name="_bx_courses_form_content_node_display_edit"><![CDATA[Edit Node]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_sys_title"><![CDATA[Title]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_title"><![CDATA[Title]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_title_err"><![CDATA[Title is essential. Please, fill in this field.]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_sys_text"><![CDATA[Description]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_text"><![CDATA[Description]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_sys_passing"><![CDATA[Passing]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_passing"><![CDATA[Passing]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_passing_all"><![CDATA[All at once]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_passing_alternate"><![CDATA[Alternate]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_sys_do_submit"><![CDATA[Submit]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_do_submit"><![CDATA[Submit]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_sys_do_cancel"><![CDATA[Cancel]]></string_add>
+	<string_add name="_bx_courses_form_content_node_input_do_cancel"><![CDATA[Cancel]]></string_add>
+	<string_add name="_bx_courses_page_title_sys_view_profile_node"><![CDATA[View Lesson]]></string_add>
+	<string_add name="_bx_courses_page_title_view_profile_node"><![CDATA[Lesson]]></string_add>
+	<string_add name="_bx_courses_page_title_sys_profile_content"><![CDATA[Content]]></string_add>
+	<string_add name="_bx_courses_page_title_profile_content"><![CDATA[Content]]></string_add>
+	<string_add name="_bx_courses_page_block_title_system_profile_structure"><![CDATA[Structure]]></string_add>
+	<string_add name="_bx_courses_page_block_title_profile_structure_link"><![CDATA[Structure of <a href="{profile_link}">{display_name}</a>]]></string_add>
+	<string_add name="_bx_courses_page_block_title_system_profile_data"><![CDATA[Content]]></string_add>
+	<string_add name="_bx_courses_page_block_title_profile_data_link"><![CDATA[Content of <a href="{profile_link}">{display_name}</a>]]></string_add>
+	<string_add name="_bx_courses_page_block_title_profile_structure_l1"><![CDATA[Modules]]></string_add>
+	<string_add name="_bx_courses_page_block_title_profile_structure_l2"><![CDATA[Themes]]></string_add>
+	<string_add name="_bx_courses_page_block_title_system_entry_node"><![CDATA[Lesson]]></string_add>
+	<string_add name="_bx_courses_page_block_title_entry_node"><![CDATA[]]></string_add>
+	<string_add name="_bx_courses_menu_item_title_system_edit_content"><![CDATA[Edit Content]]></string_add>
+	<string_add name="_bx_courses_menu_item_title_edit_content"><![CDATA[Edit Content]]></string_add>
+	<string_add name="_bx_courses_menu_item_title_system_sm_reports"><![CDATA[Reports]]></string_add>
+	<string_add name="_bx_courses_menu_item_title_system_sm_pass"><![CDATA[Pass]]></string_add>
+	<string_add name="_bx_courses_menu_title_content_add"><![CDATA[Add Content]]></string_add>
+	<string_add name="_bx_courses_menu_set_title_content_add"><![CDATA[Add Content]]></string_add>
+	<string_add name="_bx_courses_menu_item_title_node_data_steps"><![CDATA[Steps]]></string_add>
+	<string_add name="_bx_courses_menu_item_title_node_data_attachments"><![CDATA[Attachments]]></string_add>
+	<string_add name="_bx_courses_grid_column_title_cnt_active"><![CDATA[]]></string_add>
+	<string_add name="_bx_courses_grid_column_title_cnt_title"><![CDATA[Title]]></string_add>
+	<string_add name="_bx_courses_grid_column_title_cnt_cn_l2"><![CDATA[Themes]]></string_add>
+	<string_add name="_bx_courses_grid_column_title_cnt_cn_l3"><![CDATA[Lessons]]></string_add>
+	<string_add name="_bx_courses_grid_column_title_cnt_counters"><![CDATA[Info]]></string_add>
+	<string_add name="_bx_courses_grid_column_title_cnt_content_id"><![CDATA[Content]]></string_add>
+	<string_add name="_bx_courses_grid_column_title_cnt_content_type"><![CDATA[Type]]></string_add>
+	<string_add name="_bx_courses_grid_column_title_cnt_usage"><![CDATA[Usage]]></string_add>
+	<string_add name="_bx_courses_grid_column_title_cnt_added"><![CDATA[Added]]></string_add>
+	<string_add name="_bx_courses_grid_action_title_cnt_back"><![CDATA[Back]]></string_add>
+	<string_add name="_bx_courses_grid_action_title_cnt_add"><![CDATA[Add {node}]]></string_add>
+	<string_add name="_bx_courses_grid_action_title_cnt_add_step"><![CDATA[Add step]]></string_add>
+	<string_add name="_bx_courses_grid_action_title_cnt_add_attachment"><![CDATA[Add attachment]]></string_add>
+	<string_add name="_bx_courses_grid_action_title_cnt_edit"><![CDATA[Edit]]></string_add>
+	<string_add name="_bx_courses_grid_action_title_cnt_delete"><![CDATA[Delete]]></string_add>
+	<string_add name="_bx_courses_popup_title_cnt_add"><![CDATA[Add {node}]]></string_add>
+	<string_add name="_bx_courses_popup_title_cnt_edit"><![CDATA[Edit {node}]]></string_add>
+	<string_add name="_bx_courses_txt_sample_l1_single"><![CDATA[module]]></string_add>
+	<string_add name="_bx_courses_txt_sample_l1_plural"><![CDATA[modules]]></string_add>
+	<string_add name="_bx_courses_txt_sample_l2_single"><![CDATA[theme]]></string_add>
+	<string_add name="_bx_courses_txt_sample_l2_plural"><![CDATA[themes]]></string_add>
+	<string_add name="_bx_courses_txt_sample_l3_single"><![CDATA[lesson]]></string_add>
+	<string_add name="_bx_courses_txt_sample_l3_plural"><![CDATA[lessons]]></string_add>
+	<string_add name="_bx_courses_txt_status_not_started"><![CDATA[not started]]></string_add>
+	<string_add name="_bx_courses_txt_status_in_process"><![CDATA[in process]]></string_add>
+	<string_add name="_bx_courses_txt_status_completed"><![CDATA[completed]]></string_add>
+	<string_add name="_bx_courses_txt_pass_start"><![CDATA[Start]]></string_add>
+	<string_add name="_bx_courses_txt_pass_continue"><![CDATA[Continue]]></string_add>
+	<string_add name="_bx_courses_txt_pass_again"><![CDATA[Pass again]]></string_add>
+	<string_add name="_bx_courses_txt_pass"><![CDATA[Pass]]></string_add>
+	<string_add name="_bx_courses_txt_passed"><![CDATA[Passed]]></string_add>
+	<string_add name="_bx_courses_txt_n_m_steps"><![CDATA[{0}/{1} steps]]></string_add>
+	<string_add name="_bx_courses_txt_n_m_progress"><![CDATA[{passed}/{total}]]></string_add>
+	<string_add name="_bx_courses_txt_st"><![CDATA[steps]]></string_add>
+	<string_add name="_bx_courses_txt_at"><![CDATA[attachments]]></string_add>
+	<string_add name="_bx_courses_txt_data_type_bx_files"><![CDATA[file]]></string_add>
+	<string_add name="_bx_courses_txt_data_type_bx_photos"><![CDATA[image]]></string_add>
+	<string_add name="_bx_courses_txt_data_type_bx_polls"><![CDATA[poll]]></string_add>
+	<string_add name="_bx_courses_txt_data_type_bx_posts"><![CDATA[theory]]></string_add>
+	<string_add name="_bx_courses_txt_data_type_bx_videos"><![CDATA[video]]></string_add>
+	<string_add name="_bx_courses_txt_data_usage_0"><![CDATA[step]]></string_add>
+	<string_add name="_bx_courses_txt_data_usage_1"><![CDATA[attachment]]></string_add>
+	<string_add name="_bx_courses_txt_back_step"><![CDATA[Back]]></string_add>
+	<string_add name="_bx_courses_txt_next_step"><![CDATA[Next]]></string_add>
+	<string_add name="_bx_courses_txt_download"><![CDATA[Download]]></string_add>
+</resources>

+ 101 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/install/sql/enable.sql

@@ -0,0 +1,101 @@
+-- SETTINGS
+SET @iCategId = (SELECT `id` FROM `sys_options_categories` WHERE `name`='bx_courses' LIMIT 1);
+DELETE FROM `sys_options` WHERE `name` IN ('bx_courses_enable_content', 'bx_courses_content_level_max', 'bx_courses_content_modules_st', 'bx_courses_content_modules_at');
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `extra`, `check`, `check_error`, `order`) VALUES
+('bx_courses_enable_content', '', @iCategId, '_bx_courses_option_enable_content', 'checkbox', '', '', '', 5),
+('bx_courses_content_level_max', '3', @iCategId, '_bx_courses_option_content_level_max', 'select', '1,2,3', '', '', 6),
+('bx_courses_content_modules_st', '', @iCategId, '_bx_courses_option_content_modules_step', 'list', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:30:"get_options_content_modules_st";}', '', '', 7),
+('bx_courses_content_modules_at', '', @iCategId, '_bx_courses_option_content_modules_attachment', 'list', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:30:"get_options_content_modules_at";}', '', '', 8);
+
+
+-- PAGES
+DELETE FROM `sys_pages_blocks` WHERE `object`='bx_courses_view_profile' AND `title` IN ('_bx_courses_page_block_title_profile_structure_l1', '_bx_courses_page_block_title_profile_structure_l2');
+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_courses_view_profile', 2, 'bx_courses', '', '_bx_courses_page_block_title_profile_structure_l1', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:25:"entity_structure_l1_block";}', 0, 0, 1, 1),
+('bx_courses_view_profile', 2, 'bx_courses', '', '_bx_courses_page_block_title_profile_structure_l2', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:25:"entity_structure_l2_block";}', 0, 0, 1, 2);
+
+DELETE FROM `sys_objects_page` WHERE `object`='bx_courses_view_profile_node';
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_view_profile_node', 'view-course-profile-node', '_bx_courses_page_title_sys_view_profile_node', '_bx_courses_page_title_view_profile_node', 'bx_courses', 5, 2147483647, 1, 'page.php?i=view-course-profile-node', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+DELETE FROM `sys_pages_blocks` WHERE `object`='bx_courses_view_profile_node';
+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_courses_view_profile_node', 1, 'bx_courses', '_bx_courses_page_block_title_system_entry_node', '_bx_courses_page_block_title_entry_node', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:17:"entity_node_block";}', 0, 0, 1, 0);
+
+DELETE FROM `sys_objects_page` WHERE `object`='bx_courses_profile_content';
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_profile_content', 'edit-course-content', '_bx_courses_page_title_sys_profile_content', '_bx_courses_page_title_profile_content', 'bx_courses', 5, 2147483647, 1, 'page.php?i=edit-course-content', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+DELETE FROM `sys_pages_blocks` WHERE `object`='bx_courses_profile_content';
+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_courses_profile_content', 1, 'bx_courses', '_bx_courses_page_block_title_system_profile_structure', '_bx_courses_page_block_title_profile_structure_link', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:30:"entity_content_structure_block";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 1),
+('bx_courses_profile_content', 1, 'bx_courses', '_bx_courses_page_block_title_system_profile_data', '_bx_courses_page_block_title_profile_data_link', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:25:"entity_content_data_block";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 2);
+
+
+-- MENUS
+DELETE FROM `sys_menu_items` WHERE `set_name`='bx_courses_view_actions_more' AND `name`='edit-course-content';
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `order`) VALUES 
+('bx_courses_view_actions_more', 'bx_courses', 'edit-course-content', '_bx_courses_menu_item_title_system_edit_content', '_bx_courses_menu_item_title_edit_content', 'page.php?i=edit-course-content&profile_id={profile_id}', '', '', 'folder-tree', '', 2147483647, 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:20:"is_content_avaliable";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 1, 0, 42);
+
+DELETE FROM `sys_objects_menu` WHERE `object`='bx_courses_content_add';
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `persistent`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_content_add', '_bx_courses_menu_title_content_add', 'bx_courses_content_add', 'bx_courses', 6, 1, 0, 1, 'BxCoursesMenuContentAdd', 'modules/boonex/courses/classes/BxCoursesMenuContentAdd.php');
+
+DELETE FROM `sys_menu_sets` WHERE `set_name`='bx_courses_content_add';
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_content_add', 'bx_courses', '_bx_courses_menu_set_title_content_add', 0);
+
+DELETE FROM `sys_menu_items` WHERE `set_name`='bx_courses_snippet_meta' AND `name` IN ('pass', 'reports');
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `editable`, `order`) VALUES
+('bx_courses_snippet_meta', 'bx_courses', 'reports', '_bx_courses_menu_item_title_system_sm_reports', '', '', '', '', '', '', 2147483647, '', 0, 0, 1, 33),
+('bx_courses_snippet_meta', 'bx_courses', 'pass', '_bx_courses_menu_item_title_system_sm_pass', '', '', '', '', '', '', 2147483647, '', 0, 0, 1, 65);
+
+
+-- GRIDS
+UPDATE `sys_grid_actions` SET `title`='_bx_courses_grid_action_title_adm_clear_reports' WHERE `object`='bx_courses_administration' AND `type`='bulk' AND `name`='clear_reports';
+
+DELETE FROM `sys_objects_grid` WHERE `object`='bx_courses_cnt_structure_manage';
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('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');
+
+DELETE FROM `sys_grid_fields` WHERE `object`='bx_courses_cnt_structure_manage';
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('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', '10%', 0, 0, '', 8),
+('bx_courses_cnt_structure_manage', 'actions', '', '20%', 0, 0, '', 9);
+
+DELETE FROM `sys_grid_actions` WHERE `object`='bx_courses_cnt_structure_manage';
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_courses_cnt_structure_manage', 'independent', 'back', '_bx_courses_grid_action_title_cnt_back', 'arrow-left', 0, 0, 1),
+('bx_courses_cnt_structure_manage', 'independent', 'add', '_bx_courses_grid_action_title_cnt_add', '', 0, 0, 2),
+('bx_courses_cnt_structure_manage', 'single', 'edit', '_bx_courses_grid_action_title_cnt_edit', 'pencil-alt', 1, 0, 1),
+('bx_courses_cnt_structure_manage', 'single', 'delete', '_bx_courses_grid_action_title_cnt_delete', 'remove', 1, 1, 2),
+('bx_courses_cnt_structure_manage', 'bulk', 'delete', '_bx_courses_grid_action_title_cnt_delete', '', 0, 1, 1);
+
+DELETE FROM `sys_objects_grid` WHERE `object`='bx_courses_cnt_data_manage';
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_cnt_data_manage', 'Sql', 'SELECT * FROM `bx_courses_content_data` WHERE 1 ', 'bx_courses_content_data', 'id', 'order', '', '', 20, NULL, 'start', '', '', '', 'like', '', '', 2147483647, 'BxCoursesGridCntDataManage', 'modules/boonex/courses/classes/BxCoursesGridCntDataManage.php');
+
+DELETE FROM `sys_grid_fields` WHERE `object`='bx_courses_cnt_data_manage';
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_courses_cnt_data_manage', 'order', '', '4%', 0, 0, '', 1),
+('bx_courses_cnt_data_manage', 'checkbox', '_sys_select', '4%', 0, 0, '', 2),
+('bx_courses_cnt_data_manage', 'content_id', '_bx_courses_grid_column_title_cnt_content_id', '42%', 0, 0, '', 3),
+('bx_courses_cnt_data_manage', 'content_type', '_bx_courses_grid_column_title_cnt_content_type', '10%', 0, 0, '', 4),
+('bx_courses_cnt_data_manage', 'usage', '_bx_courses_grid_column_title_cnt_usage', '10%', 0, 0, '', 5),
+('bx_courses_cnt_data_manage', 'added', '_bx_courses_grid_column_title_cnt_added', '10%', 0, 0, '', 6),
+('bx_courses_cnt_data_manage', 'actions', '', '20%', 0, '', '', 7);
+
+DELETE FROM `sys_grid_actions` WHERE `object`='bx_courses_cnt_data_manage';
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_courses_cnt_data_manage', 'independent', 'back', '_bx_courses_grid_action_title_cnt_back', 'arrow-left', 0, 0, 1),
+('bx_courses_cnt_data_manage', 'independent', 'add_st', '_bx_courses_grid_action_title_cnt_add_step', '', 0, 0, 2),
+('bx_courses_cnt_data_manage', 'independent', 'add_at', '_bx_courses_grid_action_title_cnt_add_attachment', '', 0, 0, 3),
+('bx_courses_cnt_data_manage', 'single', 'delete', '_bx_courses_grid_action_title_cnt_delete', 'remove', 1, 1, 1),
+('bx_courses_cnt_data_manage', 'bulk', 'delete', '_bx_courses_grid_action_title_cnt_delete', '', 0, 1, 1);

+ 93 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/install/sql/install.sql

@@ -0,0 +1,93 @@
+-- TABLES
+CREATE TABLE IF NOT EXISTS `bx_courses_content_structure` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `entry_id` int(11) NOT NULL default '0',
+  `parent_id` int(11) NOT NULL DEFAULT '0',
+  `node_id` int(11) NOT NULL DEFAULT '0',
+  `level` tinyint(4) NOT NULL DEFAULT '0',
+  `order` int(11) NOT NULL DEFAULT '0',
+  `cn_l2` int(11) NOT NULL DEFAULT '0',
+  `cn_l3` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `node_id` (`node_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_content_nodes` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `entry_id` int(11) NOT NULL default '0',
+  `title` varchar(255) NOT NULL DEFAULT '',
+  `text` text NOT NULL,
+  `passing` tinyint(4) NOT NULL DEFAULT '0',
+  `counters` text NOT NULL,
+  `added` int(11) NOT NULL,
+  `status` enum('active','hidden') NOT NULL DEFAULT 'active',
+  PRIMARY KEY (`id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_content_nodes2users` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `node_id` int(11) NOT NULL default '0',
+  `profile_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `pass` (`node_id`, `profile_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_content_data` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `entry_id` int(11) NOT NULL default '0',
+  `node_id` int(11) NOT NULL DEFAULT '0',
+  `content_type` varchar(32) NOT NULL DEFAULT '',
+  `content_id` int(11) NOT NULL DEFAULT '0',
+  `usage` tinyint(4) NOT NULL DEFAULT '0',
+  `added` int(11) NOT NULL DEFAULT '0',
+  `order` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_content_data2users` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `data_id` int(11) NOT NULL default '0',
+  `profile_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `pass` (`data_id`, `profile_id`)
+);
+
+
+-- FORMS
+DELETE FROM `sys_objects_form` WHERE `object`='bx_courses_content_node';
+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
+('bx_courses_content_node', 'bx_courses', '_bx_courses_form_content_node', '', '', 'do_submit', 'bx_courses_content_nodes', 'id', '', '', '', 0, 1, 'BxCoursesFormContentNode', 'modules/boonex/courses/classes/BxCoursesFormContentNode.php');
+
+DELETE FROM `sys_form_displays` WHERE `object`='bx_courses_content_node';
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_courses_content_node_add', 'bx_courses', 'bx_courses_content_node', '_bx_courses_form_content_node_display_add', 0),
+('bx_courses_content_node_edit', 'bx_courses', 'bx_courses_content_node', '_bx_courses_form_content_node_display_edit', 0);
+
+DELETE FROM `sys_form_inputs` WHERE `object`='bx_courses_content_node';
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_courses_content_node', 'bx_courses', 'title', '', '', 0, 'text', '_bx_courses_form_content_node_input_sys_title', '_bx_courses_form_content_node_input_title', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_courses_form_content_node_input_title_err', 'Xss', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'text', '', '', 0, 'textarea', '_bx_courses_form_content_node_input_sys_text', '_bx_courses_form_content_node_input_text', '', 0, 0, 2, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'passing', '', '', 0, 'select', '_bx_courses_form_content_node_input_sys_passing', '_bx_courses_form_content_node_input_passing', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'do_submit', '_bx_courses_form_content_node_input_do_submit', '', 0, 'submit', '_bx_courses_form_content_node_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'do_cancel', '_bx_courses_form_content_node_input_do_cancel', '', 0, 'button', '_bx_courses_form_content_node_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+
+DELETE FROM `sys_form_display_inputs` WHERE `display_name` IN ('bx_courses_content_node_add', 'bx_courses_content_node_edit');
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_courses_content_node_add', 'id', 2147483647, 0, 1),
+('bx_courses_content_node_add', 'title', 2147483647, 1, 2),
+('bx_courses_content_node_add', 'text', 2147483647, 1, 3),
+('bx_courses_content_node_add', 'passing', 2147483647, 1, 4),
+('bx_courses_content_node_add', 'controls', 2147483647, 1, 5),
+('bx_courses_content_node_add', 'do_submit', 2147483647, 1, 6),
+('bx_courses_content_node_add', 'do_cancel', 2147483647, 1, 7),
+
+('bx_courses_content_node_edit', 'id', 2147483647, 1, 1),
+('bx_courses_content_node_edit', 'title', 2147483647, 1, 2),
+('bx_courses_content_node_edit', 'text', 2147483647, 1, 3),
+('bx_courses_content_node_edit', 'passing', 2147483647, 1, 4),
+('bx_courses_content_node_edit', 'controls', 2147483647, 1, 5),
+('bx_courses_content_node_edit', 'do_submit', 2147483647, 1, 6),
+('bx_courses_content_node_edit', 'do_cancel', 2147483647, 1, 7);

+ 378 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesConfig.php

@@ -0,0 +1,378 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxCoursesConfig extends BxBaseModGroupsConfig
+{
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_aMenuItems2MethodsActions = array_merge($this->_aMenuItems2MethodsActions, array(
+            'view-course-profile' => 'checkAllowedView',
+            'edit-course-profile' => 'checkAllowedEdit',
+            'edit-course-cover' => 'checkAllowedChangeCover',
+            'invite-to-course' => 'checkAllowedInvite',
+            'delete-course-profile' => 'checkAllowedDelete',
+            'approve-course-profile' => 'checkAllowedApprove',
+        ));
+
+        $this->CNF = array (
+
+            // module icon
+            'ICON' => 'book-reader col-blue3-dark',
+
+            // database tables
+            'TABLE_ENTRIES' => $aModule['db_prefix'] . 'data',
+            'TABLE_ENTRIES_FULLTEXT' => 'search_fields',
+            'TABLE_ADMINS' => $aModule['db_prefix'] . 'admins',
+            'TABLE_INVITES' => $aModule['db_prefix'] . 'invites',
+            'TABLE_PRICES' => $aModule['db_prefix'] . 'prices',
+            'TABLE_CNT_STRUCTURE' => $aModule['db_prefix'] . 'content_structure',
+            'TABLE_CNT_NODES' => $aModule['db_prefix'] . 'content_nodes',
+            'TABLE_CNT_NODES2USERS' => $aModule['db_prefix'] . 'content_nodes2users',
+            'TABLE_CNT_DATA' => $aModule['db_prefix'] . 'content_data',
+            'TABLE_CNT_DATA2USERS' => $aModule['db_prefix'] . 'content_data2users',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_AUTHOR' => 'author',
+            'FIELD_ADDED' => 'added',
+            'FIELD_CHANGED' => 'changed',
+            'FIELD_NAME' => 'name',
+            'FIELD_TITLE' => 'name',
+            'FIELD_TEXT' => 'desc',
+            'FIELD_CATEGORY' => 'cat',
+            'FIELD_PICTURE' => 'picture',
+            'FIELD_COVER' => 'cover',
+            'FIELD_COVER_POSITION' => 'cover_data',
+            'FIELD_JOIN_CONFIRMATION' => 'join_confirmation',
+            'FIELD_ALLOW_VIEW_TO' => 'allow_view_to',
+            'FIELD_ALLOW_POST_TO' => 'allow_post_to',
+            'FIELD_CF' => 'cf',
+            'FIELD_VIEWS' => 'views',
+            'FIELD_VOTES' => 'votes',
+            'FIELD_REPORTS' => 'reports',
+            'FIELD_STATUS' => 'status',
+            'FIELD_STATUS_ADMIN' => 'status_admin',
+            'FIELD_COMMENTS' => 'comments',
+            'FIELDS_QUICK_SEARCH' => array('name'),
+            'FIELD_LOCATION' => 'location',
+            'FIELD_LOCATION_PREFIX' => 'location',
+            'FIELD_LABELS' => 'labels',
+            'FIELDS_WITH_KEYWORDS' => 'auto', // can be 'auto', array of fields or comma separated string of field names, works only when OBJECT_METATAGS is specified
+
+            'FIELD_PRICE_ROLE_ID' => 'role_id',
+            'FIELD_PRICE_NAME' => 'name',
+
+            // page URIs
+            'URI_VIEW_ENTRY' => 'view-course-profile',
+            'URI_VIEW_ENTRY_NODE' => 'view-course-profile-node',
+            'URI_EDIT_ENTRY' => 'edit-course-profile',
+            'URI_EDIT_ENTRY_CONTENT' => 'edit-course-content',
+            'URI_JOIN_ENTRY' => 'join-course-profile',
+            'URI_JOINED_ENTRIES' => 'joined-courses',
+            'URI_MANAGE_COMMON' => 'courses-manage',
+            'URI_FAVORITES_LIST' => 'courses-favorites',
+
+            'URL_HOME' => 'page.php?i=courses-home',
+            'URL_ENTRY_FANS' => 'page.php?i=course-fans',
+            'URL_MANAGE_STRUCTURE' => 'page.php?i=edit-course-content',
+            'URL_MANAGE_COMMON' => 'page.php?i=courses-manage',
+            'URL_MANAGE_ADMINISTRATION' => 'page.php?i=courses-administration',
+
+            'PARAM_AUTO_APPROVE' => 'bx_courses_enable_auto_approve',
+            'PARAM_NUM_RSS' => 'bx_courses_num_rss',
+            'PARAM_NUM_CONNECTIONS_QUICK' => 'bx_courses_num_connections_quick',
+
+            'PARAM_SEARCHABLE_FIELDS' => 'bx_courses_searchable_fields',
+            'PARAM_PER_PAGE_BROWSE_SHOWCASE' => 'bx_courses_per_page_browse_showcase',
+            'PARAM_PER_PAGE_BROWSE_RECOMMENDED' => 'bx_courses_per_page_browse_recommended',
+
+            'PARAM_MMODE' => 'bx_courses_members_mode',
+            'PARAM_PAID_JOIN_ENABLED' => true,
+            'PARAM_RECURRING_RESERVE' => 3, // 3 days for recurring payment to be registered
+            'PARAM_PER_PAGE_FOR_FAVORITES_LISTS' => 'bx_courses_per_page_for_favorites_lists',
+            'PARAM_USE_IN' => 'bx_courses_internal_notifications',
+
+            'PARAM_CONTENT' => 'bx_courses_enable_content',
+            'PARAM_CONTENT_LEVEL_MAX' => 'bx_courses_content_level_max',
+            'PARAM_CONTENT_MODULES_ST' => 'bx_courses_content_modules_st',
+            'PARAM_CONTENT_MODULES_AT' => 'bx_courses_content_modules_at',
+
+            // objects
+            'OBJECT_STORAGE' => 'bx_courses_pics',
+            'OBJECT_STORAGE_COVER' => 'bx_courses_pics',
+            'OBJECT_IMAGES_TRANSCODER_THUMB' => 'bx_courses_thumb',
+            'OBJECT_IMAGES_TRANSCODER_ICON' => 'bx_courses_icon',
+            'OBJECT_IMAGES_TRANSCODER_AVATAR' => 'bx_courses_avatar',
+            'OBJECT_IMAGES_TRANSCODER_AVATAR_BIG' => 'bx_courses_avatar_big',
+            'OBJECT_IMAGES_TRANSCODER_PICTURE' => 'bx_courses_picture',
+            'OBJECT_IMAGES_TRANSCODER_COVER' => 'bx_courses_cover',
+            'OBJECT_IMAGES_TRANSCODER_COVER_THUMB' => 'bx_courses_cover_thumb',
+            'OBJECT_IMAGES_TRANSCODER_GALLERY' => 'bx_courses_gallery',
+            'OBJECT_VIEWS' => 'bx_courses',
+            'OBJECT_VOTES' => 'bx_courses',
+            'OBJECT_SCORES' => 'bx_courses',
+            'OBJECT_FAVORITES' => 'bx_courses',
+            'OBJECT_FEATURED' => 'bx_courses',
+            'OBJECT_COMMENTS' => 'bx_courses',
+            'OBJECT_NOTES' => 'bx_courses_notes',
+            'OBJECT_REPORTS' => 'bx_courses',
+            'OBJECT_METATAGS' => 'bx_courses',
+            'OBJECT_CATEGORY' => 'bx_courses_cats',
+            'OBJECT_FORM_ENTRY' => 'bx_course',
+            'OBJECT_FORM_ENTRY_DISPLAY_VIEW' => 'bx_course_view',
+            'OBJECT_FORM_ENTRY_DISPLAY_VIEW_FULL' => 'bx_course_view_full', // for "info" tab on view course page
+            'OBJECT_FORM_ENTRY_DISPLAY_ADD' => 'bx_course_add',
+            'OBJECT_FORM_ENTRY_DISPLAY_EDIT' => 'bx_course_edit',
+            'OBJECT_FORM_ENTRY_DISPLAY_EDIT_COVER' => 'bx_course_edit_cover',
+            'OBJECT_FORM_ENTRY_DISPLAY_DELETE' => 'bx_course_delete',
+            'OBJECT_FORM_ENTRY_DISPLAY_INVITE' => 'bx_course_invite',
+            'OBJECT_FORM_PRICE' => 'bx_courses_price',
+            'OBJECT_FORM_PRICE_DISPLAY_ADD' => 'bx_courses_price_add',
+            'OBJECT_FORM_PRICE_DISPLAY_EDIT' => 'bx_courses_price_edit',
+            'OBJECT_FORM_CNT_NODE' => 'bx_courses_content_node',
+            'OBJECT_FORM_CNT_NODE_DISPLAY_ADD' => 'bx_courses_content_node_add',
+            'OBJECT_FORM_CNT_NODE_DISPLAY_EDIT' => 'bx_courses_content_node_edit',
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY' => 'bx_courses_view_actions', // actions menu on view entry page
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY_MORE' => 'bx_courses_view_actions_more', // actions menu on view entry page for "more" popup
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY_ALL' => 'bx_courses_view_actions_all', // all actions menu on view entry page
+            'OBJECT_MENU_ACTIONS_MY_ENTRIES' => 'bx_courses_my', // actions menu on profile entries page
+            'OBJECT_MENU_SUBMENU' => 'bx_courses_submenu', // main module submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY' => 'bx_courses_view_submenu',  // view entry submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY_MAIN_SELECTION' => 'courses-home', // first item in view entry submenu from main module submenu
+            'OBJECT_MENU_MANAGE_VIEW_ENTRY' => '',
+            'OBJECT_MENU_VIEW_ENTRY_META' => 'bx_courses_view_meta', // meta menu on view entry page
+            'OBJECT_MENU_SNIPPET_META' => 'bx_courses_snippet_meta', // menu for snippet meta info
+            'OBJECT_MENU_MANAGE_TOOLS' => 'bx_courses_menu_manage_tools', //manage menu in content administration tools
+            'OBJECT_MENU_CONTENT_ADD' => 'bx_courses_content_add',
+            'OBJECT_PAGE_VIEW_ENTRY' => 'bx_courses_view_profile',
+            'OBJECT_PAGE_VIEW_ENTRY_CLOSED' => 'bx_courses_view_profile_closed',
+            'OBJECT_PAGE_JOINED_ENTRY' => 'bx_courses_join_profile',
+            'OBJECT_PRIVACY_VIEW' => 'bx_courses_allow_view_to',
+            'OBJECT_PRIVACY_LIST_VIEW' => 'bx_courses_allow_view_favorite_list',
+            'OBJECT_PRIVACY_VIEW_NOTIFICATION_EVENT' => 'bx_courses_allow_view_notification_to',
+            'OBJECT_PRIVACY_POST' => 'bx_courses_allow_post_to',
+            'OBJECT_GRID_ADMINISTRATION' => 'bx_courses_administration',
+            'OBJECT_GRID_COMMON' => 'bx_courses_common',
+            'OBJECT_GRID_CONNECTIONS' => 'bx_courses_fans',
+            'OBJECT_GRID_INVITES' => 'bx_courses_invites',
+            'OBJECT_GRID_PRICES_MANAGE' => 'bx_courses_prices_manage',
+            'OBJECT_GRID_PRICES_VIEW' => 'bx_courses_prices_view',
+            'OBJECT_GRID_CNT_STRUCTURE_MANAGE' => 'bx_courses_cnt_structure_manage',
+            'OBJECT_GRID_CNT_DATA_MANAGE' => 'bx_courses_cnt_data_manage',
+            'OBJECT_CONNECTIONS' => 'bx_courses_fans',
+            'OBJECT_UPLOADERS_COVER' => array('bx_courses_cover_crop'),
+            'OBJECT_UPLOADERS_PICTURE' => array('bx_courses_picture_crop'),
+            'OBJECT_PRE_LIST_ROLES' => 'bx_courses_roles',
+            'OBJECT_PRE_LIST_PERIOD_UNITS' => 'bx_courses_period_units',
+
+            'BADGES_AVALIABLE' => true,
+            
+            'INVITES_KEYS_LIFETIME' => 86400,
+
+            'EMAIL_INVITATION' => 'bx_courses_invitation',
+            'EMAIL_JOIN_REQUEST' => 'bx_courses_join_request',
+            'EMAIL_JOIN_CONFIRM' => 'bx_courses_join_confirm',
+            'EMAIL_FAN_BECOME_ADMIN' => 'bx_courses_fan_become_admin',
+            'EMAIL_ADMIN_BECOME_FAN' => 'bx_courses_admin_become_fan',
+            'EMAIL_FAN_REMOVE' => 'bx_courses_fan_remove',
+            'EMAIL_FAN_SET_ROLE' => 'bx_courses_set_role',
+            'EMAIL_JOIN_REJECT' => 'bx_courses_join_reject',
+
+            'TRIGGER_MENU_PROFILE_VIEW_SUBMENU' => 'trigger_group_view_submenu',
+            'TRIGGER_MENU_PROFILE_SNIPPET_META' => 'trigger_group_snippet_meta',
+            'TRIGGER_MENU_PROFILE_VIEW_ACTIONS' => 'trigger_group_view_actions',
+            'TRIGGER_PAGE_VIEW_ENTRY' => 'trigger_page_group_view_entry',
+
+            // menu items which visibility depends on custom visibility checking
+            'MENU_ITEM_TO_METHOD' => array (
+                'bx_courses_view_actions' => $this->_aMenuItems2MethodsActions,
+                'bx_courses_view_actions_more' => $this->_aMenuItems2MethodsActions,
+                'bx_courses_view_actions_all' => $this->_aMenuItems2MethodsActions,
+            ),
+
+            // informer messages
+            'INFORMERS' => array (
+                'status' => array (
+                    'name' => 'bx-courses-status-not-active',
+                    'map' => array (
+                        BX_PROFILE_STATUS_PENDING => '_bx_courses_txt_account_pending',
+                        BX_PROFILE_STATUS_SUSPENDED => '_bx_courses_txt_account_suspended',
+                    ),
+                ),
+                'approving' => array (
+                    'name' => 'bx-groups-approving',
+                    'map' => array (
+                        'pending' => array('msg' => '_bx_courses_txt_account_pending', 'type' => BX_INFORMER_ALERT),
+                        'hidden' => array('msg' => '_bx_courses_txt_status_hidden', 'type' => BX_INFORMER_ERROR),
+                    ),
+                ),
+            ),
+
+            // some language keys
+            'T' => array (
+                'txt_sample_single' => '_bx_courses_txt_sample_single',
+                'txt_sample_single_with_article' => '_bx_courses_txt_sample_single_with_article',
+                'txt_sample_comment_single' => '_bx_courses_txt_sample_comment_single',
+                'txt_sample_vote_single' => '_bx_courses_txt_sample_vote_single',
+                'txt_sample_score_up_single' => '_bx_courses_txt_sample_score_up_single',
+                'txt_sample_score_down_single' => '_bx_courses_txt_sample_score_down_single',
+                'txt_private_group' => '_bx_courses_txt_private_entry',
+                'txt_N_fans' => '_bx_courses_txt_N_fans',
+                'txt_ntfs_join_invitation' => '_bx_courses_txt_ntfs_join_invitation',
+                'txt_ntfs_join_request' => '_bx_courses_txt_ntfs_join_request',
+                'txt_ntfs_fan_added' => '_bx_courses_txt_ntfs_fan_added',
+                'txt_ntfs_timeline_post_common' => '_bx_courses_txt_ntfs_timeline_post_common',
+                'option_members_mode_multi_roles' => '_bx_courses_option_members_mode_multi_roles',
+                'form_field_author' => '_bx_courses_form_entry_input_author',
+                'menu_item_title_befriend_sent' => '_bx_courses_menu_item_title_befriend_sent',
+                'menu_item_title_unfriend_cancel_request' => '_bx_courses_menu_item_title_unfriend_cancel_request',
+                'menu_item_title_befriend_confirm' => '_bx_courses_menu_item_title_befriend_confirm',
+                'menu_item_title_unfriend_reject_request' => '_bx_courses_menu_item_title_unfriend_reject_request',
+                'menu_item_title_befriend' => '_bx_courses_menu_item_title_befriend',
+                'menu_item_title_unfriend' => '_bx_courses_menu_item_title_unfriend',
+                'grid_action_err_delete' => '_bx_courses_grid_action_err_delete',
+                'grid_txt_account_manager' => '_bx_courses_grid_txt_account_manager',
+                'filter_item_active' => '_bx_courses_grid_filter_item_title_adm_active',
+                'filter_item_hidden' => '_bx_courses_grid_filter_item_title_adm_hidden',
+                'filter_item_pending' => '_bx_courses_grid_filter_item_title_adm_pending',
+                'filter_item_suspended' => '_bx_courses_grid_filter_item_title_adm_suspended',
+                'filter_item_select_one_filter1' => '_bx_courses_grid_filter_item_title_adm_select_one_filter1',
+                'filter_item_select_one_filter2' => '_bx_courses_grid_filter_item_title_adm_select_one_filter2',
+                'menu_item_manage_my' => '_bx_courses_menu_item_title_manage_my',
+                'menu_item_manage_all' => '_bx_courses_menu_item_title_manage_all',
+                'menu_item_title_sm_join' => '_bx_courses_menu_item_title_sm_join',
+                'menu_item_title_sm_join_paid' => '_bx_courses_menu_item_title_sm_join_paid',
+                'menu_item_title_sm_join_requested' => '_bx_courses_menu_item_title_become_fan_sent',
+                'menu_item_title_sm_join_confirm' => '_bx_courses_menu_item_title_sm_join_confirm',
+                'menu_item_title_sm_leave' => '_bx_courses_menu_item_title_sm_leave',
+                'menu_item_title_sm_leave_cancel' => '_bx_courses_menu_item_title_sm_leave_cancel',
+                'menu_item_title_sm_leave_reject' => '_bx_courses_menu_item_title_sm_leave_reject',
+                'txt_all_entries_by_author' => '_bx_courses_page_title_browse_by_author',
+                'txt_invitation_popup_title' => '_bx_courses_txt_invite_popup_title',
+                'txt_invitation_popup_text' => '_bx_courses_txt_invite_popup_text',
+                'txt_invitation_popup_accept_button' => '_bx_courses_txt_invite_popup_button_accept',
+                'txt_invitation_popup_decline_button' => '_bx_courses_txt_invite_popup_button_decline',
+                'txt_invitation_popup_error_invitation_absent' => '_bx_courses_txt_invite_popup_error_invitation_absent',
+                'txt_invitation_popup_error_wrong_user' => '_bx_courses_txt_invite_popup_error_invitation_wrong_user',
+                'txt_n_unit' => '_bx_courses_txt_n_unit',
+                'txt_buy_title' => '_bx_courses_grid_action_title_buy_title',
+                'txt_cart_item_title' => '_bx_courses_txt_cart_item_title',
+                'txt_cart_item_title_lifetime' => '_bx_courses_txt_cart_item_title_lifetime',
+                'txt_subscribe_title' => '_bx_courses_grid_action_title_subscribe_title',
+                'msg_performed' => '_bx_courses_msg_performed',
+                'popup_title_price_add' => '_bx_courses_popup_title_price_add',
+                'popup_title_price_edit' => '_bx_courses_popup_title_price_edit',
+                'popup_title_content_node_add' => '_bx_courses_popup_title_cnt_add',
+                'popup_title_content_node_edit' => '_bx_courses_popup_title_cnt_edit',
+                'err_period_unit' => '_bx_courses_form_price_input_err_period_unit',
+                'err_price_duplicate' => '_bx_courses_err_price_duplicate',
+                'err_cannot_perform' => '_bx_courses_err_cannot_perform',
+            ),
+
+        );
+
+        $this->_aJsClasses = [
+            'main' => 'BxCoursesMain',
+            'entry' => 'BxCoursesEntry',
+            'manage_tools' => 'BxCoursesManageTools',
+            'invite_popup' => 'BxCoursesInvitePopup',
+            'prices' => 'BxCoursesPrices'
+        ];
+
+        $this->_aJsObjects = [
+            'main' => 'oBxCoursesMain',
+            'entry' => 'oBxCoursesEntry',
+            'manage_tools' => 'oBxCoursesManageTools',
+            'invite_popup' => 'oBxCoursesInvitePopup',
+            'prices' => 'oBxCoursesPrices'
+        ];
+
+        $this->_aGridObjects = [
+            'common' => $this->CNF['OBJECT_GRID_COMMON'],
+            'administration' => $this->CNF['OBJECT_GRID_ADMINISTRATION'],
+        ];
+
+        $sHtmlPrefix = str_replace('_', '-', $this->_sName);
+        $this->_aHtmlIds = [
+            'popup_content_node' => $sHtmlPrefix . '-popup-content-node',
+            'popup_content_data' => $sHtmlPrefix . '-popup-content-data',
+        ];
+    }
+
+    public function isContent()
+    {
+        return getParam($this->CNF['PARAM_CONTENT']) == 'on';
+    }
+
+    public function getContentModules($iUsage)
+    {
+        return explode(',', getParam($this->CNF['PARAM_CONTENT_MODULES_' . $this->getUsageI2S($iUsage, false)]));
+    }
+
+    public function getContentLevelMax()
+    {
+        return (int)getParam($this->CNF['PARAM_CONTENT_LEVEL_MAX']);
+    }
+
+    public function getContentLevel2Node($bSingle = true)
+    {
+        $aResult = [];
+
+        $sPostfix = $bSingle ? 'single' : 'plural';
+        switch($this->getContentLevelMax()) {
+            case 1:
+                $aResult = [
+                    1 => _t('_bx_courses_txt_sample_l3_' . $sPostfix)
+                ];
+                break;
+            
+            case 2:
+                $aResult = [
+                    1 => _t('_bx_courses_txt_sample_l1_' . $sPostfix),
+                    2 => _t('_bx_courses_txt_sample_l3_' . $sPostfix)
+                ];
+                break;
+            
+            case 3:
+                $aResult = [
+                    1 => _t('_bx_courses_txt_sample_l1_' . $sPostfix),
+                    2 => _t('_bx_courses_txt_sample_l2_' . $sPostfix),
+                    3 => _t('_bx_courses_txt_sample_l3_' . $sPostfix)
+                ];
+                break;
+        }
+        
+        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)
+    {
+        $a = [
+            BX_COURSES_CND_USAGE_ST => 'st',
+            BX_COURSES_CND_USAGE_AT => 'at'
+        ];
+
+        if(!isset($a[$iUsage]))
+            $iUsage = BX_COURSES_CND_USAGE_ST;        
+
+        return $bLowerCase ? $a[$iUsage] : strtoupper($a[$iUsage]);
+    }
+}
+
+/** @} */

+ 461 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesDb.php

@@ -0,0 +1,461 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Courses module database queries
+ */
+class BxCoursesDb extends BxBaseModGroupsDb
+{
+    public function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+    }
+
+    public function getContentNodes($aParams)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+
+        $sSelectClause = "`tcn`.*";
+        $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        switch($aParams['sample']) {
+            case 'id':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'id' => $aParams['id']
+                ];
+
+                $sWhereClause = "AND `tcn`.`id`=:id";
+                break;
+
+            case 'id_full':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'id' => $aParams['id']
+                ];
+
+                $sSelectClause .= ", `tcs`.`parent_id`, `tcs`.`level`, `tcs`.`order`, `tcs`.`cn_l2`, `tcs`.`cn_l3`";
+                $sJoinClause = "LEFT JOIN `" . $CNF['TABLE_CNT_STRUCTURE'] . "` AS `tcs` ON `tcn`.`id`=`tcs`.`node_id`";
+                $sWhereClause = "AND `tcn`.`id`=:id";
+                break;
+        }
+
+        if(!empty($sOrderClause))
+            $sOrderClause = "ORDER BY " . $sOrderClause;
+        
+        if(!empty($sLimitClause))
+            $sLimitClause = "LIMIT " . $sLimitClause;
+
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $CNF['TABLE_CNT_NODES'] . "` AS `tcn` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+
+    public function updateContentNodes($aParamsSet, $aParamsWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsSet) || empty($aParamsWhere))
+            return false;
+
+        return $this->query("UPDATE `" . $CNF['TABLE_CNT_NODES'] . "` SET " . $this->arrayToSQL($aParamsSet) . " WHERE " . $this->arrayToSQL($aParamsWhere, " AND "));
+    }
+
+    public function deleteContentNodes($aParamsWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsWhere))
+            return false;
+
+        return $this->query("DELETE FROM `" . $CNF['TABLE_CNT_NODES'] . "` WHERE " . $this->arrayToSQL($aParamsWhere, " AND "));
+    }
+
+    public function deleteContentNodesWithTracks($iEntryId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return $this->query("DELETE FROM `tcn`, `tcnu` USING `" . $CNF['TABLE_CNT_NODES'] . "` AS `tcn` LEFT JOIN `" . $CNF['TABLE_CNT_NODES2USERS'] . "` AS `tcnu` ON `tcn`.`id`=`tcnu`.`node_id` WHERE `tcn`.`entry_id`=:entry_id", [
+            'entry_id' => $iEntryId
+        ]);
+    }
+
+    public function insertContentNodes2Users($aParamsSet)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsSet))
+            return false;
+
+        return (int)$this->query("INSERT INTO `" . $CNF['TABLE_CNT_NODES2USERS'] . "` SET " . $this->arrayToSQL($aParamsSet) . " ON DUPLICATE KEY UPDATE `date`=UNIX_TIMESTAMP()") > 0 ? (int)$this->lastId() : false;
+    }
+
+    public function deleteContentNodes2Users($aParamsWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsWhere))
+            return false;
+
+        return $this->query("DELETE FROM `" . $CNF['TABLE_CNT_NODES2USERS'] . "` WHERE " . $this->arrayToSQL($aParamsWhere, " AND "));
+    }
+
+    public function getContentStructure($aParams)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+
+        $sSelectClause = "`tcs`.*";
+        $sJoinClause = $sWhereClause = $sGroupClause = $sOrderClause = $sLimitClause = "";
+
+        switch($aParams['sample']) {
+            case 'id':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'id' => $aParams['id']
+                ];
+
+                $sWhereClause = "AND `tcs`.`id`=:id";
+                break;
+
+            case 'node_id':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'node_id' => $aParams['node_id']
+                ];
+
+                $sWhereClause = "AND `tcs`.`node_id`=:node_id";
+                break;
+            
+            case 'entry_id':
+                $aMethod['params'][1] = [
+                    'entry_id' => $aParams['entry_id']
+                ];
+
+                $sWhereClause = "AND `tcs`.`entry_id`=:entry_id";
+
+                if(isset($aParams['level'])) {
+                    $aMethod['params'][1]['level'] = $aParams['level'];
+                        
+                    $sWhereClause .= " AND `tcs`.`level`=:level";
+                }
+                break;
+
+            case 'entry_id_full':
+                $aMethod['params'][1] = [
+                    'entry_id' => $aParams['entry_id']
+                ];
+
+                $sSelectClause .= ", `tcn`.`title`, `tcn`.`counters`";
+                $sJoinClause = "LEFT JOIN `" . $CNF['TABLE_CNT_NODES'] . "` AS `tcn` ON `tcs`.`node_id`=`tcn`.`id`";
+                $sWhereClause = "AND `tcs`.`entry_id`=:entry_id";
+
+                if(isset($aParams['parent_id'])) {
+                    $aMethod['params'][1]['parent_id'] = $aParams['parent_id'];
+                        
+                    $sWhereClause .= " AND `tcs`.`parent_id`=:parent_id";
+                }
+
+                if(isset($aParams['level'])) {
+                    $aMethod['params'][1]['level'] = $aParams['level'];
+                        
+                    $sWhereClause .= " AND `tcs`.`level`=:level";
+                }
+
+                if(isset($aParams['status'])) {
+                    $aMethod['params'][1]['status'] = $aParams['status'];
+                        
+                    $sWhereClause .= " AND `tcn`.`status`=:status";
+                }
+
+                if(isset($aParams['start'], $aParams['per_page']) && (int)$aParams['per_page'] != 0)
+                    $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']
+                ];
+
+                $sWhereClause = "AND `tcs`.`parent_id`=:parent_id";
+                break;
+
+            case 'user_passed':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'profile_id' => $aParams['profile_id'],
+                    'node_id' => $aParams['node_id']
+                ];
+
+                $sSelectClause = "`tcnu`.*";
+                $sJoinClause = "INNER JOIN `" . $CNF['TABLE_CNT_NODES2USERS'] . "` AS `tcnu` ON `tcs`.`node_id`=`tcnu`.`node_id` AND `tcnu`.`node_id`=:node_id AND `tcnu`.`profile_id`=:profile_id";
+                break;
+
+            case 'user_track':
+                $aMethod['params'][1] = [
+                    'profile_id' => $aParams['profile_id'],
+                    'entry_id' => $aParams['entry_id'],
+                    'node_id' => $aParams['node_id']
+                ];
+
+                $sJoinClause = "INNER JOIN `" . $CNF['TABLE_CNT_NODES2USERS'] . "` AS `tcnu` ON `tcs`.`node_id`=`tcnu`.`node_id` AND `tcnu`.`profile_id`=:profile_id";
+                $sWhereClause = "AND `tcs`.`entry_id`=:entry_id AND `tcs`.`parent_id`=:node_id";
+                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 . " " . $sGroupClause . " " . $sOrderClause . " " . $sLimitClause;
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+
+    public function getContentStructureOrderMax($iEntryId, $iParentId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return (int)$this->getOne("SELECT IFNULL(MAX(`order`), 0) FROM `" . $CNF['TABLE_CNT_STRUCTURE'] . "` WHERE `entry_id`=:entry_id AND `parent_id`=:parent_id", [
+            'entry_id' => $iEntryId,
+            'parent_id' => $iParentId
+        ]);
+    }
+    
+    public function insertContentStructureNode($aParamsSet)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsSet))
+            return false;
+
+        return (int)$this->query("INSERT INTO `" . $CNF['TABLE_CNT_STRUCTURE'] . "` SET " . $this->arrayToSQL($aParamsSet)) > 0 ? (int)$this->lastId() : false;
+    }
+
+    public function updateContentStructureNode($aParamsSet, $aParamsWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsSet) || empty($aParamsWhere))
+            return false;
+
+        return $this->query("UPDATE `" . $CNF['TABLE_CNT_STRUCTURE'] . "` SET " . $this->arrayToSQL($aParamsSet) . " WHERE " . $this->arrayToSQL($aParamsWhere, " AND "));
+    }
+    
+    public function updateContentStructureCounters($iParentId, $iLevel, $iAdd)
+    {
+        $aParent = $this->getContentStructure(['sample' => 'node_id', 'node_id' => $iParentId]);
+        if(empty($aParent) || !is_array($aParent))
+            return;
+
+        $sKeyLevel = 'cn_l' . $iLevel;
+        $this->updateContentStructureNode([$sKeyLevel => $aParent[$sKeyLevel] + $iAdd], ['id' => $aParent['id']]);
+        
+        if(!empty($aParent['parent_id']))
+            $this->updateContentStructureCounters($aParent['parent_id'], $iLevel, $iAdd);
+    }
+
+    public function deleteContentStructureNode($aParamsWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsWhere))
+            return false;
+
+        return $this->query("DELETE FROM `" . $CNF['TABLE_CNT_STRUCTURE'] . "` WHERE " . $this->arrayToSQL($aParamsWhere, " AND "));
+    }
+
+    public function getContentData($aParams)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+
+        $sSelectClause = "`tcd`.*";
+        $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        switch($aParams['sample']) {
+            case 'id':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'id' => $aParams['id']
+                ];
+
+                $sWhereClause = "AND `tcd`.`id`=:id";
+                break;
+            
+            case 'content':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'content_type' => $aParams['content_type'],
+                    'content_id' => $aParams['content_id']
+                ];
+
+                $sWhereClause = "AND `tcd`.`content_type`=:content_type AND `tcd`.`content_id`=:content_id";
+                break;
+
+            case 'entry_id':
+                $aMethod['params'][1] = [
+                    'entry_id' => $aParams['entry_id']
+                ];
+
+                $sWhereClause = "AND `tcd`.`entry_id`=:entry_id";
+                break;
+
+            case 'node_id':
+                $aMethod['params'][1] = [
+                    'node_id' => $aParams['node_id']
+                ];
+
+                $sWhereClause = "AND `tcd`.`node_id`=:node_id";
+                $sOrderClause = "`tcd`.`order` ASC";
+                break;
+
+            case 'entry_node_ids':
+                $aMethod['params'][1] = [
+                    'entry_id' => $aParams['entry_id'],
+                    'node_id' => $aParams['node_id']
+                ];
+
+                $sWhereClause = "AND `tcd`.`entry_id`=:entry_id AND `tcd`.`node_id`=:node_id";
+                $sOrderClause = "`tcd`.`order` ASC";
+
+                if(isset($aParams['usage'])) {
+                    $aMethod['params'][1]['usage'] = $aParams['usage'];
+
+                    $sWhereClause .= " AND `tcd`.`usage`=:usage";
+                }
+                break;
+                
+            case 'siblings':
+                $aMethod['name'] = 'getAllWithKey';
+                $aMethod['params'][1] = 'order';
+                $aMethod['params'][2] = [
+                    'entry_id' => $aParams['entry_id'],
+                    'node_id' => $aParams['node_id'],
+                    'usage' => $aParams['usage'],
+                    'order' => $aParams['order']
+                ];
+                $sWhereClause = "AND `tcd`.`entry_id`=:entry_id AND `tcd`.`node_id`=:node_id AND `tcd`.`usage`=:usage AND (`tcd`.`order`=(:order - 1) OR `tcd`.`order`=(:order + 1))";
+                $sOrderClause = "`tcd`.`order` ASC";
+                break;
+
+            case 'user_passed':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'profile_id' => $aParams['profile_id'],
+                    'data_id' => $aParams['data_id']
+                ];
+
+                $sSelectClause = "`tcdu`.*";
+                $sJoinClause = "INNER JOIN `" . $CNF['TABLE_CNT_DATA2USERS'] . "` AS `tcdu` ON `tcd`.`id`=`tcdu`.`data_id` AND `tcdu`.`data_id`=:data_id AND `tcdu`.`profile_id`=:profile_id";
+                break;
+
+            case 'user_track':
+                $aMethod['params'][1] = [
+                    'profile_id' => $aParams['profile_id'],
+                    'entry_id' => $aParams['entry_id'],
+                    'node_id' => $aParams['node_id']
+                ];
+
+                $sJoinClause = "INNER JOIN `" . $CNF['TABLE_CNT_DATA2USERS'] . "` AS `tcdu` ON `tcd`.`id`=`tcdu`.`data_id` AND `tcdu`.`profile_id`=:profile_id";
+                $sWhereClause = "AND `tcd`.`entry_id`=:entry_id AND `tcd`.`node_id`=:node_id AND `tcd`.`usage`='" . BX_COURSES_CND_USAGE_ST . "'";
+                break;
+        }
+
+        if(!empty($sOrderClause))
+            $sOrderClause = "ORDER BY " . $sOrderClause;
+        
+        if(!empty($sLimitClause))
+            $sLimitClause = "LIMIT " . $sLimitClause;
+
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $CNF['TABLE_CNT_DATA'] . "` AS `tcd` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+
+    public function getContentDataOrderMax($iEntryId, $iNodeId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return (int)$this->getOne("SELECT IFNULL(MAX(`order`), 0) FROM `" . $CNF['TABLE_CNT_DATA'] . "` WHERE `entry_id`=:entry_id AND `node_id`=:node_id", [
+            'entry_id' => $iEntryId,
+            'node_id' => $iNodeId
+        ]);
+    }
+
+    public function insertContentData($aParamsSet)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsSet))
+            return false;
+
+        return (int)$this->query("INSERT INTO `" . $CNF['TABLE_CNT_DATA'] . "` SET " . $this->arrayToSQL($aParamsSet)) > 0 ? (int)$this->lastId() : false;
+    }
+
+    public function deleteContentData($aParamsWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsWhere))
+            return false;
+
+        return $this->query("DELETE FROM `" . $CNF['TABLE_CNT_DATA'] . "` WHERE " . $this->arrayToSQL($aParamsWhere, " AND "));
+    }
+
+    public function deleteContentDataWithTracks($iEntryId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return $this->query("DELETE FROM `tcd`, `tcdu` USING `" . $CNF['TABLE_CNT_DATA'] . "` AS `tcd` LEFT JOIN `" . $CNF['TABLE_CNT_DATA2USERS'] . "` AS `tcdu` ON `tcd`.`id`=`tcdu`.`data_id` WHERE `tcd`.`entry_id`=:entry_id", [
+            'entry_id' => $iEntryId
+        ]);
+    }
+
+    public function insertContentData2Users($aParamsSet)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsSet))
+            return false;
+
+        return (int)$this->query("INSERT INTO `" . $CNF['TABLE_CNT_DATA2USERS'] . "` SET " . $this->arrayToSQL($aParamsSet) . " ON DUPLICATE KEY UPDATE `date`=UNIX_TIMESTAMP()") > 0 ? (int)$this->lastId() : false;
+    }
+
+    public function deleteContentData2Users($aParamsWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsWhere))
+            return false;
+
+        return $this->query("DELETE FROM `" . $CNF['TABLE_CNT_DATA2USERS'] . "` WHERE " . $this->arrayToSQL($aParamsWhere, " AND "));
+    }
+}
+
+/** @} */

+ 66 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesFormContentNode.php

@@ -0,0 +1,66 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+class BxCoursesFormContentNode extends BxTemplFormView
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    protected $_iParentId;
+    protected $_iLevel;
+    protected $_iLevelMax;
+    protected $_aLevelMaxInputs;
+
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_courses';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        parent::__construct($aInfo, $oTemplate);
+
+        $this->_iParentId = 0;
+        $this->_iLevel = 0;
+        $this->_iLevelMax = $this->_oModule->_oConfig->getContentLevelMax();
+        $this->_aLevelMaxInputs = ['text', 'passing'];
+        
+        if(isset($this->aInputs['passing']))
+            $this->aInputs['passing']['values'] = [
+                ['key' => BX_COURSES_CND_PASSING_ALL, 'value' => _t('_bx_courses_form_content_node_input_passing_all')],
+                ['key' => BX_COURSES_CND_PASSING_ALTERNATE, 'value' => _t('_bx_courses_form_content_node_input_passing_alternate')]
+            ];
+    }
+
+    public function setData($iParentId, $iLevel = 0)
+    {
+        $this->_iParentId = $iParentId;
+
+        if(!$iLevel) {
+            $aParent = $this->_oModule->_oDb->getContentStructure([
+                'sample' => 'node_id', 
+                'node_id' => $this->_iParentId
+            ]);
+
+            $iLevel = $this->_oModule->getNodeLevelByParent($aParent);
+        }
+
+        $this->_iLevel = $iLevel;
+    }
+
+    public function initChecker($aValues = [], $aSpecificValues = [])
+    {
+        if($this->_iLevel != $this->_iLevelMax)
+            $this->aInputs = array_diff_key($this->aInputs, array_flip($this->_aLevelMaxInputs));
+
+        parent::initChecker ($aValues, $aSpecificValues);
+    }
+}
+
+/** @} */

+ 64 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesFormsEntryHelper.php

@@ -0,0 +1,64 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Group profile forms functions
+ */
+class BxCoursesFormsEntryHelper extends BxBaseModGroupsFormsEntryHelper
+{
+    public function __construct($oModule)
+    {
+        parent::__construct($oModule);
+    }
+
+    public function onDataAddAfter($iAccountId, $iContentId)
+    {
+        if($s = parent::onDataAddAfter($iAccountId, $iContentId))
+            return $s;
+
+        $oGroupProfile = BxDolProfile::getInstanceByContentAndType($iContentId, $this->_oModule->_oConfig->getName());
+        if(!$oGroupProfile)
+            return '';
+
+        $iAdminProfileId = bx_get_logged_profile_id();
+        $aInitialProfiles = bx_get('initial_members');
+        if(!is_array($aInitialProfiles) || !in_array($iAdminProfileId, $aInitialProfiles))
+            $this->makeAuthorAdmin($oGroupProfile, array($iAdminProfileId));
+
+        return '';
+    }
+
+    public function onDataDeleteAfter($iContentId, $aContentInfo, $oProfile)
+    {
+        if($s = parent::onDataDeleteAfter($iContentId, $aContentInfo, $oProfile))
+            return $s;
+
+        $aDataItems = $this->_oModule->_oDb->getContentData([
+            'sample' => 'entry_id',
+            'entry_id' => $iContentId
+        ]);
+
+        if(!empty($aDataItems) && is_array($aDataItems))
+            foreach($aDataItems as $aDataItem)
+                if(($sMethod = 'delete_entity') && bx_is_srv($aDataItem['content_type'], $sMethod))
+                    bx_srv($aDataItem['content_type'], $sMethod, [$aDataItem['content_id']]);
+
+        $this->_oModule->_oDb->deleteContentDataWithTracks($iContentId);
+
+        $this->_oModule->_oDb->deleteContentNodesWithTracks($iContentId);
+
+        $this->_oModule->_oDb->deleteContentStructureNode(['entry_id' => $iContentId]);
+
+        return '';
+    }
+}
+
+/** @} */

+ 239 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesGridCntDataManage.php

@@ -0,0 +1,239 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+class BxCoursesGridCntDataManage extends BxTemplGrid
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    protected $_sPageUrl;
+    protected $_iEntryId;
+    protected $_iEntryPid;
+
+    protected $_iNodeId;
+    protected $_aNodeInfo;
+
+    protected $_iLevelMax;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_courses';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->_iEntryId = 0;
+        if(($iEntryId = bx_get('entry_id')) !== false)
+            $this->setEntryId($iEntryId);
+
+        $this->setNodeId(($iNodeId = bx_get('parent_id')) !== false ? $iNodeId : 0);            
+
+        $this->_iLevelMax = $this->_oModule->_oConfig->getContentLevelMax();
+    }
+
+    public function setEntryId($iEntryId)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->_iEntryId = (int)$iEntryId;
+        $this->_iEntryPid = ($oProfile = BxDolProfile::getInstanceByContentAndType($this->_iEntryId, $this->_sModule)) !== false ? $oProfile->id() : 0;
+
+        $this->_sPageUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_MANAGE_STRUCTURE'], ['profile_id' => $this->_iEntryPid]);
+
+        $this->_aQueryAppend['entry_id'] = $this->_iEntryId;
+    }
+    
+    public function setNodeId($iNodeId)
+    {
+        $this->_iNodeId = (int)$iNodeId;
+
+        $this->_aNodeInfo = [];
+        if($this->_iNodeId)
+            $this->_aNodeInfo = $this->_oModule->_oDb->getContentNodes(['sample' => 'id_full', 'id' => $this->_iNodeId]);
+
+        $this->_aQueryAppend['parent_id'] = $this->_iNodeId;
+    }
+
+    public function getCode ($isDisplayHeader = true)
+    {
+        if(empty($this->_aNodeInfo) || !is_array($this->_aNodeInfo) || $this->_aNodeInfo['level'] != $this->_iLevelMax)
+            return '';
+
+        return parent::getCode($isDisplayHeader);
+    }
+
+    public function getCodeAPI($bForceReturn = false)
+    {
+        if(empty($this->_aNodeInfo) || !is_array($this->_aNodeInfo) || $this->_aNodeInfo['level'] != $this->_iLevelMax)
+            return [];
+
+        return parent::getCodeAPI($bForceReturn);
+    }    
+
+    protected function _getCellContentId($mixedValue, $sKey, $aField, $aRow)
+    {
+        $iContentId = (int)$mixedValue;
+        $mixedValue = '';
+
+        $sModule = $aRow['content_type'];
+        if(($sMethod = 'get_title') && bx_is_srv($sModule, $sMethod))
+            $mixedValue = bx_srv($sModule, $sMethod, [$iContentId]);
+        if(!$mixedValue && ($sMethod = 'get_text') &&  bx_is_srv($sModule, $sMethod))
+            $mixedValue = bx_srv($sModule, $sMethod, [$iContentId]);
+        if(!$mixedValue)
+            $mixedValue = _t('_undefined');
+
+        if(($sMethod = 'get_link') && bx_is_srv($sModule, $sMethod))
+            $mixedValue = $this->_oModule->_oTemplate->parseHtmlByName('name_link.html', [
+                'href' => bx_srv($sModule, $sMethod, [$iContentId]),
+                'title' => bx_html_attribute($mixedValue),
+                'content' => $mixedValue
+            ]);
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellContentType($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(_t('_' . $mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellUsage($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(_t('_bx_courses_txt_data_usage_' . $mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellAdded($mixedValue, $sKey, $aField, $aRow)
+    {
+        if($this->_bIsApi)
+            return ['type' => 'time', 'data' => $mixedValue];
+
+        return parent::_getCellDefault(bx_time_js($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getActionBack($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = [])
+    {
+        if(empty($this->_aNodeInfo))
+            return $this->_bIsApi ? [] : '';
+
+        $sUrl = $this->_sPageUrl;
+        if(!empty($this->_aNodeInfo['parent_id']))
+            $sUrl = bx_append_url_params($sUrl, ['parent_id' => $this->_aNodeInfo['parent_id']]);
+        
+        if($this->_bIsApi)
+            return array_merge($a, ['name' => $sKey, 'type' => 'link', 'link' => bx_api_get_relative_url($sUrl)]);
+
+        $a['attr'] = array_merge($a['attr'], [
+            "onclick" => "window.open('" . $sUrl . "', '_self');"
+    	]);
+
+    	return $this->_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+
+    protected function _getActionAddSt($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = [])
+    {
+        if($this->_bIsApi)
+            return array_merge($a, ['name' => $sKey, 'type' => 'menu', 'action' => $sKey, 'values' => $this->_getValues(BX_COURSES_CND_USAGE_ST)]);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $a['attr'] = array_merge($a['attr'], [
+            "onclick" => "$(this).off('click'); bx_menu_popup('" . $CNF['OBJECT_MENU_CONTENT_ADD'] . "', this, {removeOnClose: 1}, {entry_pid: " . $this->_iEntryPid . ", node_id: " . $this->_iNodeId . ", usage:0});"
+    	]);
+    	return $this->_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+
+    protected function _getActionAddAt($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = [])
+    {
+        if($this->_bIsApi)
+            return array_merge($a, ['name' => $sKey, 'type' => 'menu', 'action' => $sKey, 'values' => $this->_getValues(BX_COURSES_CND_USAGE_AT)]);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $a['attr'] = array_merge($a['attr'], [
+            "onclick" => "$(this).off('click'); bx_menu_popup('" . $CNF['OBJECT_MENU_CONTENT_ADD'] . "', this, {removeOnClose: 1}, {entry_pid: " . $this->_iEntryPid . ", node_id: " . $this->_iNodeId . ", usage:1});"
+    	]);
+    	return $this->_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+
+    protected function _delete($mixedId)
+    {
+        $aData = $this->_oModule->_oDb->getContentData(['sample' => 'id', 'id' => (int)$mixedId]);
+        if(!empty($aData) && is_array($aData)) {
+            $sModule = $aData['content_type'];
+            $sMethod = 'delete_entity';
+            if(bx_is_srv($sModule, $sMethod) && bx_srv($sModule, $sMethod, [$aData['content_id']]) != '')
+                return false;
+        }
+
+        return parent::_delete($mixedId) !== false;
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        if(!$this->_iEntryId)
+            return [];
+
+        $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString(" AND `entry_id`=? AND `node_id`=?", $this->_iEntryId, $this->_iNodeId);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+
+    protected function _getIds()
+    {
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $iId = (int)bx_get('id');
+            if(!$iId) 
+                return false;
+
+            $aIds = [$iId];
+        }
+
+        return $aIds;
+    }
+
+    protected function _getValues($iUsage)
+    {
+        $aValues = [];
+        $oPermalink = BxDolPermalinks::getInstance();
+
+        $aModules = $this->_oModule->_oConfig->getContentModules($iUsage);
+        foreach($aModules as $sModule) {
+            $oModule = BxDolModule::getInstance($sModule);
+            if(!$oModule)
+                continue;
+
+            $CNF = &$oModule->_oConfig->CNF;
+            if(!isset($CNF['URI_ADD_ENTRY']))
+                continue;
+
+            $aParams = [
+                'context_pid' => $this->_iEntryPid,
+                'context_nid' => $this->_iNodeId,
+                'context_usage' => $iUsage
+            ];
+
+            $aValues[] = [
+                'key' => $sModule, 
+                'title' => _t('_' . $sModule),
+                'link' => bx_api_get_relative_url(BX_DOL_URL_ROOT . $oPermalink->permalink('page.php?i=' . $CNF['URI_ADD_ENTRY'], $aParams)), 
+                'callback' => $sModule . '/entity_create/&params[]=' . json_encode($aParams)
+            ];
+        }
+
+        return $aValues;
+    }
+}
+
+/** @} */

+ 420 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesGridCntStructureManage.php

@@ -0,0 +1,420 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+class BxCoursesGridCntStructureManage extends BxTemplGrid
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    protected $_sPageUrl;
+    protected $_iEntryId;
+    protected $_iEntryPid;
+
+    protected $_iParentId;
+    protected $_aParentInfo;
+
+    protected $_iLevel;
+    protected $_iLevelMax;
+    protected $_aLevelToNode;
+    protected $_aLevelToNodePl;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_courses';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        parent::__construct ($aOptions, $oTemplate);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->_iEntryId = 0;
+        if(($iEntryId = bx_get('entry_id')) !== false)
+            $this->setEntryId($iEntryId);
+
+        $this->setParentId(($iParentId = bx_get('parent_id')) !== false ? $iParentId : 0);            
+
+        $this->_iLevel = $this->_getNodeLevel();
+        $this->_iLevelMax = $this->_oModule->_oConfig->getContentLevelMax();
+        $this->_aLevelToNode = $this->_oModule->_oConfig->getContentLevel2Node();
+        $this->_aLevelToNodePl = $this->_oModule->_oConfig->getContentLevel2Node(false);
+    }
+
+    public function setEntryId($iEntryId)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->_iEntryId = (int)$iEntryId;
+        $this->_iEntryPid = ($oProfile = BxDolProfile::getInstanceByContentAndType($this->_iEntryId, $this->_sModule)) !== false ? $oProfile->id() : 0;
+
+        $this->_sPageUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_MANAGE_STRUCTURE'], ['profile_id' => $this->_iEntryPid]);
+
+        $this->_aQueryAppend['entry_id'] = $this->_iEntryId;
+    }
+
+    public function setParentId($iParentId)
+    {
+        $this->_iParentId = (int)$iParentId;
+
+        $this->_aParentInfo = [];
+        if($this->_iParentId)
+            $this->_aParentInfo = $this->_oModule->_oDb->getContentNodes(['sample' => 'id_full', 'id' => $this->_iParentId]);
+
+        $this->_aQueryAppend['parent_id'] = $this->_iParentId;
+    }
+
+    public function getCode ($isDisplayHeader = true)
+    {
+        if($this->_iLevel > $this->_iLevelMax)
+            return '';
+
+        return parent::getCode($isDisplayHeader);
+    }
+
+    public function getFormBlockTitleAPI($sAction, $iId = 0)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sResult = '';
+
+        switch($sAction) {
+            case 'add':
+                $sResult = $CNF['T']['popup_title_content_node_add'];
+                break;
+
+            case 'edit':
+                $sResult = $CNF['T']['popup_title_content_node_edit'];
+                break;
+        }
+
+        return $this->_parseNodeName(_t($sResult));
+    }
+
+    public function getFormCallBackUrlAPI($sAction, $iId = 0)
+    {
+         return '/api.php?r=system/perfom_action_api/TemplServiceGrid/&params[]=&o=' . $this->_sObject . '&a=' . $sAction . '&entry_id=' . $this->_iEntryId . '&parent_id=' . $this->_iParentId . '&id=' . $iId;
+    }
+
+    public function getCodeAPI($bForceReturn = false)
+    {
+        if($this->_iLevel > $this->_iLevelMax)
+            return [];
+
+        return parent::getCodeAPI($bForceReturn);
+    }
+
+    protected function _getActionsAPI ($sType)
+    {
+        $aResult = parent::_getActionsAPI($sType);
+
+        if($aResult && $sType == 'independent') {
+            if(!empty($aResult['add']))
+                $aResult['add']['title'] = $this->_parseNodeName($aResult['add']['title']);
+        }
+
+        return $aResult;
+    }
+
+    public function performActionAdd()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	$sAction = 'add';
+
+        $aEntryInfo = $this->_oModule->_oDb->getContentInfoById($this->_iEntryId);
+        if(($mixedResult = $this->_oModule->checkAllowedEdit($aEntryInfo)) !== CHECK_ACTION_RESULT_ALLOWED)
+            return $this->_getActionResult(['msg' => $mixedResult]);
+
+        $sForm = $CNF['OBJECT_FORM_CNT_NODE_DISPLAY_ADD'];
+    	$oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_CNT_NODE'], $CNF['OBJECT_FORM_CNT_NODE_DISPLAY_ADD']);
+    	$oForm->setId($sForm);
+        $oForm->setName($sForm);
+        $oForm->setAction(BX_DOL_URL_ROOT . bx_append_url_params('grid.php', ['o' => $this->_sObject, 'a' => $sAction, 'entry_id' => $this->_iEntryId, 'parent_id' => $this->_iParentId]));
+        $oForm->setData($this->_iParentId, $this->_iLevel);
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+
+            $iId = (int)$oForm->insert(['entry_id' => $this->_iEntryId, 'added' => time()]);
+            if($iId != 0) {
+                $this->_oModule->_oDb->insertContentStructureNode([
+                    'entry_id' => $this->_iEntryId,
+                    'parent_id' => $this->_iParentId,
+                    'node_id' => $iId,
+                    'level' => $this->_iLevel,
+                    'order' => $this->_oModule->_oDb->getContentStructureOrderMax($this->_iEntryId, $this->_iParentId) + 1
+                ]);
+
+                if($this->_iLevel > 1) 
+                    $this->_oModule->_oDb->updateContentStructureCounters($this->_iParentId, $this->_iLevel, 1);
+
+                $aResult = $this->_bIsApi ? [] : ['grid' => $this->getCode(false), 'blink' => $iId];
+            }
+            else
+                $aResult = ['msg' => _t($CNF['T']['err_cannot_perform'])];
+
+            return $this->_getActionResult($aResult);
+        }
+
+        if($this->_bIsApi)
+            return $this->getFormBlockAPI($oForm, $sAction);
+
+        bx_import('BxTemplFunctions');
+        $sContent = BxTemplFunctions::getInstance()->popupBox($this->_oModule->_oConfig->getHtmlIds('popup_content_node'), $this->_parseNodeName(_t($CNF['T']['popup_title_content_node_add'])), $this->_oModule->_oTemplate->parseHtmlByName('popup_content_node.html', [
+            'form_id' => $oForm->getId(),
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+    }
+
+    public function performActionEdit()
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sAction = 'edit';
+
+        $aEntryInfo = $this->_oModule->_oDb->getContentInfoById($this->_iEntryId);
+        if(($mixedResult = $this->_oModule->checkAllowedEdit($aEntryInfo)) !== CHECK_ACTION_RESULT_ALLOWED)
+            return $this->_getActionResult(['msg' => $mixedResult]);
+
+        $aIds = $this->_getIds();
+        if($aIds === false)
+            return $this->_getActionResult([]);
+
+        $aNode = $this->_oModule->_oDb->getContentNodes(['sample' => 'id', 'id' => array_shift($aIds)]);
+        if(!is_array($aNode) || empty($aNode))
+            return $this->_getActionResult([]);
+
+        $sForm = $CNF['OBJECT_FORM_CNT_NODE_DISPLAY_EDIT'];
+        $oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_CNT_NODE'], $CNF['OBJECT_FORM_CNT_NODE_DISPLAY_EDIT']);
+        $oForm->setId($sForm);
+        $oForm->setName($sForm);
+    	$oForm->setAction(BX_DOL_URL_ROOT . bx_append_url_params('grid.php', ['o' => $this->_sObject, 'a' => $sAction, 'entry_id' => $this->_iEntryId, 'parent_id' => $this->_iParentId, 'id' => $aNode['id']]));
+        $oForm->setData($this->_iParentId);
+
+        $oForm->initChecker($aNode);
+        if($oForm->isSubmittedAndValid()) {
+            if($oForm->update($aNode['id']) !== false)
+                $aResult = $this->_bIsApi ? [] : ['grid' => $this->getCode(false), 'blink' => $aNode['id']];
+            else
+                $aResult = ['msg' => _t($CNF['T']['err_cannot_perform'])];
+
+            return $this->_getActionResult($aResult);
+        }
+
+        if($this->_bIsApi)
+            return $this->getFormBlockAPI($oForm, $sAction, $aNode['id']);
+
+        bx_import('BxTemplFunctions');
+        $sContent = BxTemplFunctions::getInstance()->popupBox($this->_oModule->_oConfig->getHtmlIds('popup_content_node'), $this->_parseNodeName(_t($CNF['T']['popup_title_content_node_edit'])), $this->_oModule->_oTemplate->parseHtmlByName('popup_content_node.html', [
+            'form_id' => $oForm->getId(),
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        return echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+    }
+
+    protected function _getCellHeaderCnL2($sKey, $aField)
+    {
+        if($this->_iLevelMax < 2 || $this->_iLevel >= 2)
+            return $this->_bIsApi ? [] : '';
+
+        $aField['title'] = ucfirst($this->_aLevelToNodePl[2]);
+        return parent::_getCellHeaderDefault($sKey, $aField);
+    }
+
+    protected function _getCellHeaderCnL3($sKey, $aField)
+    {
+        if($this->_iLevelMax < 3 || $this->_iLevel >= 3)
+            return $this->_bIsApi ? [] : '';
+
+        $aField['title'] = ucfirst($this->_aLevelToNodePl[3]);
+        return parent::_getCellHeaderDefault($sKey, $aField);
+    }
+
+    protected function _getCellHeaderCounters($sKey, $aField)
+    {
+        if($this->_iLevel != $this->_iLevelMax)
+            return $this->_bIsApi ? [] : '';
+
+        return parent::_getCellHeaderDefault($sKey, $aField);
+    }
+
+    protected function _getCellTitle($mixedValue, $sKey, $aField, $aRow)
+    {
+        $sLink = bx_append_url_params($this->_sPageUrl, ['parent_id' => $aRow['id']]);
+
+        if($this->_bIsApi)
+            return ['type' => 'link', 'data' => [
+                'text' => $mixedValue,
+                'url' => bx_api_get_relative_url($sLink)
+            ]];
+
+        $mixedValue = $this->_oModule->_oTemplate->parseHtmlByName('name_link.html', [
+            'href' => $sLink,
+            'title' => bx_html_attribute($mixedValue),
+            'content' => $mixedValue
+        ]);
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+    
+    protected function _getCellCnL2($mixedValue, $sKey, $aField, $aRow)
+    {
+        if($this->_iLevelMax < 2 || $this->_iLevel >= 2)
+            return $this->_bIsApi ? [] : '';
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+    
+    protected function _getCellCnL3($mixedValue, $sKey, $aField, $aRow)
+    {
+        if($this->_iLevelMax < 3 || $this->_iLevel >= 3)
+            return $this->_bIsApi ? [] : '';
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+    
+    protected function _getCellCounters($mixedValue, $sKey, $aField, $aRow)
+    {
+        if($this->_iLevel != $this->_iLevelMax)
+            return $this->_bIsApi ? [] : '';
+
+        $mixedCounters = '';
+        if(!empty($mixedValue) && ($aCounters = json_decode(html_entity_decode($mixedValue), true)))
+            $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) . ' ';
+            }
+
+            return ['type' => 'text', 'value' => trim($sResult)];
+        }
+
+        return parent::_getCellDefault($mixedCounters, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellAdded($mixedValue, $sKey, $aField, $aRow)
+    {
+        if($this->_bIsApi)
+            return ['type' => 'time', 'data' => $mixedValue];
+
+        return parent::_getCellDefault(bx_time_js($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getActionBack($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = [])
+    {
+        if(empty($this->_aParentInfo))
+            return $this->_bIsApi ? [] : '';
+
+        $sUrl = $this->_sPageUrl;
+        if(!empty($this->_aParentInfo['parent_id']))
+            $sUrl = bx_append_url_params($sUrl, ['parent_id' => $this->_aParentInfo['parent_id']]);
+
+        if($this->_bIsApi)
+            return array_merge($a, ['name' => $sKey, 'type' => 'link', 'link' => bx_api_get_relative_url($sUrl)]);
+
+        $a['attr'] = array_merge($a['attr'], [
+            "onclick" => "window.open('" . $sUrl . "','_self');"
+    	]);
+
+    	return $this->_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+
+    protected function _getActionAdd($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = [])
+    {
+        if($this->_iLevel > $this->_iLevelMax)
+            return '';
+
+        $a['title'] = $this->_parseNodeName($a['title']);
+    	return $this->_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+
+    protected function _delete($mixedId)
+    {
+        if($this->_iLevel > 1) 
+            $this->_oModule->_oDb->updateContentStructureCounters($this->_iParentId, $this->_iLevel, -1);
+
+        $this->_oModule->_oDb->deleteContentStructureNode(['node_id' => (int)$mixedId]);
+
+        return parent::_delete($mixedId);
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        if(!$this->_iEntryId)
+            return [];
+
+        $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString(" AND `tcn`.`entry_id`=? AND `tcs`.`parent_id`=?", $this->_iEntryId, $this->_iParentId);
+
+        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';
+    }
+
+    protected function _switcherState2Checked($mixedState)
+    {
+        return 'active' == $mixedState ? true : false;
+    }
+
+    protected function _getNodeLevel()
+    {
+        return $this->_oModule->getNodeLevelByParent($this->_aParentInfo);
+    }
+    
+    protected function _parseNodeName($s)
+    {
+        return bx_replace_markers($s, [
+            'node' => isset($this->_aLevelToNode[$this->_iLevel]) ? $this->_aLevelToNode[$this->_iLevel]: _t('_undefined')
+        ]);
+    }
+    protected function _getIds()
+    {
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $iId = (int)bx_get('id');
+            if(!$iId) 
+                return false;
+
+            $aIds = [$iId];
+        }
+
+        return $aIds;
+    }
+}
+
+/** @} */

+ 78 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesMenuContentAdd.php

@@ -0,0 +1,78 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxCoursesMenuContentAdd extends BxTemplMenu
+{
+    protected $_sModule;
+    protected $_oModule;
+    
+    protected $_iEntryPid;
+    protected $_iNodeId;
+    protected $_iUsage;
+
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_courses';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        parent::__construct($aObject, $oTemplate);
+
+        $this->_iEntryPid = 0;
+        if(($iEntryPid = bx_get('entry_pid')) !== false)
+            $this->_iEntryPid = (int)$iEntryPid;
+
+        $this->_iNodeId = 0;
+        if(($iNodeId = bx_get('node_id')) !== false)
+            $this->_iNodeId = (int)$iNodeId;
+        
+        $this->_iUsage = 0;
+        if(($iUsage = bx_get('usage')) !== false)
+            $this->_iUsage = (int)$iUsage;
+    }
+
+    protected function getMenuItemsRaw()
+    {
+        $aResults = [];
+
+        $oPermalink = BxDolPermalinks::getInstance();
+
+        $aModules = $this->_oModule->_oConfig->getContentModules($this->_iUsage);
+        foreach($aModules as $sModule) {
+            $oModule = BxDolModule::getInstance($sModule);
+            if(!$oModule)
+                continue;
+
+            $CNF = &$oModule->_oConfig->CNF;
+            if(!isset($CNF['URI_ADD_ENTRY']))
+                continue;
+
+            $sUrl = $oPermalink->permalink('page.php?i=' . $CNF['URI_ADD_ENTRY'], [
+                'context_pid' => $this->_iEntryPid,
+                'context_nid' => $this->_iNodeId,
+                'context_usage' => $this->_iUsage
+            ]);
+
+            $aResults[] = [
+                'id' => $sModule, 
+                'name' => $sModule, 
+                'class' => '', 
+                'link' => $sUrl, 
+                'onclick' => "$(this).parents('.bx-popup-applied:visible:first').dolPopupHide()",
+                'target' => '_self', 
+                'title' => _t('_' . $sModule)
+            ];
+        }
+
+        return $aResults;
+    }
+}
+
+/** @} */

+ 102 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesMenuSnippetMeta.php

@@ -0,0 +1,102 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxCoursesMenuSnippetMeta extends BxBaseModGroupsMenuSnippetMeta
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_courses';
+
+        parent::__construct($aObject, $oTemplate);
+
+        unset($this->_aConnectionToFunctionCheck['sys_profiles_friends']);
+    }
+
+    protected function _getMenuItemReports($aItem, $aParams = [])
+    {
+        $bShowAsObject = isset($aParams['show_as_object']) && (bool)$aParams['show_as_object'] === true;
+
+        if($bShowAsObject || $this->_bIsApi)  //--- API: Object base votes are used by default
+            return $this->_getMenuItemReportObject($aItem, $aParams);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(empty($CNF['FIELD_REPORTS']) || (empty($this->_aContentInfo[$CNF['FIELD_REPORTS']]) && !$this->_bShowZeros))
+            return false;
+
+        $sTitle = _t('_report_n_reports', $this->_aContentInfo[$CNF['FIELD_REPORTS']]);
+
+        if($this->_bIsApi)
+            return $this->_getMenuItemAPI($aItem, 'text', [
+                'title' => $sTitle
+            ]);
+
+        return $this->getUnitMetaItemText($sTitle);
+    }
+
+    protected function _getMenuItemReportObject($aItem, $aParams = [])
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(empty($CNF['OBJECT_REPORTS']))
+            return false;
+
+        $oObject = BxDolReport::getObjectInstance($CNF['OBJECT_REPORTS'], $this->_aContentInfo[$CNF['FIELD_ID']]);
+        if(!$oObject || !$oObject->isEnabled())
+            return false;
+
+        $aObjectOptions = [
+            'show_do_report_as_button' => true,
+            'show_do_report_label' => true,
+            'show_counter' => true
+        ];
+        if(!empty($aParams['object_options']) && is_array($aParams['object_options']))
+            $aObjectOptions = array_merge($aObjectOptions, $aParams['object_options']);
+
+        if($this->_bIsApi)
+            return $this->_getMenuItemElementAPI($aItem, $oObject->getElementApi($aObjectOptions));
+
+        return $this->getUnitMetaItemCustom($oObject->getElementInline($aObjectOptions));
+    }
+
+    protected function _getMenuItemPass($aItem, $aParams = [])
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $iProfileId = bx_get_logged_profile_id();
+        if(!$this->_oContentProfile || !$this->_oModule->serviceIsFan($this->_oContentProfile->id(), $iProfileId))
+            return false;
+
+        list($iPassPercent, $aPassDetails, $sPassStatus, $sPassTitle) = $this->_oModule->getEntryPass($iProfileId, $this->_iContentId);
+        if(empty($sPassTitle))
+            return false;
+
+        $sLink = bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'], [
+            'id' => $this->_iContentId
+        ]));
+
+        if($this->_bIsApi)
+            return $this->_getMenuItemAPI($aItem, ['display' => 'button'], [
+                'title' => $sPassTitle,
+                'link' => bx_api_get_relative_url($sLink)
+            ]);
+
+        return [
+            $this->getUnitMetaItemButton($sPassTitle, [
+                'class' => !empty($aItem['primary']) ? 'bx-btn-primary' : '',
+                'href' => $sLink
+            ]),
+            'bx-menu-item-button'
+        ];
+    }
+}
+
+/** @} */

+ 680 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesModule.php

@@ -0,0 +1,680 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * How to pass a lesson's steps:
+ * 0 - all at once, the order is not important
+ * 1 - alternate, the order is important
+ */
+define('BX_COURSES_CND_PASSING_ALL', 0);
+define('BX_COURSES_CND_PASSING_ALTERNATE', 1);
+
+/**
+ * Content data usage:
+ * st - step,
+ * at - attachment
+ */
+define('BX_COURSES_CND_USAGE_ST', 0);
+define('BX_COURSES_CND_USAGE_AT', 1);
+
+/**
+ * Courses profiles module.
+ */
+class BxCoursesModule extends BxBaseModGroupsModule
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_aSearchableNamesExcept[] = $this->_oConfig->CNF['FIELD_JOIN_CONFIRMATION'];
+    }
+
+    public function actionPassNode()
+    {
+        $iNodeId = bx_get('node_id');
+        if($iNodeId === false)
+            return echoJson([]);
+
+        return echoJson($this->servicePassNode((int)$iNodeId));
+    }
+
+    public function actionPassData()
+    {
+        $iDataId = bx_get('data_id');
+        if($iDataId === false)
+            return echoJson([]);
+
+        return echoJson($this->servicePassData((int)$iDataId));
+    }
+
+    public function serviceGetSafeServices()
+    {
+        return array_merge(parent::serviceGetSafeServices(), [
+            'PassNode' => '',
+            'PassData' => '',
+        ]);
+    }
+
+    public function servicePassNode($iNodeId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aNode = $this->_oDb->getContentNodes([
+            'sample' => 'id',
+            'id' => $iNodeId
+        ]);
+
+        if(empty($aNode) || !is_array($aNode))
+            return ['code' => 1];
+
+        $iEntryId = (int)$aNode['entry_id'];
+        $iProfileId = bx_get_logged_profile_id();
+        if($this->isNodePassed($iProfileId, $iNodeId)) {
+            $aDataItems = $this->_oDb->getContentData([
+                'sample' => 'entry_node_ids', 
+                'entry_id' => $iEntryId,
+                'node_id' => $iNodeId,
+            ]);
+
+            if(!empty($aDataItems) && is_array($aDataItems))
+                foreach($aDataItems as $aDataItem)
+                    $this->_oDb->deleteContentData2Users(['data_id' => $aDataItem['id'], 'profile_id' => $iProfileId]);
+
+            $this->_oDb->deleteContentNodes2Users(['node_id' => $iNodeId, 'profile_id' => $iProfileId]);
+        }
+
+        $sLink = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY_NODE'] . '&id=' . $iEntryId, [
+            'node_id' => $iNodeId
+        ]);
+
+        return [
+            'code' => 0, 
+            'redirect' => $this->_bIsApi ? bx_api_get_relative_url($sLink) : $sLink
+        ];
+    }
+
+    public function servicePassData($iDataId)
+    {
+        $iNow = time();
+        $iProfileId = bx_get_logged_profile_id();
+        
+        $aData = $this->_oDb->getContentData([
+            'sample' => 'id',
+            'id' => $iDataId
+        ]);
+        
+        if(empty($aData) || !is_array($aData))
+            return ['code' => 1];
+
+        $this->_oDb->insertContentData2Users([
+            'data_id' => $iDataId, 
+            'profile_id' => $iProfileId, 
+            'date' => $iNow
+        ]);
+
+        $aNode = $this->_oDb->getContentNodes([
+            'sample' => 'id_full',
+            'id' => $aData['node_id']
+        ]);
+
+        $iTotal = $this->getDataTotalByNode($aNode, BX_COURSES_CND_USAGE_ST);
+
+        $aUserTrack = $this->_oDb->getContentData([
+            'sample' => 'user_track', 
+            'entry_id' => $aNode['entry_id'], 
+            'node_id' => $aNode['id'], 
+            'profile_id' => $iProfileId
+        ]);
+
+        if(count($aUserTrack) == $iTotal) {
+            $this->_oDb->insertContentNodes2Users([
+                'node_id' => $aNode['id'], 
+                'profile_id' => $iProfileId, 
+                'date' => $iNow
+            ]);
+            
+            $this->_autoPassNodes($iProfileId, $aNode['parent_id']);
+        }
+
+        $aResult = ['code' => 0];
+        if(($sMethod = 'get_view') && bx_is_srv($aData['content_type'], $sMethod)) {
+            $sView = bx_srv($aData['content_type'], $sMethod, [$aData['content_id']]);
+            $aResult = $this->_oTemplate->entryData($aData, $sView);
+        }
+        else if(($sMethod = 'get_link') && bx_is_srv($aData['content_type'], $sMethod))
+            $aResult['redirect'] = bx_srv($aData['content_type'], $sMethod, [$aData['content_id']]);
+
+        if($this->_bIsApi)
+            $aResult['redirect'] = bx_api_get_relative_url($aResult['redirect']);
+
+        return $aResult;
+    }
+    
+    protected function _autoPassNodes($iProfileId, $iNodeId)
+    {
+        $aNode = $this->_oDb->getContentStructure([
+            'sample' => 'node_id', 
+            'node_id' => $iNodeId, 
+        ]);
+
+        if(empty($aNode) || !is_array($aNode))
+            return;
+
+        $iTotal = (int)$aNode['cn_l' . ($aNode['level'] + 1)];
+        if(!$iTotal)
+            return;
+
+        $aUserTrack = $this->_oDb->getContentStructure([
+            'sample' => 'user_track',
+            'profile_id' => $iProfileId,
+            'entry_id' => $aNode['entry_id'],
+            'node_id' => $iNodeId
+        ]);
+
+        if(count($aUserTrack) == $iTotal) {
+            $this->_oDb->insertContentNodes2Users([
+                'node_id' => $iNodeId, 
+                'profile_id' => $iProfileId, 
+                'date' => time()
+            ]);
+
+            if((int)$aNode['parent_id'] != 0)
+                $this->_autoPassNodes($iProfileId, $aNode['parent_id']);
+        }
+    }
+
+    public function serviceGetOptionsContentModulesSt()
+    {
+        return $this->_getOptionsContentModules();
+    }
+
+    public function serviceGetOptionsContentModulesAt()
+    {
+        return $this->_getOptionsContentModules();
+    }
+
+    public function serviceIsContentAvaliable($iProfileId)
+    {
+        if(!$this->_oConfig->isContent())
+            return false;
+
+        $aContentInfo = $this->_oDb->getContentInfoByProfileId($iProfileId);
+        if($this->checkAllowedEdit($aContentInfo) !== CHECK_ACTION_RESULT_ALLOWED)
+            return false;
+
+        return true;
+    }
+
+    public function serviceOnContentAdded($sContentType, $iContentId, $iContextId, $iContextNodeId, $iContextUsage)
+    {
+        $this->_oDb->insertContentData([
+            'entry_id' => $iContextId,
+            'node_id' => $iContextNodeId,
+            'content_type' => $sContentType,
+            'content_id' => $iContentId,
+            'usage' => $iContextUsage,
+            'added' => time(),
+            'order' => $this->_oDb->getContentDataOrderMax($iContextId, $iContextNodeId) + 1
+        ]);
+
+        $aNode = $this->_oDb->getContentNodes(['sample' => 'id', 'id' => $iContextNodeId]);
+
+        $aCounters = [];
+        if(!empty($aNode['counters']))
+            $aCounters = json_decode($aNode['counters'], true);
+
+        $sUsage = $this->_oConfig->getUsageI2S($iContextUsage);
+        if(!isset($aCounters[$sUsage]))
+            $aCounters[$sUsage] = [];
+
+        if(!isset($aCounters[$sUsage][$sContentType]))
+            $aCounters[$sUsage][$sContentType] = 0;
+        $aCounters[$sUsage][$sContentType] += 1;
+
+        $this->_oDb->updateContentNodes(['counters' => json_encode($aCounters)], ['id' => $iContextNodeId]);
+    }
+
+    public function serviceOnContentAddedRedirect($sContentType, $iContentId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aData = $this->_oDb->getContentData(['sample' => 'content', 'content_type' => $sContentType, 'content_id' => $iContentId]);
+        if(empty($aData) || !is_array($aData))
+            return false;
+
+        $sUrl = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_EDIT_ENTRY_CONTENT'] . '&id=' . $aData['entry_id'], [
+            'parent_id' => $aData['node_id']
+        ]);
+
+        return $this->_bIsApi ? '' : $sUrl;
+    }
+
+    public function serviceOnContentDeleted($sContentType, $iContentId, $iContextId)
+    {
+        $aData = $this->_oDb->getContentData(['sample' => 'content', 'content_type' => $sContentType, 'content_id' => $iContentId]);
+        if(empty($aData) || !is_array($aData))
+            return;
+
+        $this->_oDb->deleteContentData([
+            'entry_id' => $iContextId,
+            'content_type' => $sContentType,
+            'content_id' => $iContentId,
+        ]);
+
+        $this->_oDb->deleteContentData2Users([
+            'data_id' => $aData['id']
+        ]);
+
+        $aNode = $this->_oDb->getContentNodes(['sample' => 'id', 'id' => $aData['node_id']]);
+        if(!empty($aNode['counters'])) {
+            $sUsage = $this->_oConfig->getUsageI2S($aData['usage']);
+            $aCounters = json_decode($aNode['counters'], true);
+
+            if(!empty($aCounters[$sUsage][$sContentType])) {
+                if((int)$aCounters[$sUsage][$sContentType] > 1)
+                    $aCounters[$sUsage][$sContentType] -= 1;
+                else
+                    unset($aCounters[$sUsage][$sContentType]);
+
+                $this->_oDb->updateContentNodes(['counters' => json_encode($aCounters)], ['id' => $aData['node_id']]);
+            }
+        }
+    }
+
+    public function serviceEntityContentStructureBlock($iProfileId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oConfig->isContent())
+            return '';
+
+        if(!$iProfileId)
+            $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
+        if(!$iProfileId)
+            return '';
+
+        $aContentInfo = $this->_oDb->getContentInfoByProfileId($iProfileId);
+
+        $sGridKey = 'OBJECT_GRID_CNT_STRUCTURE_' . ($this->checkAllowedEdit($aContentInfo) === CHECK_ACTION_RESULT_ALLOWED ? 'MANAGE' : 'VIEW');
+        $oGrid = BxDolGrid::getObjectInstance($CNF[$sGridKey]);
+        if(!$oGrid)
+            return '';
+
+        $oGrid->setEntryId($aContentInfo[$CNF['FIELD_ID']]);
+
+        if($this->_bIsApi)
+            return ($aGrid = $oGrid->getCodeAPI()) ? [
+                bx_api_get_block('grid', $aGrid)
+            ] : [];
+
+        return $oGrid->getCode();
+    }
+    
+    public function serviceEntityContentDataBlock($iProfileId = 0)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oConfig->isContent())
+            return '';
+
+        if(!$iProfileId)
+            $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
+        if(!$iProfileId)
+            return '';
+
+        $aContentInfo = $this->_oDb->getContentInfoByProfileId($iProfileId);
+
+        $sGridKey = 'OBJECT_GRID_CNT_DATA_' . ($this->checkAllowedEdit($aContentInfo) === CHECK_ACTION_RESULT_ALLOWED ? 'MANAGE' : 'VIEW');
+        $oGrid = BxDolGrid::getObjectInstance($CNF[$sGridKey]);
+        if(!$oGrid)
+            return '';
+
+        $oGrid->setEntryId($aContentInfo[$CNF['FIELD_ID']]);
+
+        if($this->_bIsApi)
+            return ($aGrid = $oGrid->getCodeAPI()) ? [
+                bx_api_get_block('grid', $aGrid)
+            ] : [];
+
+        return $oGrid->getCode();
+    }
+    
+    public function serviceEntityStructureL1Block($iContentId = 0)
+    {
+        if(!$this->_oConfig->isContent() || $this->_oConfig->getContentLevelMax() == 1)
+            return $this->_bIsApi ? [] : '';
+
+        $mixedResult = $this->_serviceTemplateFuncEx ('entryStructureByLevel', $iContentId, [
+            '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 : 2,
+        ]);
+
+        return $this->_bIsApi ? [bx_api_get_block('course_structure', $mixedResult)] : $mixedResult;
+    }
+
+    public function serviceEntityStructureL2Block($iContentId = 0, $iParentId = 0)
+    {
+        if(!$this->_oConfig->isContent())
+            return $this->_bIsApi ? [] : '';
+
+        if(!$iParentId && ($_iParentId = bx_get('parent_id')) !== false)
+            $iParentId = (int)$_iParentId;
+
+        $mixedResult = $this->_serviceTemplateFuncEx ('entryStructureByParentMl' . $this->_oConfig->getContentLevelMax(), $iContentId, [
+            'parent_id' => $iParentId
+        ]);
+
+        return $this->_bIsApi ? [bx_api_get_block('module_structure', $mixedResult)] : $mixedResult;
+    }
+    
+    public function serviceEntityNodeBlock($iContentId = 0, $iNodeId = 0, $iUsage = false)
+    {
+        if(!$this->_oConfig->isContent())
+            return $this->_bIsApi ? [] : '';
+
+        if(!$iNodeId && ($_iNodeId = bx_get('node_id')) !== false)
+            $iNodeId = (int)$_iNodeId;
+
+        if($iUsage === false && ($_iUsage = bx_get('usage')) !== false)
+            $iUsage = (int)$_iUsage;
+
+        $mixedResult = $this->_serviceTemplateFuncEx('entryNode', $iContentId, [
+            'node_id' => $iNodeId,
+            'usage' => $iUsage
+        ]);
+
+        return $this->_bIsApi ? [bx_api_get_block('lesson_structure', $mixedResult)] : $mixedResult;
+    }
+
+    public function getNodeLevelByParent($aParentInfo)
+    {
+        return (is_array($aParentInfo) && !empty($aParentInfo['level']) ? (int)$aParentInfo['level'] : 0) + 1;
+    }
+    
+    public function getDataTotalByNode($mixedNode, $iUsage = 0)
+    {
+        if(!is_array($mixedNode))
+            $mixedNode = $this->_oDb->getContentNodes([
+                'sample' => 'id', 
+                'id' => (int)$mixedNode
+            ]);
+        
+        $iTotal = 0;
+        $sUsage = $this->_oConfig->getUsageI2S($iUsage);
+        if(!empty($mixedNode['counters']) && ($aCountes = json_decode($mixedNode['counters'], true)) && !empty($aCountes[$sUsage]) && is_array($aCountes[$sUsage]))
+            $iTotal = array_sum($aCountes[$sUsage]);
+
+        return $iTotal;
+    }
+
+    public function isNodeStarted($iProfileId, $mixedNode)
+    {
+        $iNodeId = is_array($mixedNode) ? $mixedNode['id'] : (int)$mixedNode;
+
+        $aChildNodes = $this->_oDb->getContentStructure([
+            'sample' => 'parent_id',
+            'parent_id' => $iNodeId
+        ]);
+
+        if(!empty($aChildNodes) && is_array($aChildNodes)) {
+            foreach($aChildNodes as $aChildNode)
+                if($this->isNodePassed($iProfileId, $aChildNode['node_id']))
+                    return true;
+                else
+                    return $this->isNodeStarted($iProfileId, $aChildNode['node_id']);
+        }
+        else {
+            $aChildData = $this->_oDb->getContentData([
+                'sample' => 'node_id',
+                'node_id' => $iNodeId
+            ]);
+
+            if(!empty($aChildData) && is_array($aChildData))
+                foreach($aChildData as $aChildData)
+                    if($this->isDataPassed($iProfileId, $aChildData['id']))
+                        return true;
+
+            return false;
+        }
+    }
+
+    public function isNodePassed($iProfileId, $mixedNode)
+    {
+        $aUserTrack = $this->_oDb->getContentStructure([
+            'sample' => 'user_passed', 
+            'profile_id' => $iProfileId, 
+            'node_id' => is_array($mixedNode) ? $mixedNode['id'] : (int)$mixedNode
+        ]);
+
+        return !empty($aUserTrack) && is_array($aUserTrack);
+    }
+    
+    public function isDataPassed($iProfileId, $mixedData)
+    {
+        $aUserTrack = $this->_oDb->getContentData([
+            'sample' => 'user_passed', 
+            'profile_id' => $iProfileId, 
+            'data_id' => is_array($mixedData) ? $mixedData['id'] : (int)$mixedData
+        ]);
+
+        return !empty($aUserTrack) && is_array($aUserTrack);
+    }
+
+    public function getEntryPass($iProfileId, $iContentId)
+    {
+        $aChildren = $this->_oDb->getContentStructure([
+            'sample' => 'entry_id', 
+            'entry_id' => $iContentId,
+            'level' => 1
+        ]);
+
+        $aPassDetails = [];
+        foreach($aChildren as $aChild)
+            $this->getNodePass($iProfileId, $iContentId, $aChild, $aPassDetails);
+
+        $iPassPercent = 0;
+        $sPassStatus = '_bx_courses_txt_status_not_started';
+        $sPassTitle = '_bx_courses_txt_pass_start';
+        $iLevelMax = $this->_oConfig->getContentLevelMax();
+        if(isset($aPassDetails[$iLevelMax]) && ($aPassLevelMax = $aPassDetails[$iLevelMax])) {
+            $iPassPercent = ($iTotal = (int)$aPassLevelMax['total']) != 0 ? (int)round(100 * $aPassLevelMax['passed']/$iTotal) : 0;
+
+            if($aPassLevelMax['passed'] != 0) {
+                if($aPassLevelMax['passed'] != $aPassLevelMax['total']) {
+                    $sPassStatus = '_bx_courses_txt_status_in_process';
+                    $sPassTitle = '_bx_courses_txt_pass_continue';
+                }
+                else {
+                    $sPassStatus = '_bx_courses_txt_status_completed';
+                    $sPassTitle = '_bx_courses_txt_pass_again';
+                }
+            }
+        }
+
+        return [
+            $iPassPercent,
+            $aPassDetails,
+            _t($sPassStatus),
+            _t($sPassTitle)
+        ];
+    }
+
+    public function getNodePass($iProfileId, $iContentId, $aNode, &$aResults)
+    {
+        $iLevel = $aNode['level'];
+
+        if(!isset($aResults[$iLevel]))
+            $aResults[$iLevel] = [
+                'passed' => 0,
+                'total' => 0
+            ];
+
+        $aResults[$iLevel]['passed'] += $this->isNodePassed($iProfileId, $aNode['node_id']);
+        $aResults[$iLevel]['total'] += 1;
+
+        if($iLevel != $this->_oConfig->getContentLevelMax() && !empty($aNode['cn_l' . ($iLevel + 1)])) {
+            $aChildren = $this->_oDb->getContentStructure([
+                'sample' => 'parent_id', 
+                'parent_id' => $aNode['node_id']
+            ]);
+            
+            foreach($aChildren as $aChild)
+                $this->getNodePass($iProfileId, $iContentId, $aChild, $aResults);
+        }
+    }
+
+    public function getNodePassByChildren($iProfileId, $iContentId, $aNode, &$aResults)
+    {
+        $aUserTrack = $this->_oDb->getContentStructure([
+            'sample' => 'user_track', 
+            'entry_id' => $iContentId, 
+            'node_id' => $aNode['node_id'], 
+            'profile_id' => $iProfileId
+        ]);
+
+        $iLevelChildren = $aNode['level'] + 1;
+        $iCountChildren = $aNode['cn_l' . $iLevelChildren];
+
+        if(!isset($aResults[$iLevelChildren]))
+            $aResults[$iLevelChildren] = [
+                'passed' => 0,
+                'total' => 0
+            ];
+
+        $aResults[$iLevelChildren]['passed'] += count($aUserTrack);
+        $aResults[$iLevelChildren]['total'] += $iCountChildren;
+
+        if($iCountChildren && $iLevelChildren != $this->_oConfig->getContentLevelMax()) {
+            $aChildren = $this->_oDb->getContentStructure([
+                'sample' => 'parent_id', 
+                'parent_id' => $aNode['node_id']
+            ]);
+            
+            foreach($aChildren as $aChild)
+                $this->getNodePassByChildren($iProfileId, $iContentId, $aChild, $aResults);
+        }
+    }
+
+    public function getNodePassByData($iProfileId, $iContentId, $aNode)
+    {
+        $iTotal = 0;
+        $iPassCount = $iPassPercent = 0;
+        $sPassStatus = $sPassTitle = '';
+        if(($iTotal = $this->getDataTotalByNode($aNode)) != 0) {
+            $aUserTrack = $this->_oDb->getContentData([
+                'sample' => 'user_track', 
+                'entry_id' => $iContentId, 
+                'node_id' => $aNode['node_id'], 
+                'profile_id' => $iProfileId
+            ]);
+
+            $iPassCount = count($aUserTrack);
+            $iPassPercent = (int)round(100 * $iPassCount/$iTotal);
+            
+            if($iPassCount == 0) {
+                $sPassStatus = '_bx_courses_txt_status_not_started';
+                $sPassTitle = '_bx_courses_txt_pass_start';
+            }
+            else {
+                if($iPassCount != $iTotal) {
+                    $sPassStatus = '_bx_courses_txt_status_in_process';
+                    $sPassTitle = '_bx_courses_txt_pass_continue';
+                }
+                else {
+                    $sPassStatus = '_bx_courses_txt_status_completed';
+                    $sPassTitle = '_bx_courses_txt_pass_again';
+                }
+            }
+        }
+
+        return [
+            $iPassPercent,
+            _t('_bx_courses_txt_n_m_steps', $iPassCount, $iTotal),
+            _t($sPassStatus ? $sPassStatus : '_undefined'),
+            _t($sPassTitle)
+        ];
+    }
+
+    public function decodeDataAPI($aData, $aParams = [])
+    {
+        $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)
+                ];
+            }
+
+            $aResultAdd = [
+                'percent' => $iPassPercent,
+                'counters' => $aCounters,
+                'status' => $sPassStatus,
+                'show_pass' => $iPassPercent > 0 && $iPassPercent < 100,
+            ];
+
+            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]
+                ];
+            }
+
+            $aResultAdd = [
+                'counters' => $aCounters,
+            ];
+        }
+
+        return array_merge($aResult, $aResultAdd);
+    }
+
+    protected function _getOptionsContentModules()
+    {
+        $aResult = [];
+
+        $aModules = bx_srv('system', 'get_modules_by_type', ['content']);
+        foreach($aModules as $aModule)
+            $aResult[] = ['key' => $aModule['name'], 'value' => $aModule['title']];
+
+        return $aResult;
+    }
+}
+
+/** @} */

+ 34 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesPageEntry.php

@@ -0,0 +1,34 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Profile create/edit/delete pages.
+ */
+class BxCoursesPageEntry extends BxBaseModGroupsPageEntry
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_courses';
+        parent::__construct($aObject, $oTemplate);
+    }
+
+    public function getCode ()
+    {
+        $sResult = parent::getCode();
+        if(!empty($sResult))
+            $sResult .= $this->_oModule->_oTemplate->getJsCode('entry');
+
+        $this->_oModule->_oTemplate->addJs(['modules/base/groups/js/|entry.js', 'entry.js']);
+        return $sResult;
+    }
+}
+
+/** @} */

+ 677 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/classes/BxCoursesTemplate.php

@@ -0,0 +1,677 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/*
+ * Courses module representation.
+ */
+class BxCoursesTemplate extends BxBaseModGroupsTemplate
+{
+    protected $_iProfileId;
+
+    public function __construct(&$oConfig, &$oDb)
+    {
+        $this->MODULE = 'bx_courses';
+
+        parent::__construct($oConfig, $oDb);
+
+        $this->_iProfileId = bx_get_logged_profile_id();
+    }
+
+    public function unit ($aData, $isCheckPrivateContent = true, $mixedTemplate = false, $aParams = [])
+    {
+        if(!empty($aParams['context']) && in_array($aParams['context'], ['favorite', 'joined_entries']))
+            $aParams['template_name'] = 'unit_personal.html';
+
+        return parent::unit($aData, $isCheckPrivateContent, $mixedTemplate, $aParams);
+    }
+
+    public function unitVars($aData, $isCheckPrivateContent = true, $mixedTemplate = false, $aParams = [])
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aVars = parent::unitVars($aData, $isCheckPrivateContent, $mixedTemplate, $aParams);
+
+        $bVarsShowProgress = $bVarsShoStats = false;
+        $aVarsShowProgress = $aVarsShowStats = [true];
+        if(!empty($aParams['context']) && in_array($aParams['context'], ['favorite', 'joined_entries'])) {
+            $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']]);
+
+            $bVarsShowProgress = true;
+            $aVarsShowProgress = [
+                'progress' => $iPassPercent
+            ];
+
+            $aTmplVarsCounters = [];
+            foreach($aPassDetails as $iLevel => $aDetails) {
+                $aTmplVarsCounters[] = [
+                    'cn_title' => $aLevelToNode[$iLevel],
+                    'cn_passed' => $aDetails['passed'],
+                    'cn_total' => $aDetails['total'],
+                    'cn_progress' => bx_replace_markers($sTxtProgress, $aDetails)
+                ];
+            }
+
+            $bVarsShoStats = true;
+            $aVarsShowStats = [
+                'status' => $sPassStatus,
+                'bx_repeat:counters' => $aTmplVarsCounters,
+                'bx_if:show_pass' => [
+                    'condition' => $iPassPercent > 0 && $iPassPercent < 100,
+                    'content' => [
+                        'pass_link' => $aVars['content_url'],
+                        'pass_title' => $sPassTitle
+                    ]
+                ]
+            ];
+        }
+
+        return array_merge($aVars, [
+            'bx_if:show_pass_progress' => [
+                'condition' => $bVarsShowProgress,
+                'content' => $aVarsShowProgress
+            ],
+            'bx_if:show_pass_stats' => [
+                'condition' => $bVarsShoStats,
+                'content' => $aVarsShowStats
+            ]
+        ]);
+    }
+
+    public function getCounters($aCounters)
+    {
+        $aTmplVars = [];
+        foreach([BX_COURSES_CND_USAGE_ST, BX_COURSES_CND_USAGE_AT] as $iUsage) {
+            $sUsage = $this->_oConfig->getUsageI2S($iUsage);
+
+            $aTmplVarsCounters = [true];
+            $bTmplVarsCounters = !empty($aCounters[$sUsage]) && is_array($aCounters[$sUsage]);
+            if($bTmplVarsCounters) {
+                $aTmplVarsCounters['bx_repeat:counters_' . $sUsage] = [];
+                foreach($aCounters[$sUsage] as $sModule => $iCount) {
+                    $aCounter = [
+                        'title' => _t('_' . $sModule), 
+                        'value' => $iCount
+                    ];
+
+                    if($this->_bIsApi)
+                        $aTmplVars[$sUsage][] = $aCounter;
+                    else
+                        $aTmplVarsCounters['bx_repeat:counters_' . $sUsage][] = $aCounter;
+                }
+            }
+            
+            if($this->_bIsApi)
+                continue;
+
+            $aTmplVars['bx_if:show_' . $sUsage] = [
+                'condition' => $bTmplVarsCounters,
+                'content' => $aTmplVarsCounters
+            ];
+        }
+
+        return $this->_bIsApi ? $aTmplVars : $this->parseHtmlByName('counters.html', $aTmplVars);
+    }
+    
+    public function entryStructureByLevel($aContentInfo, $aParams = [])
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!isset($aParams['level']))
+            return '';
+
+        $iContentId = (int)$aContentInfo[$CNF['FIELD_ID']];
+        $iProfileId = bx_get_logged_profile_id();
+
+        $oPermalink = BxDolPermalinks::getInstance();
+
+        $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'] : 2;
+
+        $aNodes = $this->_oDb->getContentStructure([
+            'sample' => 'entry_id_full', 
+            'entry_id' => $iContentId, 
+            'level' => $iLevel, 
+            'status' => 'active',
+            'start' => $iStart, 
+            'per_page' => $iPerPage ? $iPerPage + 1 : 0
+        ]);
+
+        if(empty($aNodes) || !is_array($aNodes))
+            return '';
+
+        $iLevelMax = $this->_oConfig->getContentLevelMax();
+        $aLevelToNode = $this->_oConfig->getContentLevel2Node(false);
+
+        $sTxtProgress = _t('_bx_courses_txt_n_m_progress');
+        $sTmplKeysSelected = $this->_bIsApi ? 'selected' : 'bx_if:selected';
+        $sTmplKeysCounters = $this->_bIsApi ? 'counters' : 'bx_repeat:counters';
+        
+        $aTmplVarsNodes = [];
+        foreach($aNodes as $iKey => $aNode) {
+            $aNodeStats = [];
+            $this->_oModule->getNodePassByChildren($iProfileId, $iContentId, $aNode, $aNodeStats);           
+
+            $aTmplVarsCounters = [];
+            for($i = $iLevel + 1; $i <= $iLevelMax; $i++) {
+                if(!isset($aNodeStats[$i]))
+                    continue;
+
+                $aTmplVarsCounters[] = [
+                    'cn_title' => $aLevelToNode[$i],
+                    'cn_passed' => $aNodeStats[$i]['passed'],
+                    'cn_total' => $aNodeStats[$i]['total'],
+                    'cn_progress' => bx_replace_markers($sTxtProgress, $aNodeStats[$i])
+                ];
+            }
+
+            $bSelected = $aNode['node_id'] == $iSelected;
+            $sLink = BX_DOL_URL_ROOT . $oPermalink->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&' . $CNF['FIELD_ID'] . '=' . $iContentId, [
+                'parent_id' => $aNode['node_id']
+            ]);
+            $aNode = array_merge($aNode, [
+                $sTmplKeysSelected => $this->_bIsApi ? $bSelected : [
+                    'condition' => $bSelected,
+                    'content' => [true]
+                ],
+                'index' => $iKey + 1,
+                'link' => $this->_bIsApi ? bx_api_get_relative_url($sLink) : $sLink,
+                '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
+            ]);
+
+            $aTmplVarsNodes[] = $this->_bIsApi ? $aNode : [
+                'node' => $this->parseHtmlByName('node_l' . $iLevel . '.html', $aNode)
+            ];
+        }
+
+        if($this->_bIsApi)
+            return $aTmplVarsNodes;
+
+        $oPaginate = new BxTemplPaginate([
+            'start' => $iStart,
+            'per_page' => $iPerPage,
+            'on_change_page' => "return !loadDynamicBlockAutoPaginate(this, '{start}', '{per_page}')"
+        ]);
+        $oPaginate->setNumFromDataArray($aTmplVarsNodes);
+
+        return $this->parseHtmlByName('nodes_l' . $iLevel . '.html', [
+            'level' => $iLevel,
+            'bx_repeat:nodes' => $aTmplVarsNodes,
+            'paginate' => $oPaginate->getSimplePaginate()
+        ]);
+    }
+
+    /**
+     * For 1 level bases structure ( Max Level = 1)
+     */
+    public function entryStructureByParentMl1($aContentInfo, $aParams = [])
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $iContentId = (int)$aContentInfo[$CNF['FIELD_ID']];
+        $iParentId = isset($aParams['parent_id']) ? (int)$aParams['parent_id'] : 0;
+        $iProfileId = bx_get_logged_profile_id();
+
+        $aNodes = $this->_oDb->getContentStructure([
+            'sample' => 'entry_id_full', 
+            'entry_id' => $iContentId, 
+            'parent_id' => $iParentId,
+            'status' => 'active'
+        ]);
+
+        if(empty($aNodes) || !is_array($aNodes))
+            return $this->_bIsApi ? [] : '';
+
+        $sJsObject = $this->_oConfig->getJsObject('entry');
+        $oPermalink = BxDolPermalinks::getInstance();
+        
+        $iLevelMax = $this->_oConfig->getContentLevelMax();
+
+        $sTmplKeysShowPass = $this->_bIsApi ? 'show_pass' : 'bx_if:show_pass';
+
+        $aTmplVarsNodes = [];
+        foreach($aNodes as $iKey => $aNode) {
+            list($iPassPercent, $sPassProgress, $sPassStatus, $sPassTitle) = $this->_oModule->getNodePassByData($iProfileId, $iContentId, $aNode);
+
+            $sLink = BX_DOL_URL_ROOT . $oPermalink->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY_NODE'] . '&id=' . $iContentId, [
+                'node_id' => $aNode['node_id']
+            ]);
+
+            $bShowPass = !empty($sPassTitle);
+
+            $aNode = array_merge($aNode, [
+                'level_max' => $iLevelMax,
+                'index' => $iKey + 1,
+                'link' => $sLink,
+                'pass_percent' => $iPassPercent,
+                'pass_progress' => $sPassProgress,
+                'pass_status' => $sPassStatus,
+                $sTmplKeysShowPass => $this->_bIsApi ? $bShowPass : [
+                    'condition' => $bShowPass,
+                    'content' => [
+                        'js_object' => $sJsObject,
+                        'id' => $aNode['node_id'],
+                        'pass_href' => $sLink,
+                        'pass_title' => $sPassTitle,
+                    ]
+                ]
+            ]);
+
+            if($this->_bIsApi) {
+                if(!empty($aNode['counters']) && ($aCounters = json_decode($aNode['counters'], true)))
+                    $aNode['counters'] = $this->getCounters($aCounters);
+
+                $aNode = array_merge($aNode, [
+                    'link' => bx_api_get_relative_url($aNode['link']),
+                    'pass_title' => $sPassTitle,
+                    'pass_callback' => $this->MODULE . '/pass_node&params[]=' . $aNode['node_id'],
+                ]);
+            }
+
+            $aTmplVarsNodes[] = $this->_bIsApi ? $aNode : [
+                'node' => $this->parseHtmlByName('ml' . $iLevelMax . '_node_l' . $aNode['level'] . '.html', $aNode)
+            ];
+        }
+
+        if($this->_bIsApi)
+            return $aTmplVarsNodes;
+
+        return $this->parseHtmlByName('ml' . $iLevelMax . '_nodes_l' . $aNode['level'] . '.html', [
+            'level_max' => $iLevelMax,
+            'bx_repeat:nodes' => $aTmplVarsNodes
+        ]);
+    }
+
+    /**
+     * For 2 levels bases structure ( Max Level = 2)
+     */
+    public function entryStructureByParentMl2($aContentInfo, $aParams = [])
+    {
+        if(empty($aParams['parent_id']))
+            return $this->_bIsApi ? [] : '';
+
+        return $this->entryStructureByParentMl1($aContentInfo, $aParams);
+    }
+
+    /**
+     * For 3 levels bases structure ( Max Level = 3)
+     */
+    public function entryStructureByParentMl3($aContentInfo, $aParams = [])
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParams['parent_id']))
+            return $this->_bIsApi ? [] : '';
+
+        $iContentId = (int)$aContentInfo[$CNF['FIELD_ID']];
+        $iParentId = (int)$aParams['parent_id'];
+        $iProfileId = bx_get_logged_profile_id();
+
+        $aNodes = $this->_oDb->getContentStructure([
+            'sample' => 'entry_id_full', 
+            'entry_id' => $iContentId, 
+            'parent_id' => $iParentId,
+            'status' => 'active'
+        ]);
+
+        if(empty($aNodes) || !is_array($aNodes))
+            return $this->_bIsApi ? [] : '';
+
+        $sJsObject = $this->_oConfig->getJsObject('entry');
+        $oPermalink = BxDolPermalinks::getInstance();
+
+        $aInputs = [];
+        foreach($aNodes as $aNode) {
+            $aInputs['node_' . $aNode['node_id']] = [
+                'type' => 'block_header',
+                'caption' => bx_process_output($aNode['title']),
+                'collapsed' => false,
+                'attrs' => ['id' => 'node_' . $aNode['node_id'], 'class' => ''],
+            ];
+
+            $aSubNodes = $this->_oDb->getContentStructure([
+                'sample' => 'entry_id_full', 
+                'entry_id' => $iContentId, 
+                'parent_id' => (int)$aNode['node_id'], 
+                'status' => 'active'
+            ]);
+
+            if(!empty($aSubNodes) && is_array($aSubNodes)) {
+                $sTmplKeysShowPass = $this->_bIsApi ? 'show_pass' : 'bx_if:show_pass';
+                $aTmplVarsNodes = [];
+                foreach($aSubNodes as $iKey => $aSubNode) {
+                    list($iPassPercent, $sPassProgress, $sPassStatus, $sPassTitle) = $this->_oModule->getNodePassByData($iProfileId, $iContentId, $aSubNode);
+
+                    $sLink = BX_DOL_URL_ROOT . $oPermalink->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY_NODE'] . '&id=' . $iContentId, [
+                        'node_id' => $aSubNode['node_id']
+                    ]);
+
+                    $bShowPass = !empty($sPassTitle);
+                    
+                    $aSubNode = array_merge($aSubNode, [
+                        'index' => $iKey + 1,
+                        'link' => $this->_bIsApi ? bx_api_get_relative_url($sLink) : $sLink,
+                        'pass_percent' => $iPassPercent,
+                        'pass_progress' => $sPassProgress,
+                        'pass_status' => $sPassStatus,
+                        $sTmplKeysShowPass => $this->_bIsApi ? $bShowPass : [
+                            'condition' => $sPassTitle,
+                            'content' => [
+                                'js_object' => $sJsObject,
+                                'id' => $aSubNode['node_id'],
+                                'pass_href' => $sLink,
+                                'pass_title' => $sPassTitle,
+                            ]
+                        ]
+                    ]);
+
+                    $aTmplVarsNodes[] = $this->_bIsApi ? $aSubNode : [
+                        'node' => $this->parseHtmlByName('ml3_node_l' . $aSubNode['level'] . '.html', $aSubNode)
+                    ];
+                }
+
+                $sInput = 'node_' . $aNode['node_id'] . '_subnodes';
+                $aInputs[$sInput] = [
+                    'type' => 'custom',
+                    'name' => $sInput,
+                    'caption' => '',
+                    'content' => $this->_bIsApi ? $aTmplVarsNodes : $this->parseHtmlByName('ml3_nodes_l' . $aSubNode['level'] . '.html', [
+                        'bx_repeat:nodes' => $aTmplVarsNodes
+                    ]),
+                ];
+            }
+            else
+                $aInputs['node_' . $aNode['node_id']]['collapsed'] = true;
+        }
+
+        $oForm = new BxTemplFormView([
+            'form_attrs' => [
+                'id' => 'bx-courses-structure-by-parent-' . $iParentId,
+            ],
+            'inputs' => $aInputs,
+        ]);
+        $oForm->setShowEmptySections(true);
+
+        return $this->_bIsApi ? $oForm->getCodeAPI() : $oForm->getCode();
+    }
+
+    public function entryNode($aContentInfo, $aParams = [])
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!isset($aParams['node_id']))
+            return '';
+
+        $iContentId = (int)$aContentInfo[$CNF['FIELD_ID']];
+        $iNodeId = (int)$aParams['node_id'];
+        $iUsage = isset($aParams['usage']) && $aParams['usage'] !== false ? (int)$aParams['usage'] : BX_COURSES_CND_USAGE_ST;
+        $iProfileId = bx_get_logged_profile_id();
+
+        $aNode = $this->_oDb->getContentNodes([
+            'sample' => 'id_full', 
+            'id' => $iNodeId,
+        ]);
+
+        $aTmplVars = [
+            'index' => $aNode['order'],
+            'sample' => $this->_oConfig->getContentNodeTitle($aNode['level']),
+            'title' => $aNode['title'],
+            'text' => $aNode['text'],
+            'passing' => $aNode['passing']
+        ];
+
+        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
+        ];
+    }
+
+    public function entryData($aData, $sView)
+    {
+        $sJsObject = $this->_oConfig->getJsObject('entry');
+
+        $aNode = $this->_oDb->getContentNodes([
+            'sample' => 'id', 
+            'id' => $aData['node_id']
+        ]);  
+
+        $iOrder = (int)$aData['order'];
+        $aSiblings = $this->_oDb->getContentData([
+            'sample' => 'siblings', 
+            'entry_id' => $aData['entry_id'], 
+            'node_id' => $aData['node_id'], 
+            'usage' => BX_COURSES_CND_USAGE_ST, 
+            'order' => $iOrder
+        ]);
+
+        $bTmplVarsBack = false;
+        $aTmplVarsBack = ($iOrderB = $iOrder - 1) && ($bTmplVarsBack = !empty($aSiblings[$iOrderB]) && is_array($aSiblings[$iOrderB])) ? [
+            'js_object' => $sJsObject,
+            'id_back' => $aSiblings[$iOrderB]['id']
+        ] : [true];
+
+        $bTmplVarsNext = false;
+        $aTmplVarsNext = ($iOrderN = $iOrder + 1) && ($bTmplVarsNext = !empty($aSiblings[$iOrderN]) && is_array($aSiblings[$iOrderN])) ? [
+            'js_object' => $sJsObject,
+            'id_next' => $aSiblings[$iOrderN]['id']
+        ] : [true];
+
+        return ['popup' => [
+            'html' => BxTemplFunctions::getInstance()->popupBox($this->_oConfig->getHtmlIds('popup_content_data'), $aNode['title'], $this->parseHtmlByName('node_data_view.html', [
+                'js_object' => $sJsObject,
+                'view' => $sView,
+                'bx_if:show_back' => [
+                    'condition' => $bTmplVarsBack,
+                    'content' => $aTmplVarsBack
+                ],
+                'bx_if:show_next' => [
+                    'condition' => $bTmplVarsNext,
+                    'content' => $aTmplVarsNext
+                ]
+            ]), true),
+            'options' => [
+                'onHide' => 'document.location = document.location'
+            ]
+        ]];
+    }
+
+    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' => $aNode['id'],
+            'usage' => $iUsage
+        ]);
+
+        $sTxtUndefined = _t('_undefined');
+        $sTxtPass = _t('_bx_courses_txt_pass');
+
+        $bUsageSt = $iUsage == BX_COURSES_CND_USAGE_ST;
+        $bUsageAt = $iUsage == BX_COURSES_CND_USAGE_AT;
+
+        $aResults = [];
+        if(!empty($aDataItems) && is_array($aDataItems))
+            foreach($aDataItems as $iIndex => $aDataItem) {
+                $sImageUrl = '';
+                if(($sMethod = 'get_thumb') && bx_is_srv($aDataItem['content_type'], $sMethod))
+                    $sImageUrl = bx_srv($aDataItem['content_type'], $sMethod, [$aDataItem['content_id']]);
+
+                $sVideoUrl = '';
+                if(($sMethod = 'get_video') && bx_is_srv($aDataItem['content_type'], $sMethod))
+                    $sVideoUrl = bx_srv($aDataItem['content_type'], $sMethod, [$aDataItem['content_id']]);
+
+                $sLink = '';
+                if(($sMethod = 'get_link') && bx_is_srv($aDataItem['content_type'], $sMethod))
+                    $sLink = bx_srv($aDataItem['content_type'], $sMethod, [$aDataItem['content_id']]);
+
+                $bPassed = $bUsageSt && $this->_oModule->isDataPassed($iProfileId, $aDataItem);
+
+                $aTmplVarsPass = [true];
+                $bTmplVarsPass = $bUsageSt && !$bPassed && $sLink && ((int)$aNode['passing'] == BX_COURSES_CND_PASSING_ALL || $iIndex == 0 || $this->_oModule->isDataPassed($iProfileId, $aDataItems[$iIndex - 1]));
+                if($bTmplVarsPass) {
+                    $aTmplVarsPass = [
+                        'js_object' => $sJsObject,
+                        'id' => $aDataItem['id'],
+                        'link' => $sLink,
+                        'title' => $sTxtPass
+                    ];
+                }
+
+                $sType = _t('_bx_courses_txt_data_type_' . $aDataItem['content_type']);
+
+                $sTitle = '';
+                if(($sMethod = 'get_title') && bx_is_srv($aDataItem['content_type'], $sMethod))              
+                    $sTitle = bx_srv($aDataItem['content_type'], $sMethod, [$aDataItem['content_id']]);
+                if(!$sTitle && ($sMethod = 'get_text') &&  bx_is_srv($aDataItem['content_type'], $sMethod))
+                    $sTitle = bx_srv($aDataItem['content_type'], $sMethod, [$aDataItem['content_id']]);
+                if(!$sTitle)
+                    $sTitle = $sTxtUndefined;
+
+                $bTmplVarsShowLink = $bUsageAt && $sLink;
+                $aTmplVarsShowLink = $bTmplVarsShowLink ? [
+                    'link' => $sLink,
+                    'title' => $sTitle
+                ] : [true];
+
+                $bTmplVarsShowSize = false;
+                $aTmplVarsShowSize = [true];
+                $bTmplVarsShowDownload = false;
+                $aTmplVarsShowDownload = [true];
+                if($bUsageAt && ($bTmplVarsShowSize = ($sMethod = 'get_file') && bx_is_srv($aDataItem['content_type'], $sMethod))) {
+                    $aFileInfo = bx_srv($aDataItem['content_type'], $sMethod, [$aDataItem['content_id']]);
+                    if(!empty($aFileInfo) && is_array($aFileInfo)) {
+                        $aTmplVarsShowSize['size'] = _t_format_size($aFileInfo['size']);
+
+                        $bTmplVarsShowDownload = !empty($aFileInfo['url_download']);
+                        $aTmplVarsShowDownload = [
+                            'link' => $aFileInfo['url_download']
+                        ];
+                    }
+                }
+
+                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[] = array_merge([
+                        'id' => $aDataItem['id'],
+                        'type' => $sType,
+                        'title' => $sTitle,
+                        'text' => $sText,
+                        'image' => $sImageUrl,
+                        'video' => $sVideoUrl
+                    ], ($bUsageSt ? [
+                        'pass_link' => $bTmplVarsPass ? bx_api_get_relative_url($sLink) : '',
+                        'pass_title' => $sTxtPass,
+                        'passed' => $bPassed
+                    ] : [
+                        'size' => $bTmplVarsShowSize ? $aTmplVarsShowSize['size'] : '',
+                        'view_link' => $bTmplVarsShowLink ? bx_api_get_relative_url($sLink) : '',
+                        'download_link' => $bTmplVarsShowDownload ? $aTmplVarsShowDownload['link'] : '',
+                    ]));
+                }
+                else 
+                    $aResults[] = [
+                        'bx_if:show_image' => [
+                            'condition' => $sImageUrl,
+                            'content' => [
+                                'image' => $sImageUrl
+                            ]
+                        ],
+                        'bx_if:show_image_empty' => [
+                            'condition' => !$sImageUrl,
+                            'content' => [
+                                'type' => $sType
+                            ]
+                        ],
+                        'bx_if:show_video' => [
+                            'condition' => $sVideoUrl,
+                            'content' => [
+                                'video' => $sVideoUrl
+                            ]
+                        ],
+                        'type' => $sType,
+                        'bx_if:show_link' => [
+                            'condition' => $bTmplVarsShowLink,
+                            'content' => $aTmplVarsShowLink
+                        ],
+                        'bx_if:show_text' => [
+                            'condition' => !$bTmplVarsShowLink,
+                            'content' => [
+                                'title' => $sTitle,
+                            ]
+                        ],
+                        'bx_if:show_size' => [
+                            'condition' => $bTmplVarsShowSize,
+                            'content' => $aTmplVarsShowSize
+                        ],
+                        'bx_if:show_pass' => [
+                            'condition' => $bTmplVarsPass,
+                            'content' => $aTmplVarsPass
+                        ],
+                        'bx_if:show_download' => [
+                            'condition' => $bTmplVarsShowDownload,
+                            'content' => $aTmplVarsShowDownload
+                        ],
+                    ];
+            }
+
+        return $aResults;
+    }
+
+    protected function _getNodeStatus($iProfileId, $iContentId, $iNodeId)
+    {
+        if($this->_oModule->isNodePassed($iProfileId, $iNodeId))
+            return _t('_bx_courses_txt_status_completed');
+
+        if($this->_oModule->isNodeStarted($iProfileId, $iNodeId))
+            return _t('_bx_courses_txt_status_in_process');
+        
+        return _t('_bx_courses_txt_status_not_started');
+    }
+}
+
+/** @} */

+ 152 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/install/config.php

@@ -0,0 +1,152 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_courses',
+    'title' => 'Courses',
+    'note' => 'Basic course profiles functionality.',
+    'version' => '14.0.3',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/courses/',
+    'home_uri' => 'courses',
+
+    'db_prefix' => 'bx_courses_',
+    'class_prefix' => 'BxCourses',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Courses',
+
+    /**
+     * Connections.
+     */
+    'connections' => array(
+        'sys_profiles_friends' => array ('type' => 'profiles'),
+        'sys_profiles_subscriptions' => array ('type' => 'profiles'),
+    ),
+
+    /**
+     * Menu triggers.
+     */
+    'menu_triggers' => array(
+    	'trigger_profile_view_submenu',
+        'trigger_group_view_submenu',
+        'trigger_group_snippet_meta',
+        'trigger_group_view_actions',
+    ),
+
+    /**
+     * Page triggers.
+     */
+    'page_triggers' => array (
+    	'trigger_page_group_view_entry', 
+    ),
+
+	/**
+     * Storage objects to automatically delete files from upon module uninstallation.
+     * Note. Don't add storage objects used in transcoder objects.
+     */
+    'storages' => array(
+    	'bx_courses_pics'
+    ),
+
+	/**
+     * Transcoders.
+     */
+    'transcoders' => array(
+    	'bx_courses_icon', 
+    	'bx_courses_thumb', 
+    	'bx_courses_avatar', 
+        'bx_courses_avatar_big', 
+    	'bx_courses_picture', 
+    	'bx_courses_cover', 
+    	'bx_courses_cover_thumb',
+        'bx_courses_gallery'
+    ),
+
+    /**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_courses',
+    	'bx_courses_cmts',
+    ),
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+    	'process_esearches' => 1,
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    	'process_connections' => 1,
+        'process_deleted_profiles' => 1,
+    	'update_relations' => 1,        
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable_success' => array(
+    	'process_menu_triggers' => 1,
+    	'process_page_triggers' => 1,
+    	'process_esearches' => 1,
+    	'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'unregister_transcoders' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+    	'register_transcoders' => 1,
+    	'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Connections Section
+     */
+    'relations' => array(
+        'bx_timeline',
+        'bx_notifications',
+    ),
+);
+
+/** @} */

+ 541 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/install/langs/en.xml

@@ -0,0 +1,541 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string name="_bx_courses"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_cmts"><![CDATA[Courses' Comments]]></string>
+
+    <string name="_bx_courses_option_enable_auto_approve"><![CDATA[Auto approve a course after creation]]></string>
+    <string name="_bx_courses_option_enable_content"><![CDATA[Enable possibility to add content in a course (experimental)]]></string>
+    <string name="_bx_courses_option_content_level_max"><![CDATA[Max level for a node in tree based structure (choose before start making courses)]]></string>
+    <string name="_bx_courses_option_content_modules_step"><![CDATA[Modules from which the content can be added in a course as step]]></string>
+    <string name="_bx_courses_option_content_modules_attachment"><![CDATA[Modules from which the content can be posted in a course as attachment]]></string>
+    <string name="_bx_courses_option_num_connections_quick"><![CDATA[Number of items in friends block]]></string>
+    <string name="_bx_courses_option_per_page_browse"><![CDATA[Number of items per page]]></string>
+    <string name="_bx_courses_option_num_rss"><![CDATA[Number of items in RSS feed]]></string>
+    <string name="_bx_courses_option_searchable_fields"><![CDATA[Searchable fields]]></string>
+    <string name="_bx_courses_option_members_mode"><![CDATA[Special mode for course members]]></string>
+    <string name="_bx_courses_option_members_mode_multi_roles"><![CDATA[Multi Roles]]></string>
+    <string name="_bx_courses_option_per_page_for_favorites_lists"><![CDATA[Number of favorite lists per page]]></string>
+    <string name="_bx_courses_option_internal_notifications"><![CDATA[Use internal notifications]]></string>
+
+    <string name="_bx_courses_form_profile"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_form_profile_display_add"><![CDATA[Add Course]]></string>
+    <string name="_bx_courses_form_profile_display_edit"><![CDATA[Edit Course]]></string>
+    <string name="_bx_courses_form_profile_display_edit_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_courses_form_profile_display_delete"><![CDATA[Delete Course]]></string>
+    <string name="_bx_courses_form_profile_display_view"><![CDATA[View Course]]></string>
+    <string name="_bx_courses_form_profile_display_view_full"><![CDATA[Full View Course]]></string>
+    <string name="_bx_courses_form_profile_display_invite"><![CDATA[Invite to Course]]></string>
+    <string name="_bx_courses_form_entry_input_author"><![CDATA[Author]]></string>
+    <string name="_bx_courses_form_profile_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_courses_form_profile_input_sys_join_confirm"><![CDATA[Join confirmation]]></string>
+    <string name="_bx_courses_form_profile_input_join_confirm"><![CDATA[Approval required to join]]></string>
+    <string name="_bx_courses_form_profile_input_sys_allow_view_to"><![CDATA[Visible to]]></string>
+    <string name="_bx_courses_form_profile_input_allow_view_to"><![CDATA[Visible to]]></string>
+    <string name="_bx_courses_form_profile_input_allow_view_favorite_list"><![CDATA[Visibility]]></string>
+    <string name="_bx_courses_form_profile_input_allow_view_to_desc"><![CDATA[Closed - anyone can find the course and see who's in it, only students can see posts. Secret - only students can find the course and see posts.]]></string>
+    <string name="_bx_courses_form_profile_input_sys_allow_post_to"><![CDATA[Who can post]]></string>
+    <string name="_bx_courses_form_profile_input_allow_post_to"><![CDATA[Who can post]]></string>
+    <string name="_bx_courses_form_profile_input_sys_initial_members"><![CDATA[Invite Students]]></string>
+    <string name="_bx_courses_form_profile_input_initial_members"><![CDATA[Invite Students]]></string>
+    <string name="_bx_courses_form_profile_input_sys_name"><![CDATA[Name]]></string>
+    <string name="_bx_courses_form_profile_input_name"><![CDATA[Name]]></string>
+    <string name="_bx_courses_form_profile_input_name_err"><![CDATA[This information is essential. Please, fill in this field.]]></string>
+    <string name="_bx_courses_form_profile_input_sys_desc"><![CDATA[Description]]></string>
+    <string name="_bx_courses_form_profile_input_desc"><![CDATA[Description]]></string>
+    <string name="_bx_courses_form_profile_input_sys_cat"><![CDATA[Category]]></string>
+    <string name="_bx_courses_form_profile_input_cat"><![CDATA[Category]]></string>
+    <string name="_bx_courses_form_profile_input_cat_err"><![CDATA[Category field is mandatory]]></string>
+    <string name="_bx_courses_form_profile_input_sys_picture"><![CDATA[Course Picture]]></string>
+    <string name="_bx_courses_form_profile_input_picture"><![CDATA[Upload New Course Picture]]></string>
+    <string name="_bx_courses_form_profile_input_picture_err"><![CDATA[Course picture is mandatory.]]></string>
+    <string name="_bx_courses_form_profile_input_sys_cover"><![CDATA[Cover Picture]]></string>
+    <string name="_bx_courses_form_profile_input_cover"><![CDATA[Upload New Cover Picture]]></string>
+    <string name="_bx_courses_form_profile_input_sys_delete_confirm"><![CDATA[Delete confirmation]]></string>
+    <string name="_bx_courses_form_profile_input_delete_confirm"><![CDATA[I am sure I want to delete this course]]></string>
+    <string name="_bx_courses_form_profile_input_delete_confirm_info"><![CDATA[This action can not be undone.]]></string>
+    <string name="_bx_courses_form_profile_input_delete_confirm_error"><![CDATA[Please check this checkbox if you really want to delete course.]]></string>
+
+    <string name="_bx_courses_form_price"><![CDATA[Price]]></string>
+    <string name="_bx_courses_form_price_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_courses_form_price_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_courses_form_price_input_sys_id"><![CDATA[ID]]></string>
+    <string name="_bx_courses_form_price_input_sys_role_id"><![CDATA[Role ID]]></string>
+    <string name="_bx_courses_form_price_input_sys_name"><![CDATA[Name]]></string>
+    <string name="_bx_courses_form_price_input_name"><![CDATA[Name]]></string>
+    <string name="_bx_courses_form_price_input_inf_name"><![CDATA[Unique value which will be used in back end and API calls]]></string>
+    <string name="_bx_courses_form_price_input_err_name"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_courses_form_price_input_sys_period"><![CDATA[Period]]></string>
+    <string name="_bx_courses_form_price_input_period"><![CDATA[Period]]></string>
+    <string name="_bx_courses_form_price_input_inf_period"><![CDATA[Use 0 for lifetime]]></string>
+    <string name="_bx_courses_form_price_input_sys_period_unit"><![CDATA[Period unit]]></string>
+    <string name="_bx_courses_form_price_input_period_unit"><![CDATA[Period unit]]></string>
+    <string name="_bx_courses_form_price_input_inf_period_unit"><![CDATA[Leave empty for lifetime level.]]></string>
+    <string name="_bx_courses_form_price_input_err_period_unit"><![CDATA[You need to select a period unit here.]]></string>
+    <string name="_bx_courses_form_price_input_sys_price"><![CDATA[Price]]></string>
+    <string name="_bx_courses_form_price_input_price"><![CDATA[Price]]></string>
+    <string name="_bx_courses_form_price_input_inf_price"><![CDATA[Use 0 or leave empty to create free level.]]></string>
+    <string name="_bx_courses_form_price_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_courses_form_price_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_courses_form_price_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_courses_form_price_input_do_cancel"><![CDATA[Cancel]]></string>
+
+    <string name="_bx_courses_form_content_node"><![CDATA[Content Node]]></string>
+    <string name="_bx_courses_form_content_node_display_add"><![CDATA[Add Node]]></string>
+    <string name="_bx_courses_form_content_node_display_edit"><![CDATA[Edit Node]]></string>
+    <string name="_bx_courses_form_content_node_input_sys_title"><![CDATA[Title]]></string>
+    <string name="_bx_courses_form_content_node_input_title"><![CDATA[Title]]></string>
+    <string name="_bx_courses_form_content_node_input_title_err"><![CDATA[Title is essential. Please, fill in this field.]]></string>
+    <string name="_bx_courses_form_content_node_input_sys_text"><![CDATA[Description]]></string>
+    <string name="_bx_courses_form_content_node_input_text"><![CDATA[Description]]></string>
+    <string name="_bx_courses_form_content_node_input_sys_passing"><![CDATA[Passing]]></string>
+    <string name="_bx_courses_form_content_node_input_passing"><![CDATA[Passing]]></string>
+    <string name="_bx_courses_form_content_node_input_passing_all"><![CDATA[All at once]]></string>
+    <string name="_bx_courses_form_content_node_input_passing_alternate"><![CDATA[Alternate]]></string>
+    <string name="_bx_courses_form_content_node_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_courses_form_content_node_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_courses_form_content_node_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_courses_form_content_node_input_do_cancel"><![CDATA[Cancel]]></string>
+
+    <string name="_bx_courses_acl_action_create_profile"><![CDATA[Create Course]]></string>
+    <string name="_bx_courses_acl_action_use_paid_join"><![CDATA[Use paid join]]></string>
+    <string name="_bx_courses_acl_action_delete_profile"><![CDATA[Delete Course]]></string>
+    <string name="_bx_courses_acl_action_view_profile"><![CDATA[View Course]]></string>
+    <string name="_bx_courses_acl_action_edit_any_profile"><![CDATA[Edit Any Course]]></string>
+    <string name="_bx_courses_acl_action_delete_any_profile"><![CDATA[Delete Any Course]]></string>
+    <string name="_bx_courses_acl_action_delete_invites"><![CDATA[Delete Invites]]></string>
+
+    <string name="_bx_courses_page_title_sys_joined_entries"><![CDATA[Profile Courses]]></string>
+    <string name="_bx_courses_page_title_joined_entries"><![CDATA[Profile Courses]]></string>
+    <string name="_bx_courses_page_title_sys_create_profile"><![CDATA[Create Course]]></string>
+    <string name="_bx_courses_page_title_create_profile"><![CDATA[Create Course]]></string>
+    <string name="_bx_courses_page_title_sys_edit_profile"><![CDATA[Edit Course]]></string>
+    <string name="_bx_courses_page_title_edit_profile"><![CDATA[Edit Course]]></string>
+    <string name="_bx_courses_page_title_sys_edit_profile_cover"><![CDATA[Edit Course Cover]]></string>
+    <string name="_bx_courses_page_title_edit_profile_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_courses_page_title_sys_delete_profile"><![CDATA[Delete Course]]></string>
+    <string name="_bx_courses_page_title_delete_profile"><![CDATA[Delete Course]]></string>
+    <string name="_bx_courses_page_title_sys_join_profile"><![CDATA[Join Course]]></string>
+    <string name="_bx_courses_page_title_join_profile"><![CDATA[Join Course]]></string>
+    <string name="_bx_courses_page_title_sys_view_profile_closed"><![CDATA[View Closed Course]]></string>
+    <string name="_bx_courses_page_title_sys_view_profile"><![CDATA[View Course]]></string>
+    <string name="_bx_courses_page_title_sys_view_profile_node"><![CDATA[View Lesson]]></string>
+    <string name="_bx_courses_page_title_view_profile"><![CDATA[{display_name}]]></string>
+    <string name="_bx_courses_page_title_view_profile_node"><![CDATA[Lesson]]></string>
+    <string name="_bx_courses_page_title_browse"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_page_title_browse_by_author"><![CDATA[Courses of {display_name}]]></string>
+    <string name="_bx_courses_page_title_browse_featured"><![CDATA[Featured Courses]]></string>
+    <string name="_bx_courses_page_title_browse_top"><![CDATA[Top Courses]]></string>
+    <string name="_bx_courses_page_title_browse_recent"><![CDATA[New Courses]]></string>
+    <string name="_bx_courses_page_title_browse_connections"><![CDATA[{0} connections]]></string>
+    <string name="_bx_courses_page_title_browse_connections_mutual"><![CDATA[{0} and {1} mutual connections]]></string>
+    <string name="_bx_courses_page_title_sys_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_courses_page_title_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_courses_page_title_sys_entry_fans"><![CDATA[Students]]></string>
+    <string name="_bx_courses_page_title_entry_fans"><![CDATA[Students]]></string>
+    <string name="_bx_courses_page_title_sys_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_courses_page_title_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_courses_page_block_title_entry_reports"><![CDATA[Reports]]></string>
+    <string name="_bx_courses_page_title_sys_profile_info"><![CDATA[Course Info]]></string>
+    <string name="_bx_courses_page_title_profile_info"><![CDATA[Info about {display_name}]]></string>
+    <string name="_bx_courses_page_title_sys_profile_pricing"><![CDATA[Pricing]]></string>
+    <string name="_bx_courses_page_title_profile_pricing"><![CDATA[Pricing]]></string>
+    <string name="_bx_courses_page_title_sys_profile_content"><![CDATA[Content]]></string>
+    <string name="_bx_courses_page_title_profile_content"><![CDATA[Content]]></string>
+    <string name="_bx_courses_page_title_sys_recent"><![CDATA[New Courses]]></string>
+    <string name="_bx_courses_page_title_recent"><![CDATA[New Courses]]></string>
+    <string name="_bx_courses_page_title_sys_manage"><![CDATA[Manage Own Courses]]></string>
+    <string name="_bx_courses_page_title_sys_manage_administration"><![CDATA[Manage All Courses]]></string>
+    <string name="_bx_courses_page_title_manage"><![CDATA[Manage Courses]]></string>
+    <string name="_bx_courses_page_title_sys_top"><![CDATA[Top Courses]]></string>
+    <string name="_bx_courses_page_title_top"><![CDATA[Top Courses]]></string>
+    <string name="_bx_courses_page_title_sys_joined"><![CDATA[Attended  Courses]]></string>
+    <string name="_bx_courses_page_title_joined"><![CDATA[Attended Courses]]></string>
+    <string name="_bx_courses_page_title_sys_invite_to_course"><![CDATA[Invite Students to the Course]]></string>
+    <string name="_bx_courses_page_title_invite_to_course"><![CDATA[Invite Students to the Course]]></string>
+    <string name="_bx_courses_page_title_sys_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_courses_page_title_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_courses_page_title_sys_entries_favorites"><![CDATA[Favorites Courses]]></string>
+    <string name="_bx_courses_page_title_entries_favorites"><![CDATA[Favorites Courses]]></string>
+
+    <string name="_bx_courses_page_block_title_sys_entries_actions"><![CDATA[Courses actions]]></string>
+    <string name="_bx_courses_page_block_title_entries_actions"><![CDATA[Courses actions]]></string>
+    <string name="_bx_courses_page_block_title_sys_favorites_entries"><![CDATA[Favorite List]]></string>
+    <string name="_bx_courses_page_block_title_favorites_entries"><![CDATA["{title}" List]]></string>
+    <string name="_bx_courses_page_block_title_favorites_entries_info"><![CDATA[Info]]></string>
+    <string name="_bx_courses_page_block_title_favorites_entries_actions"><![CDATA[Favorites List Actions]]></string>
+    <string name="_bx_courses_page_block_title_sys_entries_of_author"><![CDATA[Courses of author]]></string>
+    <string name="_bx_courses_page_block_title_entries_of_author"><![CDATA[Courses by {display_name}]]></string>
+    <string name="_bx_courses_page_block_title_favorites_of_author"><![CDATA[Favorite Courses]]></string>
+    <string name="_bx_courses_page_block_title_sys_favorites_of_author"><![CDATA[Favorite Courses]]></string>
+    <string name="_bx_courses_page_block_title_sys_joined_entries"><![CDATA[Attended Courses]]></string>
+    <string name="_bx_courses_page_block_title_joined_entries"><![CDATA[Attended Courses]]></string>
+    <string name="_bx_courses_page_block_title_create_profile"><![CDATA[Create Course]]></string>
+    <string name="_bx_courses_page_block_title_edit_profile"><![CDATA[Edit Course]]></string>
+    <string name="_bx_courses_page_block_title_edit_profile_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_courses_page_block_title_delete_profile"><![CDATA[Delete Course]]></string>
+    <string name="_bx_courses_page_block_title_join_profile"><![CDATA[Join Course]]></string>
+    <string name="_bx_courses_page_block_title_profile_info_link"><![CDATA[About <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_courses_page_block_title_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_courses_page_block_title_system_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_courses_page_block_title_system_profile_pricing"><![CDATA[Pricing]]></string>
+    <string name="_bx_courses_page_block_title_profile_pricing"><![CDATA[Pricing]]></string>
+    <string name="_bx_courses_page_block_title_profile_pricing_link"><![CDATA[Pricing of <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_courses_page_block_title_system_profile_structure"><![CDATA[Structure]]></string>
+    <string name="_bx_courses_page_block_title_profile_structure_link"><![CDATA[Structure of <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_courses_page_block_title_system_profile_data"><![CDATA[Content]]></string>
+    <string name="_bx_courses_page_block_title_profile_data_link"><![CDATA[Content of <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_courses_page_block_title_profile_description"><![CDATA[Description]]></string>
+    <string name="_bx_courses_page_block_title_profile_structure_l1"><![CDATA[Modules]]></string>
+    <string name="_bx_courses_page_block_title_profile_structure_l2"><![CDATA[Themes]]></string>
+    <string name="_bx_courses_page_block_title_profile_friends_link"><![CDATA[Friends of <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_courses_page_block_title_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_courses_page_block_title_system_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_courses_page_block_title_admins"><![CDATA[Instructors]]></string>
+    <string name="_bx_courses_page_block_title_fans_link"><![CDATA[Students of <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_courses_page_block_title_fans"><![CDATA[Students]]></string>
+    <string name="_bx_courses_page_block_title_system_fans"><![CDATA[Students]]></string>
+    <string name="_bx_courses_page_block_title_fans_invites"><![CDATA[Invites]]></string>
+    <string name="_bx_courses_page_block_title_system_invites"><![CDATA[Invites]]></string>
+    <string name="_bx_courses_page_block_title_profile_location"><![CDATA[Location]]></string>
+    <string name="_bx_courses_page_block_title_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_courses_page_block_title_profile_comments_link"><![CDATA[Comments to <a href="{entry_link}">{title}</a>]]></string>
+    <string name="_bx_courses_page_block_title_profile_actions"><![CDATA[Actions]]></string>
+    <string name="_bx_courses_page_block_title_featured_profiles"><![CDATA[Featured Courses]]></string>
+    <string name="_bx_courses_page_block_title_latest_profiles"><![CDATA[New Courses]]></string>
+    <string name="_bx_courses_page_block_title_system_manage"><![CDATA[Manage Own Courses]]></string>
+    <string name="_bx_courses_page_block_title_system_manage_administration"><![CDATA[Manage All Courses]]></string>
+    <string name="_bx_courses_page_block_title_manage"><![CDATA[Manage Courses]]></string>
+    <string name="_bx_courses_page_block_title_top_profiles"><![CDATA[Top Courses]]></string>
+    <string name="_bx_courses_page_block_title_joined_profiles"><![CDATA[Attended Courses]]></string>
+    <string name="_bx_courses_page_block_title_entry_all_actions"><![CDATA[All Actions]]></string>
+    <string name="_bx_courses_page_block_title_entry_social_sharing"><![CDATA[Share]]></string>
+    <string name="_bx_courses_page_block_title_system_entry_node"><![CDATA[Lesson]]></string>
+    <string name="_bx_courses_page_block_title_entry_node"><![CDATA[]]></string>
+    <string name="_bx_courses_page_block_title_categories"><![CDATA[Courses Categories]]></string>
+    <string name="_bx_courses_page_block_title_invite_to_course"><![CDATA[Invite Students to the Course]]></string>
+    <string name="_bx_courses_page_block_title_search_form"><![CDATA[Search Form]]></string>
+    <string name="_bx_courses_page_block_title_search_results"><![CDATA[Search Results]]></string>
+    <string name="_bx_courses_page_block_title_search_form_cmts"><![CDATA[Comments Search Form]]></string>
+    <string name="_bx_courses_page_block_title_search_results_cmts"><![CDATA[Comments Search Results]]></string>
+    <string name="_bx_courses_page_block_title_sys_featured_entries_view_showcase"><![CDATA[Featured Courses (Showcase View)]]></string>
+    <string name="_bx_courses_page_block_title_featured_entries_view_showcase"><![CDATA[Featured Courses]]></string>
+    <string name="_bx_courses_page_block_title_sys_recommended_entries_view_showcase"><![CDATA[Recommended Courses (Showcase View)]]></string>
+    <string name="_bx_courses_page_block_title_recommended_entries_view_showcase"><![CDATA[Recommended Courses]]></string>
+    <string name="_bx_courses_page_block_title_sys_labels_tree"><![CDATA[Labels tree]]></string>
+    <string name="_bx_courses_page_block_title_labels_tree"><![CDATA[Labels tree]]></string>
+    <string name="_bx_courses_page_block_title_sys_browse_by_label"><![CDATA[Browse by label]]></string>
+    <string name="_bx_courses_page_block_title_browse_by_label"><![CDATA[Browse by label]]></string>
+    <string name="_bx_courses_page_block_title_sys_labels_breadcrumbs"><![CDATA[Labels breadcrumbs]]></string>
+    <string name="_bx_courses_page_block_title_labels_breadcrumbs"><![CDATA[Labels breadcrumbs]]></string>
+    <string name="_bx_courses_page_block_title_sys_cover_block"><![CDATA[Cover]]></string>
+    <string name="_bx_courses_page_block_title_cover_block"><![CDATA[Cover]]></string>
+
+
+    <string name="_bx_courses_menu_title_submenu"><![CDATA[Courses Submenu]]></string>
+    <string name="_bx_courses_menu_set_title_submenu"><![CDATA[Courses Submenu]]></string>
+    <string name="_bx_courses_menu_set_title_view_profile_actions"><![CDATA[Course Actions]]></string>
+    <string name="_bx_courses_menu_title_view_profile_actions"><![CDATA[Course Actions]]></string>
+    <string name="_bx_courses_menu_set_title_view_profile_actions_more"><![CDATA[Course More Actions]]></string>
+    <string name="_bx_courses_menu_title_view_profile_actions_more"><![CDATA[Course More Actions]]></string>
+    <string name="_bx_courses_menu_title_view_profile_meta"><![CDATA[Course Meta]]></string>
+    <string name="_bx_courses_menu_set_title_view_profile_meta"><![CDATA[Course Meta]]></string>
+    <string name="_bx_courses_menu_set_title_view_profile_submenu"><![CDATA[View Course Submenu]]></string>
+    <string name="_bx_courses_menu_title_view_profile_submenu"><![CDATA[View Course Submenu]]></string>
+    <string name="_bx_courses_menu_title_entries_my"><![CDATA[Profile Courses Action Menu]]></string>
+    <string name="_bx_courses_menu_set_title_entries_my"><![CDATA[Profile Courses Menu]]></string>
+    <string name="_bx_courses_menu_set_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+    <string name="_bx_courses_menu_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+    <string name="_bx_courses_menu_item_title_system_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_courses_menu_item_title_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_courses_menu_item_title_system_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_courses_menu_item_title_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_courses_menu_item_title_system_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_courses_menu_item_title_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_courses_menu_item_title_system_edit_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_courses_menu_item_title_edit_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_courses_menu_item_title_system_edit_pricing"><![CDATA[Edit Pricing]]></string>
+    <string name="_bx_courses_menu_item_title_edit_pricing"><![CDATA[Edit Pricing]]></string>
+    <string name="_bx_courses_menu_item_title_system_edit_content"><![CDATA[Edit Content]]></string>
+    <string name="_bx_courses_menu_item_title_edit_content"><![CDATA[Edit Content]]></string>
+    <string name="_bx_courses_menu_item_title_system_edit_profile"><![CDATA[Edit Course]]></string>
+    <string name="_bx_courses_menu_item_title_edit_profile"><![CDATA[Edit Course]]></string>
+    <string name="_bx_courses_menu_item_title_system_delete_profile"><![CDATA[Delete Course]]></string>
+    <string name="_bx_courses_menu_item_title_delete_profile"><![CDATA[Delete Course]]></string>
+    <string name="_bx_courses_menu_item_title_system_delete_account"><![CDATA[Delete Account]]></string>
+    <string name="_bx_courses_menu_item_title_delete_account"><![CDATA[Delete Account]]></string>
+    <string name="_bx_courses_menu_item_title_system_profile_type"><![CDATA[Course]]></string>
+    <string name="_bx_courses_menu_item_title_profile_type"><![CDATA[Course]]></string>
+    <string name="_bx_courses_menu_item_title_system_create_profile"><![CDATA[Course]]></string>
+    <string name="_bx_courses_menu_item_title_create_profile"><![CDATA[Course]]></string>
+    <string name="_bx_courses_menu_item_title_system_view_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_courses_menu_item_title_view_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_courses_menu_item_title_system_view_joined_entries"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_menu_item_title_view_joined_entries"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_menu_item_title_system_view_fans"><![CDATA[Students]]></string>
+    <string name="_bx_courses_menu_item_title_view_fans"><![CDATA[Students]]></string>
+    <string name="_bx_courses_menu_item_title_system_view_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_courses_menu_item_title_view_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_courses_menu_item_title_system_view_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_courses_menu_item_title_view_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_courses_menu_item_title_system_view_profile_view"><![CDATA[Course]]></string>
+    <string name="_bx_courses_menu_item_title_view_profile_view"><![CDATA[Course]]></string>
+    <string name="_bx_courses_menu_item_title_system_view_profile_more_auto"><![CDATA[More]]></string>
+    <string name="_bx_courses_menu_item_title_view_profile_more_auto"><![CDATA[More]]></string>
+    <string name="_bx_courses_menu_item_title_system_invite"><![CDATA[Invite]]></string>
+    <string name="_bx_courses_menu_item_title_invite"><![CDATA[Invite]]></string>
+
+    <string name="_bx_courses_menu_item_title_system_befriend"><![CDATA[Add friend]]></string>
+    <string name="_bx_courses_menu_item_title_befriend"><![CDATA[Add friend]]></string>
+    <string name="_bx_courses_menu_item_title_befriend_sent"><![CDATA[Add friend (requested)]]></string>
+    <string name="_bx_courses_menu_item_title_befriend_confirm"><![CDATA[Accept friend request]]></string>
+    <string name="_bx_courses_menu_item_title_system_unfriend"><![CDATA[Unfriend]]></string>
+    <string name="_bx_courses_menu_item_title_unfriend"><![CDATA[Unfriend]]></string>
+    <string name="_bx_courses_menu_item_title_unfriend_cancel_request"><![CDATA[Cancel friend request]]></string>
+    <string name="_bx_courses_menu_item_title_unfriend_reject_request"><![CDATA[Reject friend request]]></string>
+
+    <string name="_bx_courses_menu_item_title_system_become_fan"><![CDATA[Attend Course]]></string>
+    <string name="_bx_courses_menu_item_title_become_fan"><![CDATA[Attend Course]]></string>
+    <string name="_bx_courses_menu_item_title_become_fan_sent"><![CDATA[Attend Course (requested)]]></string>
+    <string name="_bx_courses_menu_item_title_become_fan_confirm"><![CDATA[Accept Request]]></string>
+    <string name="_bx_courses_menu_item_title_system_leave_entry"><![CDATA[Leave Course]]></string>
+    <string name="_bx_courses_menu_item_title_leave_entry"><![CDATA[Leave Course]]></string>
+    <string name="_bx_courses_menu_item_title_leave_entry_cancel_request"><![CDATA[Cancel Request]]></string>
+    <string name="_bx_courses_menu_item_title_leave_entry_reject_request"><![CDATA[Reject Request]]></string>
+
+    <string name="_bx_courses_menu_item_title_system_pay_and_join"><![CDATA[Pay & Join]]></string>
+    <string name="_bx_courses_menu_item_title_pay_and_join"><![CDATA[Pay & Join]]></string>
+
+    <string name="_bx_courses_menu_item_title_system_subscribe"><![CDATA[Follow]]></string>
+    <string name="_bx_courses_menu_item_title_subscribe"><![CDATA[Follow]]></string>
+    <string name="_bx_courses_menu_item_title_system_unsubscribe"><![CDATA[Unfollow]]></string>
+    <string name="_bx_courses_menu_item_title_unsubscribe"><![CDATA[Unfollow]]></string>
+    <string name="_bx_courses_menu_item_title_system_more_actions"><![CDATA[More Actions]]></string>
+    <string name="_bx_courses_menu_item_title_more_actions"><![CDATA[]]></string>
+    <string name="_bx_courses_menu_item_title_system_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_courses_menu_item_title_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_courses_menu_item_title_system_manage_my_entries"><![CDATA[My courses]]></string>
+    <string name="_bx_courses_menu_item_title_manage_my_entries"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_menu_item_title_system_followings"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_menu_item_title_followings"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_menu_item_title_system_entries_recent"><![CDATA[New]]></string>
+    <string name="_bx_courses_menu_item_title_entries_recent"><![CDATA[New]]></string>
+    <string name="_bx_courses_menu_item_title_system_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_courses_menu_item_title_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_courses_menu_item_title_system_entries_joined"><![CDATA[Attended]]></string>
+    <string name="_bx_courses_menu_item_title_entries_joined"><![CDATA[Attended]]></string>
+    <string name="_bx_courses_menu_item_title_system_entries_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_courses_menu_item_title_entries_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_courses_menu_item_title_system_entries_home"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_menu_item_title_entries_home"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_menu_item_title_system_admt_entries"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_menu_item_title_admt_entries"><![CDATA[Courses]]></string>
+    <string name="_bx_courses_menu_item_title_manage_my"><![CDATA[My Courses]]></string>
+    <string name="_bx_courses_menu_item_title_manage_all"><![CDATA[All Courses]]></string>
+    <string name="_bx_courses_menu_item_title_system_entries_top"><![CDATA[Top]]></string>
+    <string name="_bx_courses_menu_item_title_entries_top"><![CDATA[Top]]></string>
+    <string name="_bx_courses_menu_item_title_system_sm_privacy"><![CDATA[Privacy]]></string>
+    <string name="_bx_courses_menu_item_title_sm_privacy"><![CDATA[Privacy]]></string>
+    <string name="_bx_courses_menu_item_title_system_sm_members"><![CDATA[Students]]></string>
+    <string name="_bx_courses_menu_item_title_sm_members"><![CDATA[{0} students]]></string>
+    <string name="_bx_courses_menu_item_title_system_sm_join"><![CDATA[Attend]]></string>
+    <string name="_bx_courses_menu_item_title_sm_join"><![CDATA[Attend]]></string>
+    <string name="_bx_courses_menu_item_title_sm_join_paid"><![CDATA[Pay & Attend]]></string>
+    <string name="_bx_courses_menu_item_title_sm_join_confirm"><![CDATA[Accept attend request]]></string>
+    <string name="_bx_courses_menu_item_title_sm_leave"><![CDATA[Leave]]></string>
+    <string name="_bx_courses_menu_item_title_sm_leave_reject"><![CDATA[Reject attend request]]></string>
+    <string name="_bx_courses_menu_item_title_sm_leave_cancel"><![CDATA[Cancel attend request]]></string>
+    <string name="_bx_courses_menu_item_title_system_sm_reports"><![CDATA[Reports]]></string>
+    <string name="_bx_courses_menu_item_title_system_sm_pass"><![CDATA[Pass]]></string>
+
+    <string name="_bx_courses_menu_title_content_add"><![CDATA[Add Content]]></string>
+    <string name="_bx_courses_menu_set_title_content_add"><![CDATA[Add Content]]></string>
+    <string name="_bx_courses_menu_item_title_node_data_steps"><![CDATA[Steps]]></string>
+    <string name="_bx_courses_menu_item_title_node_data_attachments"><![CDATA[Attachments]]></string>
+
+    <string name="_bx_courses_grid_filter_item_title_adm_select_one_filter1"><![CDATA[All Statuses]]></string>
+    <string name="_bx_courses_grid_filter_item_title_adm_select_one_filter2"><![CDATA[Any Audience]]></string>
+    <string name="_bx_courses_grid_filter_item_title_adm_active"><![CDATA[Active]]></string>
+    <string name="_bx_courses_grid_filter_item_title_adm_hidden"><![CDATA[Hidden]]></string>
+    <string name="_bx_courses_grid_filter_item_title_adm_pending"><![CDATA[Pending]]></string>
+    <string name="_bx_courses_grid_filter_item_title_adm_suspended"><![CDATA[Suspended]]></string>
+    <string name="_bx_courses_grid_column_title_adm_active"><![CDATA[Active]]></string>
+    <string name="_bx_courses_grid_column_title_adm_name"><![CDATA[Name]]></string>
+    <string name="_bx_courses_grid_column_title_adm_added"><![CDATA[Created]]></string>
+    <string name="_bx_courses_grid_column_title_adm_account"><![CDATA[Account]]></string>
+    <string name="_bx_courses_grid_action_title_adm_audit_context"><![CDATA[Audit actions in context]]></string>
+    <string name="_bx_courses_grid_action_title_adm_audit_content"><![CDATA[Audit actions with context]]></string>
+    <string name="_bx_courses_grid_action_title_adm_clear_reports"><![CDATA[Сlear reports]]></string>
+    <string name="_bx_courses_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_courses_grid_action_title_adm_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_courses_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
+    <string name="_bx_courses_grid_action_err_delete"><![CDATA[Cannot delete the selected course(s).]]></string>
+    <string name="_bx_courses_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
+
+    <string name="_bx_courses_grid_column_title_name"><![CDATA[Name]]></string>
+    <string name="_bx_courses_grid_column_title_role_id"><![CDATA[Role]]></string>
+    <string name="_bx_courses_grid_column_title_period"><![CDATA[Period]]></string>
+    <string name="_bx_courses_grid_column_title_price"><![CDATA[Price]]></string>
+    <string name="_bx_courses_grid_action_title_add"><![CDATA[Add]]></string>
+    <string name="_bx_courses_grid_action_title_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_courses_grid_action_title_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_courses_grid_action_title_buy"><![CDATA[Buy]]></string>
+    <string name="_bx_courses_grid_action_title_buy_title"><![CDATA[One-time payment]]></string>
+    <string name="_bx_courses_grid_action_title_subscribe"><![CDATA[Subscribe]]></string>
+    <string name="_bx_courses_grid_action_title_subscribe_title"><![CDATA[Create a subscription with recurring billing]]></string>
+    <string name="_bx_courses_grid_action_title_choose"><![CDATA[Choose]]></string>
+    <string name="_bx_courses_grid_action_title_choose_title"><![CDATA[Switch to the level for free]]></string>
+    <string name="_bx_courses_popup_title_price_add"><![CDATA[Add Pricing Plan]]></string>
+    <string name="_bx_courses_popup_title_price_edit"><![CDATA[Edit Pricing Plan]]></string>
+
+    <string name="_bx_courses_grid_column_title_cnt_active"><![CDATA[]]></string>
+    <string name="_bx_courses_grid_column_title_cnt_title"><![CDATA[Title]]></string>
+    <string name="_bx_courses_grid_column_title_cnt_cn_l2"><![CDATA[Themes]]></string>
+    <string name="_bx_courses_grid_column_title_cnt_cn_l3"><![CDATA[Lessons]]></string>
+    <string name="_bx_courses_grid_column_title_cnt_counters"><![CDATA[Info]]></string>
+    <string name="_bx_courses_grid_column_title_cnt_content_id"><![CDATA[Content]]></string>
+    <string name="_bx_courses_grid_column_title_cnt_content_type"><![CDATA[Type]]></string>
+    <string name="_bx_courses_grid_column_title_cnt_usage"><![CDATA[Usage]]></string>
+    <string name="_bx_courses_grid_column_title_cnt_added"><![CDATA[Added]]></string>
+    <string name="_bx_courses_grid_action_title_cnt_back"><![CDATA[Back]]></string>
+    <string name="_bx_courses_grid_action_title_cnt_add"><![CDATA[Add {node}]]></string>
+    <string name="_bx_courses_grid_action_title_cnt_add_step"><![CDATA[Add step]]></string>
+    <string name="_bx_courses_grid_action_title_cnt_add_attachment"><![CDATA[Add attachment]]></string>
+    <string name="_bx_courses_grid_action_title_cnt_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_courses_grid_action_title_cnt_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_courses_popup_title_cnt_add"><![CDATA[Add {node}]]></string>
+    <string name="_bx_courses_popup_title_cnt_edit"><![CDATA[Edit {node}]]></string>
+
+    <string name="_bx_courses_chart_growth"><![CDATA[Courses: Growth]]></string>
+    <string name="_bx_courses_chart_growth_speed"><![CDATA[Courses: Growth Speed]]></string>
+
+    <string name="_bx_courses_search_extended"><![CDATA[Courses Search]]></string>
+    <string name="_bx_courses_search_extended_cmts"><![CDATA[Courses Comments Search]]></string>
+
+    <string name="_bx_courses_txt_account_pending"><![CDATA[This course is pending approval, please wait until it is activated.]]></string>
+    <string name="_bx_courses_txt_account_suspended"><![CDATA[This course is suspended.]]></string>
+    <string name="_bx_courses_txt_status_hidden"><![CDATA[This course was hidden.]]></string>
+    <string name="_bx_courses_txt_N_fans"><![CDATA[{0} students]]></string>
+    <string name="_bx_courses_txt_role"><![CDATA[Role]]></string>
+    <string name="_bx_courses_txt_role_added"><![CDATA[Added]]></string>
+    <string name="_bx_courses_txt_role_expired"><![CDATA[Expires]]></string>
+    <string name="_bx_courses_txt_sample_single"><![CDATA[course]]></string>
+    <string name="_bx_courses_txt_sample_single_with_article"><![CDATA[a course]]></string>
+    <string name="_bx_courses_txt_sample_comment_single"><![CDATA[comment to course]]></string>
+    <string name="_bx_courses_txt_sample_vote_single"><![CDATA[vote to course]]></string>
+    <string name="_bx_courses_txt_sample_score_up_single"><![CDATA[score up vote to course]]></string>
+    <string name="_bx_courses_txt_sample_score_down_single"><![CDATA[score down vote to course]]></string>
+    <string name="_bx_courses_txt_set_role"><![CDATA[Set Role]]></string>
+    <string name="_bx_courses_txt_private_entry"><![CDATA[Private course]]></string>
+    <string name="_bx_courses_txt_invite_popup_text"><![CDATA[You have been invited to a course. Click the button below to accept the invitation.]]></string>
+    <string name="_bx_courses_txt_invite_popup_title"><![CDATA[Invitation to a course]]></string>
+    <string name="_bx_courses_txt_invite_popup_button_accept"><![CDATA[Accept]]></string>
+    <string name="_bx_courses_txt_invite_popup_button_decline"><![CDATA[Decline]]></string>
+    <string name="_bx_courses_txt_invite_popup_error_invitation_absent"><![CDATA[Sorry, an error has occurred. You can not join this course. The invitation is expired or deleted.]]></string>
+    <string name="_bx_courses_txt_invite_popup_error_invitation_wrong_user"><![CDATA[Sorry, an error has occurred. You can not join this course. This invitation cannot be used by you.]]></string>
+    <string name="_bx_courses_txt_n_unit"><![CDATA[{0} ({1})]]></string>
+    <string name="_bx_courses_txt_cart_item_title"><![CDATA[{0}: {1} ({2} {3})]]></string>
+    <string name="_bx_courses_txt_cart_item_title_lifetime"><![CDATA[{0}: {1} (lifetime)]]></string>    
+    <string name="_bx_courses_txt_sample_l1_single"><![CDATA[module]]></string>
+    <string name="_bx_courses_txt_sample_l1_plural"><![CDATA[modules]]></string>
+    <string name="_bx_courses_txt_sample_l2_single"><![CDATA[theme]]></string>
+    <string name="_bx_courses_txt_sample_l2_plural"><![CDATA[themes]]></string>
+    <string name="_bx_courses_txt_sample_l3_single"><![CDATA[lesson]]></string>
+    <string name="_bx_courses_txt_sample_l3_plural"><![CDATA[lessons]]></string>
+    <string name="_bx_courses_txt_status_not_started"><![CDATA[not started]]></string>
+    <string name="_bx_courses_txt_status_in_process"><![CDATA[in process]]></string>
+    <string name="_bx_courses_txt_status_completed"><![CDATA[completed]]></string>
+    <string name="_bx_courses_txt_pass_start"><![CDATA[Start]]></string>
+    <string name="_bx_courses_txt_pass_continue"><![CDATA[Continue]]></string>
+    <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_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_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>
+    <string name="_bx_courses_txt_data_type_bx_posts"><![CDATA[theory]]></string>
+    <string name="_bx_courses_txt_data_type_bx_videos"><![CDATA[video]]></string>
+    <string name="_bx_courses_txt_data_usage_0"><![CDATA[step]]></string>
+    <string name="_bx_courses_txt_data_usage_1"><![CDATA[attachment]]></string>
+    <string name="_bx_courses_txt_back_step"><![CDATA[Back]]></string>
+    <string name="_bx_courses_txt_next_step"><![CDATA[Next]]></string>
+    <string name="_bx_courses_txt_download"><![CDATA[Download]]></string>
+    
+
+    <string name="_bx_courses_msg_performed"><![CDATA[The action was successfully performed.]]></string>
+    <string name="_bx_courses_err_price_duplicate"><![CDATA[Similar pricing plan is already exists.]]></string>
+    <string name="_bx_courses_err_cannot_perform"><![CDATA[An error appeared. Please report.]]></string>
+
+    <string name="_bx_courses_alert_action_fan_added"><![CDATA[member joined]]></string>
+    <string name="_bx_courses_txt_ntfs_fan_added"><![CDATA[Course <b class="bx-ntfs-content-profile">{owner_name}</b> has got a member <b class="bx-ntfs-content-entry">{subentry_sample}</b>]]></string>
+    <string name="_bx_courses_alert_action_join_invitation_notif"><![CDATA[join invitation]]></string>
+    <string name="_bx_courses_alert_action_join_request"><![CDATA[join request]]></string>
+    <string name="_bx_courses_txt_ntfs_join_invitation"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> course's representative invites <b class="bx-ntfs-content-entry">{subentry_sample}</b> to join course]]></string>
+    <string name="_bx_courses_txt_ntfs_join_request"><![CDATA[Course <b class="bx-ntfs-content-profile">{owner_name}</b> has a join request from <b class="bx-ntfs-content-entry">{subentry_sample}</b>]]></string>
+    <string name="_bx_courses_txt_ntfs_timeline_post_common"><![CDATA[New post <b class="bx-ntfs-content-entry">{subentry_sample}</b> in <b class="bx-ntfs-content-profile">{owner_name}</b> course timeline]]></string>
+
+    <string name="_bx_courses_pre_lists_cats"><![CDATA[Course categories]]></string>
+    <string name="_bx_courses_cat_General"><![CDATA[General]]></string>
+
+    <string name="_bx_courses_pre_lists_roles"><![CDATA[Course roles]]></string>
+    <string name="_bx_courses_role_regular"><![CDATA[Student]]></string>
+    <string name="_bx_courses_role_administrator"><![CDATA[Instructor]]></string>
+    <string name="_bx_courses_role_moderator"><![CDATA[Assistant]]></string>
+
+    <string name="_bx_courses_pre_lists_period_units"><![CDATA[Billing Interval]]></string>
+    <string name="_bx_courses_period_unit_day"><![CDATA[day]]></string>
+    <string name="_bx_courses_period_unit_week"><![CDATA[week]]></string>
+    <string name="_bx_courses_period_unit_month"><![CDATA[month]]></string>
+    <string name="_bx_courses_period_unit_year"><![CDATA[year]]></string>
+
+    <string name="_bx_courses_email_join_request"><![CDATA[Course join request]]></string>
+    <string name="_bx_courses_email_join_request_subject"><![CDATA[Request to join your course]]></string>
+    <string name="_bx_courses_email_join_request_body"><![CDATA[{email_header}
+<p>New request to join <a href="{EntryUrl}">{EntryTitle}</a> course from <a href="{NewMemberUrl}">{NewMemberDisplayName}</a></p>
+{email_footer}]]></string>
+
+    <string name="_bx_courses_email_join_reject"><![CDATA[Course join request was rejected]]></string>
+    <string name="_bx_courses_email_join_reject_subject"><![CDATA[Request to join a course was rejected]]></string>
+    <string name="_bx_courses_email_join_reject_body"><![CDATA[{email_header}
+<p>Your request to join <a href="{EntryUrl}">{EntryTitle}</a> course was rejected by course admin.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_courses_email_join_confirm"><![CDATA[Course join request was confirmed]]></string>
+    <string name="_bx_courses_email_join_confirm_subject"><![CDATA[Your request to join a course was confirmed]]></string>
+    <string name="_bx_courses_email_join_confirm_body"><![CDATA[{email_header}
+<p>Your request to join <a href="{EntryUrl}">{EntryTitle}</a> course was confirmed by the course admin.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_courses_email_fan_remove"><![CDATA[Profile was removed from course fans]]></string>
+    <string name="_bx_courses_email_fan_remove_subject"><![CDATA[Your profile was removed from the course]]></string>
+    <string name="_bx_courses_email_fan_remove_body"><![CDATA[{email_header}
+<p>Your profile was removed from <a href="{EntryUrl}">{EntryTitle}</a> course by the course admin.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_courses_email_fan_become_admin"><![CDATA[Course instructor status was granted]]></string>
+    <string name="_bx_courses_email_fan_become_admin_subject"><![CDATA[You are a course instructor now]]></string>
+    <string name="_bx_courses_email_fan_become_admin_body"><![CDATA[{email_header}
+<p>You are an instructor of <a href="{EntryUrl}">{EntryTitle}</a> course now.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_courses_email_admin_become_fan"><![CDATA[Course instructor status was revoked]]></string>
+    <string name="_bx_courses_email_admin_become_fan_subject"><![CDATA[Your course instructor status was revoked]]></string>
+    <string name="_bx_courses_email_admin_become_fan_body"><![CDATA[{email_header}
+<p>Your instructor status was revoked from <a href="{EntryUrl}">{EntryTitle}</a> course.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_courses_email_invitation"><![CDATA[Invitation to course]]></string>
+    <string name="_bx_courses_email_invitation_subject"><![CDATA[Invitation to "{EntryTitle}" course]]></string>
+    <string name="_bx_courses_email_invitation_body"><![CDATA[{email_header}
+<p><a href="{InviterUrl}">{InviterDisplayName}</a> invited you to <a href="{EntryUrl}">{EntryTitle} course</a>.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_courses_email_set_role"><![CDATA[Course member status was changed]]></string>
+    <string name="_bx_courses_email_set_role_subject"><![CDATA[Your course status was changed]]></string>
+    <string name="_bx_courses_email_set_role_body"><![CDATA[{email_header}
+<p>Your status in <a href="{EntryUrl}">{EntryTitle}</a> course was changed to <b>{Role}</b>.</p>
+{email_footer}]]></string>
+</resources>

+ 60 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/install/sql/disable.sql

@@ -0,0 +1,60 @@
+
+-- SETTINGS
+SET @iTypeId = (SELECT `ID` FROM `sys_options_types` WHERE `name` = 'bx_courses' LIMIT 1);
+SET @iCategId = (SELECT `ID` FROM `sys_options_categories` WHERE `type_id` = @iTypeId LIMIT 1);
+DELETE FROM `sys_options` WHERE `category_id` = @iCategId;
+DELETE FROM `sys_options_categories` WHERE `type_id` = @iTypeId;
+DELETE FROM `sys_options_types` WHERE `id` = @iTypeId;
+
+-- PAGES
+DELETE FROM `sys_objects_page` WHERE `module` = 'bx_courses';
+DELETE FROM `sys_pages_blocks` WHERE `module` = 'bx_courses' OR `object` IN('bx_courses_create_profile', 'bx_courses_delete_profile', 'bx_courses_join_profile', 'bx_courses_edit_profile', 'bx_courses_edit_profile_cover', 'bx_courses_invite', 'bx_courses_view_profile', 'bx_courses_view_profile_closed', 'bx_courses_view_profile_node', 'bx_courses_profile_info', 'bx_courses_profile_pricing', 'bx_courses_profile_comments', 'bx_courses_home', 'bx_courses_fans', 'bx_courses_joined', 'bx_courses_favorites', 'bx_courses_top', 'bx_courses_search', 'bx_courses_manage', 'bx_courses_administration');
+
+-- MENU
+DELETE FROM `sys_objects_menu` WHERE `module` = 'bx_courses';
+DELETE FROM `sys_menu_sets` WHERE `module` = 'bx_courses';
+DELETE FROM `sys_menu_items` WHERE `module` = 'bx_courses' OR `set_name` IN('bx_courses_view_submenu', 'bx_courses_submenu', 'bx_courses_view_actions', 'bx_courses_view_actions_more', 'bx_courses_view_actions_all', 'bx_courses_view_meta', 'bx_courses_content_add', 'bx_courses_my', 'bx_courses_snippet_meta', 'bx_courses_menu_manage_tools');
+
+-- ACL
+DELETE `sys_acl_actions`, `sys_acl_matrix` FROM `sys_acl_actions`, `sys_acl_matrix` WHERE `sys_acl_matrix`.`IDAction` = `sys_acl_actions`.`ID` AND `sys_acl_actions`.`Module` = 'bx_courses';
+DELETE FROM `sys_acl_actions` WHERE `Module` = 'bx_courses';
+
+-- METATAGS
+DELETE FROM `sys_objects_metatags` WHERE `object` = 'bx_courses';
+
+-- CATEGORY
+DELETE FROM `sys_objects_category` WHERE `object` = 'bx_courses_cats';
+
+-- SEARCH
+DELETE FROM `sys_objects_search` WHERE `ObjectName` = 'bx_courses';
+
+-- CONNECTIONS
+DELETE FROM `sys_objects_connection` WHERE `object` = 'bx_courses_fans';
+
+-- STATS
+DELETE FROM `sys_statistics` WHERE `name` LIKE 'bx_courses%';
+
+-- CHARTS
+DELETE FROM `sys_objects_chart` WHERE `object` LIKE 'bx_courses%';
+
+-- GRIDS
+DELETE FROM `sys_objects_grid` WHERE `object` IN ('bx_courses_administration', 'bx_courses_moderation', 'bx_courses_common', 'bx_courses_fans', 'bx_courses_invites', 'bx_courses_prices_manage', 'bx_courses_prices_view', 'bx_courses_cnt_structure_manage', 'bx_courses_cnt_data_manage');
+DELETE FROM `sys_grid_fields` WHERE `object` IN ('bx_courses_administration', 'bx_courses_moderation', 'bx_courses_common', 'bx_courses_fans', 'bx_courses_invites', 'bx_courses_prices_manage', 'bx_courses_prices_view', 'bx_courses_cnt_structure_manage', 'bx_courses_cnt_data_manage');
+DELETE FROM `sys_grid_actions` WHERE `object` IN ('bx_courses_administration', 'bx_courses_moderation', 'bx_courses_common', 'bx_courses_fans', 'bx_courses_invites', 'bx_courses_prices_manage', 'bx_courses_prices_view', 'bx_courses_cnt_structure_manage', 'bx_courses_cnt_data_manage');
+
+-- ALERTS
+SET @iHandler := (SELECT `id` FROM `sys_alerts_handlers` WHERE `name` = 'bx_courses' LIMIT 1);
+DELETE FROM `sys_alerts` WHERE `handler_id` = @iHandler;
+DELETE FROM `sys_alerts_handlers` WHERE `id` = @iHandler;
+
+-- PRIVACY 
+DELETE FROM `sys_objects_privacy` WHERE `object` IN ('bx_courses_allow_view_to', 'bx_courses_allow_view_notification_to', 'bx_courses_allow_post_to', 'bx_courses_allow_view_favorite_list');
+
+-- EMAIL TEMPLATES
+DELETE FROM `sys_email_templates` WHERE `Module` = 'bx_courses';
+
+-- UPLOADERS
+DELETE FROM `sys_objects_uploader` WHERE `object` IN ('bx_courses_cover_crop', 'bx_courses_picture_crop');
+
+-- CRON
+DELETE FROM `sys_cron_jobs` WHERE `name` IN ('bx_courses_pruning');

+ 740 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/install/sql/enable.sql

@@ -0,0 +1,740 @@
+
+-- SETTINGS
+
+SET @iTypeOrder = (SELECT MAX(`order`) FROM `sys_options_types` WHERE `group` = 'modules');
+INSERT INTO `sys_options_types`(`group`, `name`, `caption`, `icon`, `order`) VALUES 
+('modules', 'bx_courses', '_bx_courses', 'bx_courses@modules/boonex/courses/|std-icon.svg', IF(ISNULL(@iTypeOrder), 1, @iTypeOrder + 1));
+SET @iTypeId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, 'bx_courses', '_bx_courses', 1);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `extra`, `check`, `check_error`, `order`) VALUES
+('bx_courses_enable_auto_approve', 'on', @iCategId, '_bx_courses_option_enable_auto_approve', 'checkbox', '', '', '', 0),
+('bx_courses_enable_content', '', @iCategId, '_bx_courses_option_enable_content', 'checkbox', '', '', '', 5),
+('bx_courses_content_level_max', '3', @iCategId, '_bx_courses_option_content_level_max', 'select', '1,2,3', '', '', 6),
+('bx_courses_content_modules_st', '', @iCategId, '_bx_courses_option_content_modules_step', 'list', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:30:"get_options_content_modules_st";}', '', '', 7),
+('bx_courses_content_modules_at', '', @iCategId, '_bx_courses_option_content_modules_attachment', 'list', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:30:"get_options_content_modules_at";}', '', '', 8),
+('bx_courses_num_connections_quick', '6', @iCategId, '_bx_courses_option_num_connections_quick', 'digit', '', '', '', 10),
+('bx_courses_per_page_browse', '24', @iCategId, '_bx_courses_option_per_page_browse', 'digit', '', '', '', 11),
+('bx_courses_num_rss', '10', @iCategId, '_bx_courses_option_num_rss', 'digit', '', '', '', 12),
+('bx_courses_per_page_browse_showcase', '32', @iCategId, '_sys_option_per_page_browse_showcase', 'digit', '', '', '', 15),
+('bx_courses_per_page_browse_recommended', '10', @iCategId, '_sys_option_per_page_browse_recommended', 'digit', '', '', '', 16),
+('bx_courses_per_page_for_favorites_lists', '5', @iCategId, '_bx_courses_option_per_page_for_favorites_lists', 'digit', '', '', '', 17),
+('bx_courses_searchable_fields', 'name,desc', @iCategId, '_bx_courses_option_searchable_fields', 'list', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:21:"get_searchable_fields";}', '', '', 30),
+('bx_courses_members_mode', '', @iCategId, '_bx_courses_option_members_mode', 'select', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:24:"get_options_members_mode";}', '', '', 40), 
+('bx_courses_internal_notifications', '', @iCategId, '_bx_courses_option_internal_notifications', 'checkbox', '', '', '', 50);
+
+-- PAGES
+
+-- PAGE: create profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_create_profile', 'create-course-profile', '_bx_courses_page_title_sys_create_profile', '_bx_courses_page_title_create_profile', 'bx_courses', 5, 2147483647, 1, 'page.php?i=create-course-profile', '', '', '', 0, 1, 0, '', '');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_courses_create_profile', 1, 'bx_courses', '_bx_courses_page_block_title_create_profile', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:13:"entity_create";}', 0, 1, 1);
+
+-- PAGE: view profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_view_profile', 'view-course-profile', '_bx_courses_page_title_sys_view_profile', '_bx_courses_page_title_view_profile', 'bx_courses', 12, 2147483647, 1, 'page.php?i=view-course-profile', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+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_courses_view_profile', 1, 'bx_courses', '', '_bx_courses_page_block_title_entry_social_sharing', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:21:"entity_social_sharing";}', 0, 0, 0, 0),
+('bx_courses_view_profile', 1, 'bx_courses', '', '_bx_courses_page_block_title_entry_all_actions', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:18:"entity_all_actions";}', 0, 0, 0, 0),
+('bx_courses_view_profile', 2, 'bx_courses', '', '_bx_courses_page_block_title_profile_description', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:17:"entity_text_block";}', 0, 0, 1, 0),
+('bx_courses_view_profile', 2, 'bx_courses', '', '_bx_courses_page_block_title_profile_structure_l1', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:25:"entity_structure_l1_block";}', 0, 0, 1, 1),
+('bx_courses_view_profile', 2, 'bx_courses', '', '_bx_courses_page_block_title_profile_structure_l2', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:25:"entity_structure_l2_block";}', 0, 0, 1, 2),
+('bx_courses_view_profile', 3, 'bx_courses', '', '_bx_courses_page_block_title_profile_location', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:15:"entity_location";}', 0, 0, 0, 0),
+('bx_courses_view_profile', 3, 'bx_courses', '', '_bx_courses_page_block_title_profile_info', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:11:"entity_info";}', 0, 0, 1, 0),
+('bx_courses_view_profile', 3, 'bx_courses', '', '_bx_courses_page_block_title_admins', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:6:"admins";}', 0, 0, 1, 1),
+('bx_courses_view_profile', 3, 'bx_courses', '', '_bx_courses_page_block_title_fans', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:19:"fans_without_admins";}', 0, 0, 1, 2),
+('bx_courses_view_profile', 3, 'bx_courses', '', '_bx_courses_page_block_title_profile_location', 3, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:13:"locations_map";s:6:"params";a:2:{i:0;s:10:"bx_courses";i:1;s:12:"{content_id}";}s:5:"class";s:20:"TemplServiceMetatags";}', 0, 0, 1, 3),
+('bx_courses_view_profile', 4, 'bx_courses', '', '_bx_courses_page_block_title_profile_comments', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:15:"entity_comments";}', 0, 0, 0, 0),
+('bx_courses_view_profile', 2, 'bx_courses', '', '_bx_courses_page_block_title_entry_reports', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:10:\"bx_courses\";s:6:\"method\";s:14:\"entity_reports\";}', 0, 0, 1, 6);
+
+-- PAGE: view profile node 
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_view_profile_node', 'view-course-profile-node', '_bx_courses_page_title_sys_view_profile_node', '_bx_courses_page_title_view_profile_node', 'bx_courses', 5, 2147483647, 1, 'page.php?i=view-course-profile-node', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+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_courses_view_profile_node', 1, 'bx_courses', '_bx_courses_page_block_title_system_entry_node', '_bx_courses_page_block_title_entry_node', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:17:"entity_node_block";}', 0, 0, 1, 0);
+
+-- PAGE: view closed profile 
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_view_profile_closed', 'view-course-profile-closed', '_bx_courses_page_title_sys_view_profile_closed', '_bx_courses_page_title_view_profile', 'bx_courses', 10, 2147483647, 1, 'page.php?i=view-course-profile', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+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_courses_view_profile_closed', 2, 'bx_courses', '', '_bx_courses_page_block_title_profile_info', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:11:"entity_info";}', 0, 0, 1, 0),
+('bx_courses_view_profile_closed', 3, 'bx_courses', '', '_bx_courses_page_block_title_fans', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:4:"fans";}', 0, 0, 1, 0);
+
+-- PAGE: edit profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_edit_profile', 'edit-course-profile', '_bx_courses_page_title_sys_edit_profile', '_bx_courses_page_title_edit_profile', 'bx_courses', 5, 2147483647, 1, 'page.php?i=edit-course-profile', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_courses_edit_profile', 1, 'bx_courses', '_bx_courses_page_block_title_edit_profile', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:11:"entity_edit";}', 0, 0, 0);
+
+-- PAGE: invite members
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_invite', 'invite-to-course', '_bx_courses_page_title_sys_invite_to_course', '_bx_courses_page_title_invite_to_course', 'bx_courses', 5, 2147483647, 1, 'page.php?i=invite-to-course', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_courses_invite', 1, 'bx_courses', '_bx_courses_page_block_title_invite_to_course', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:13:"entity_invite";}', 0, 0, 0);
+
+-- PAGE: delete profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_delete_profile', 'delete-course-profile', '_bx_courses_page_title_sys_delete_profile', '_bx_courses_page_title_delete_profile', 'bx_courses', 5, 2147483647, 1, 'page.php?i=delete-course-profile', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_courses_delete_profile', 1, 'bx_courses', '_bx_courses_page_block_title_delete_profile', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:13:"entity_delete";}', 0, 0, 0);
+
+-- PAGE: join profile
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_join_profile', 'join-course-profile', '_bx_courses_page_title_sys_join_profile', '_bx_courses_page_title_join_profile', 'bx_courses', 5, 2147483647, 1, 'page.php?i=join-course-profile', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_courses_join_profile', 1, 'bx_courses', '_bx_courses_page_block_title_join_profile', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:11:"entity_join";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 0);
+
+-- PAGE: profile info
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_profile_info', 'course-profile-info', '_bx_courses_page_title_sys_profile_info', '_bx_courses_page_title_profile_info', 'bx_courses', 5, 2147483647, 1, 'page.php?i=course-profile-info', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+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_courses_profile_info', 1, 'bx_courses', '_bx_courses_page_block_title_system_profile_info', '_bx_courses_page_block_title_profile_info_link', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:16:"entity_info_full";}', 0, 0, 1, 1),
+('bx_courses_profile_info', 1, 'bx_courses', '', '_bx_courses_page_block_title_profile_description', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:17:"entity_text_block";}', 0, 0, 1, 2);
+
+-- PAGE: manage profile pricing
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_profile_pricing', 'edit-course-pricing', '_bx_courses_page_title_sys_profile_pricing', '_bx_courses_page_title_profile_pricing', 'bx_courses', 5, 2147483647, 1, 'page.php?i=edit-course-pricing', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+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_courses_profile_pricing', 1, 'bx_courses', '_bx_courses_page_block_title_system_profile_pricing', '_bx_courses_page_block_title_profile_pricing_link', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:14:"entity_pricing";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 1);
+
+-- PAGE: manage profile content
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_profile_content', 'edit-course-content', '_bx_courses_page_title_sys_profile_content', '_bx_courses_page_title_profile_content', 'bx_courses', 5, 2147483647, 1, 'page.php?i=edit-course-content', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+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_courses_profile_content', 1, 'bx_courses', '_bx_courses_page_block_title_system_profile_structure', '_bx_courses_page_block_title_profile_structure_link', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:30:"entity_content_structure_block";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 1),
+('bx_courses_profile_content', 1, 'bx_courses', '_bx_courses_page_block_title_system_profile_data', '_bx_courses_page_block_title_profile_data_link', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:25:"entity_content_data_block";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 2);
+
+-- PAGE: course fans
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_fans', 'course-fans', '_bx_courses_page_title_sys_entry_fans', '_bx_courses_page_title_entry_fans', 'bx_courses', 5, 2147483647, 1, 'page.php?i=course-fans', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+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_courses_fans', 1, 'bx_courses', '_bx_courses_page_block_title_system_fans', '_bx_courses_page_block_title_fans_link', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:10:"fans_table";}', 0, 0, 1, 1),
+('bx_courses_fans', 1, 'bx_courses', '_bx_courses_page_block_title_system_invites', '_bx_courses_page_block_title_fans_invites', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:13:"invites_table";}', 0, 0, 1, 1);
+
+-- PAGE: view entry comments
+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 
+('bx_courses_profile_comments', '_bx_courses_page_title_sys_profile_comments', '_bx_courses_page_title_profile_comments', 'bx_courses', 5, 2147483647, 1, 'course-profile-comments', '', '', '', '', 0, 1, 0, 'BxCoursesPageEntry', 'modules/boonex/courses/classes/BxCoursesPageEntry.php');
+
+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_courses_profile_comments', 1, 'bx_courses', '_bx_courses_page_block_title_profile_comments', '_bx_courses_page_block_title_profile_comments_link', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:15:"entity_comments";}', 0, 0, 0, 1);
+
+-- PAGE: module home
+
+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 
+('bx_courses_home', '_bx_courses_page_title_sys_recent', '_bx_courses_page_title_recent', 'bx_courses', 5, 2147483647, 1, 'courses-home', 'page.php?i=courses-home', '', '', '', 0, 1, 0, 'BxCoursesPageBrowse', 'modules/boonex/courses/classes/BxCoursesPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('bx_courses_home', 1, 'bx_courses', '_bx_courses_page_block_title_featured_profiles', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:15:"browse_featured";s:6:"params";a:1:{i:0;s:7:"gallery";}}', 0, 1, 0),
+('bx_courses_home', 1, 'bx_courses', '_bx_courses_page_block_title_latest_profiles', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:22:"browse_recent_profiles";s:6:"params";a:1:{i:0;b:1;}}', 0, 1, 1);
+
+-- PAGE: top profiles
+
+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 
+('bx_courses_top', '_bx_courses_page_title_sys_top', '_bx_courses_page_title_top', 'bx_courses', 5, 2147483647, 1, 'courses-top', 'page.php?i=courses-top', '', '', '', 0, 1, 0, 'BxCoursesPageBrowse', 'modules/boonex/courses/classes/BxCoursesPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_courses_top', 1, 'bx_courses', '_bx_courses_page_block_title_top_profiles', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:19:"browse_top_profiles";s:6:"params";a:1:{i:0;b:1;}}', 0, 1, 0);
+
+-- PAGE: search for entries
+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 
+('bx_courses_search', '_bx_courses_page_title_sys_entries_search', '_bx_courses_page_title_entries_search', 'bx_courses', 5, 2147483647, 1, 'courses-search', 'page.php?i=courses-search', '', '', '', 0, 1, 0, 'BxCoursesPageBrowse', 'modules/boonex/courses/classes/BxCoursesPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_courses_search', 1, 'bx_courses', '_bx_courses_page_block_title_search_form', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:8:"get_form";s:6:"params";a:1:{i:0;a:1:{s:6:"object";s:10:"bx_courses";}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 1, 1),
+('bx_courses_search', 1, 'bx_courses', '_bx_courses_page_block_title_search_results', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_results";s:6:"params";a:1:{i:0;a:2:{s:6:"object";s:10:"bx_courses";s:10:"show_empty";b:1;}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 1, 2),
+('bx_courses_search', 1, 'bx_courses', '_bx_courses_page_block_title_search_form_cmts', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:8:"get_form";s:6:"params";a:1:{i:0;a:1:{s:6:"object";s:15:"bx_courses_cmts";}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 0, 3),
+('bx_courses_search', 1, 'bx_courses', '_bx_courses_page_block_title_search_results_cmts', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_results";s:6:"params";a:1:{i:0;a:2:{s:6:"object";s:15:"bx_courses_cmts";s:10:"show_empty";b:1;}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 0, 4);
+
+-- PAGE: joined profiles
+
+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 
+('bx_joined_courses', '_bx_courses_page_title_sys_joined', '_bx_courses_page_title_joined', 'bx_courses', 5, 2147483647, 1, 'courses-joined', 'page.php?i=courses-joined', '', '', '', 0, 1, 0, 'BxCoursesPageBrowse', 'modules/boonex/courses/classes/BxCoursesPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_joined_courses', 1, 'bx_courses', '_bx_courses_page_block_title_joined_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:21:"browse_joined_entries";s:6:"params";a:2:{i:0;s:12:"{profile_id}";i:1;b:1;}}', 0, 1, 0);
+
+-- PAGE: module manage own
+
+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 
+('bx_courses_manage', '_bx_courses_page_title_sys_manage', '_bx_courses_page_title_manage', 'bx_courses', 5, 2147483647, 1, 'courses-manage', 'page.php?i=courses-manage', '', '', '', 0, 1, 0, 'BxCoursesPageBrowse', 'modules/boonex/courses/classes/BxCoursesPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_courses_manage', 1, 'bx_courses', '_bx_courses_page_block_title_system_manage', '_bx_courses_page_block_title_manage', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:12:"manage_tools";}}', 0, 1, 0);
+
+-- PAGE: module manage all
+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 
+('bx_courses_administration', '_bx_courses_page_title_sys_manage_administration', '_bx_courses_page_title_manage', 'bx_courses', 5, 192, 1, 'courses-administration', 'page.php?i=courses-administration', '', '', '', 0, 1, 0, 'BxCoursesPageBrowse', 'modules/boonex/courses/classes/BxCoursesPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_courses_administration', 1, 'bx_courses', '_bx_courses_page_block_title_system_manage_administration', '_bx_courses_page_block_title_manage', 11, 192, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:12:"manage_tools";s:6:"params";a:1:{i:0;s:14:"administration";}}', 0, 1, 0);
+
+-- PAGE: user's courses
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_joined', 'joined-courses', '_bx_courses_page_title_sys_joined_entries', '_bx_courses_page_title_joined_entries', 'bx_courses', 5, 2147483647, 1, '', '', '', '', 0, 1, 0, 'BxCoursesPageJoinedEntries', 'modules/boonex/courses/classes/BxCoursesPageJoinedEntries.php');
+
+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_courses_joined', 1, 'bx_courses', '_bx_courses_page_block_title_sys_entries_actions', '_bx_courses_page_block_title_entries_actions', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:18:"my_entries_actions";}', 0, 0, 1, 1),
+('bx_courses_joined', 1, 'bx_courses', '_bx_courses_page_block_title_sys_entries_of_author', '_bx_courses_page_block_title_entries_of_author', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:22:"browse_created_entries";}', 0, 0, 0, 2),
+('bx_courses_joined', 1, 'bx_courses', '_bx_courses_page_block_title_sys_favorites_of_author', '_bx_courses_page_block_title_favorites_of_author', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:15:"browse_favorite";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 1, 1, 3),
+('bx_courses_joined', 1, 'bx_courses', '_bx_courses_page_block_title_sys_joined_entries', '_bx_courses_page_block_title_joined_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:21:"browse_joined_entries";s:6:"params";a:2:{i:0;i:0;i:1;b:1;}}', 0, 0, 1, 4);
+
+-- PAGE: favorites by list
+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 
+('bx_courses_favorites', '_bx_courses_page_title_sys_entries_favorites', '_bx_courses_page_title_entries_favorites', 'bx_courses', 12, 2147483647, 1, 'courses-favorites', 'page.php?i=courses-favorites', '', '', '', 0, 1, 0, 'BxCoursesPageListEntry', 'modules/boonex/courses/classes/BxCoursesPageListEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_courses_favorites', 2, 'bx_courses', '_bx_courses_page_block_title_sys_favorites_entries', '_bx_courses_page_block_title_favorites_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:15:"browse_favorite";s:6:"params";a:2:{s:10:"profile_id";s:12:"{profile_id}";s:6:"params";a:3:{s:9:"unit_view";s:7:"gallery";s:13:"empty_message";b:0;s:13:"ajax_paginate";b:0;}}}', 0, 1, 1),
+('bx_courses_favorites', 3, 'bx_courses', '', '_bx_courses_page_block_title_favorites_entries_info', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:19:"favorites_list_info";}', 0, 0, 0),
+('bx_courses_favorites', 3, 'bx_courses', '', '_bx_courses_page_block_title_favorites_entries_actions', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:22:"favorites_list_actions";}', 0, 0, 1);
+
+-- PAGE: add block to homepage
+SET @iBlockOrder = (SELECT `order` FROM `sys_pages_blocks` WHERE `object` = 'sys_home' AND `cell_id` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES
+('sys_home', 1, 'bx_courses', '', '_bx_courses_page_block_title_latest_profiles', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:22:"browse_recent_profiles";}', 1, 0, 0, IFNULL(@iBlockOrder, 0) + 1),
+('sys_home', 1, 'bx_courses', '_bx_courses_page_block_title_sys_recommended_entries_view_showcase', '_bx_courses_page_block_title_recommended_entries_view_showcase', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:18:"browse_recommended";}', 1, 0, 0, IFNULL(@iBlockOrder, 0) + 2);
+
+-- PAGE: service blocks
+SET @iBlockOrder = (SELECT `order` FROM `sys_pages_blocks` WHERE `object` = '' AND `cell_id` = 0 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES
+('', 0, 'bx_courses', '', '_bx_courses_page_block_title_categories', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:15:"categories_list";s:6:"params";a:2:{i:0;s:15:"bx_courses_cats";i:1;a:2:{s:10:"show_empty";b:1;s:21:"show_empty_categories";b:1;}}s:5:"class";s:20:"TemplServiceCategory";}', 1, 1, 1, IFNULL(@iBlockOrder, 0) + 1),
+('', 0, 'bx_courses', '_bx_courses_page_block_title_sys_featured_entries_view_showcase', '_bx_courses_page_block_title_featured_entries_view_showcase', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:15:"browse_featured";s:6:"params";a:3:{s:9:"unit_view";s:8:"showcase";s:13:"empty_message";b:0;s:13:"ajax_paginate";b:0;}}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 2),
+('', 0, 'bx_courses', '_bx_courses_page_block_title_sys_recommended_entries_view_showcase', '_bx_courses_page_block_title_recommended_entries_view_showcase', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:18:"browse_recommended";s:6:"params";a:3:{s:9:"unit_view";s:8:"showcase";s:13:"empty_message";b:0;s:13:"ajax_paginate";b:0;}}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 3),
+('', 0, 'bx_courses', '_bx_courses_page_block_title_sys_labels_tree', '_bx_courses_page_block_title_labels_tree', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:15:"get_lables_tree";}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 4),
+('', 0, 'bx_courses', '_bx_courses_page_block_title_sys_browse_by_label', '_bx_courses_page_block_title_browse_by_label', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:15:"browse_by_label";}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 5),
+('', 0, 'bx_courses', '_bx_courses_page_block_title_sys_labels_breadcrumbs', '_bx_courses_page_block_title_labels_breadcrumbs', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:22:"get_lables_breadcrumbs";}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 6),
+('', 0, 'bx_courses', '_bx_courses_page_block_title_sys_cover_block', '_bx_courses_page_block_title_cover_block', 3, 2147483647, 'service', 'a:2:{s:6:\"module\";s:10:\"bx_courses\";s:6:\"method\";s:12:\"entity_cover\";}', 0, 1, 1, IFNULL(@iBlockOrder, 0) + 7);
+
+-- MENU
+
+-- MENU: add to site menu
+
+SET @iSiteMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_site' AND `active` = 1 AND `order` < 9999 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_site', 'bx_courses', 'courses-home', '_bx_courses_menu_item_title_system_entries_home', '_bx_courses_menu_item_title_entries_home', 'page.php?i=courses-home', '', '', 'book-reader col-blue3-dark', 'bx_courses_submenu', 2147483647, 1, 1, IFNULL(@iSiteMenuOrder, 0) + 1);
+
+-- MENU: add to homepage menu
+
+SET @iHomepageMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_homepage' AND `active` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_homepage', 'bx_courses', 'courses-home', '_bx_courses_menu_item_title_system_entries_home', '_bx_courses_menu_item_title_entries_home', 'page.php?i=courses-home', '', '', 'book-reader col-blue3-dark', 'bx_courses_submenu', 2147483647, 1, 1, IFNULL(@iHomepageMenuOrder, 0) + 1);
+
+-- MENU: add to "add content" menu
+
+SET @iAddMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_add_content_links' AND `active` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_add_content_links', 'bx_courses', 'create-course-profile', '_bx_courses_menu_item_title_system_create_profile', '_bx_courses_menu_item_title_create_profile', 'page.php?i=create-course-profile', '', '', 'book-reader col-blue3-dark', '', 2147483647, 1, 1, IFNULL(@iAddMenuOrder, 0) + 1);
+
+-- MENU: view actions
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_view_actions', '_bx_courses_menu_title_view_profile_actions', 'bx_courses_view_actions', 'bx_courses', 9, 0, 1, 'BxCoursesMenuViewActions', 'modules/boonex/courses/classes/BxCoursesMenuViewActions.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_view_actions', 'bx_courses', '_bx_courses_menu_set_title_view_profile_actions', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `editable`, `order`) VALUES 
+('bx_courses_view_actions', 'bx_courses', 'join-course-profile', '_bx_courses_menu_item_title_system_pay_and_join', '_bx_courses_menu_item_title_pay_and_join', 'page.php?i=join-course-profile&profile_id={profile_id}', '', '', 'sign-in-alt', '', 0, 2147483647, 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:22:"is_paid_join_avaliable";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 1, 0, 0, 5),
+('bx_courses_view_actions', 'bx_courses', 'profile-fan-add', '_bx_courses_menu_item_title_system_become_fan', '{title_add_fan}', 'javascript:void(0)', 'bx_conn_action(this, \'bx_courses_fans\', \'add\', \'{profile_id}\')', '', 'sign-in-alt', '', 0, 2147483647, 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:22:"is_free_join_avaliable";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 1, 0, 0, 5),
+('bx_courses_view_actions', 'bx_courses', 'profile-fan-remove', '_bx_courses_menu_item_title_system_leave_entry', '{title_remove_fan}', 'javascript:void(0)', 'bx_conn_action(this, \'bx_courses_fans\', \'remove\', \'{profile_id}\')', '', 'sign-out-alt', '', 0, 2147483647, '', 1, 0, 1, 6),
+('bx_courses_view_actions', 'bx_courses', 'profile-subscribe-add', '_bx_courses_menu_item_title_system_subscribe', '_bx_courses_menu_item_title_subscribe', 'javascript:void(0)', 'bx_conn_action(this, \'sys_profiles_subscriptions\', \'add\', \'{profile_id}\')', '', 'check', '', 0, 2147483647, '', 1, 0, 1, 20),
+('bx_courses_view_actions', 'bx_courses', 'profile-subscribe-remove', '_bx_courses_menu_item_title_system_unsubscribe', '_bx_courses_menu_item_title_unsubscribe', 'javascript:void(0)', 'bx_conn_action(this, \'sys_profiles_subscriptions\', \'remove\', \'{profile_id}\')', '', 'check', '', 0, 2147483647, '', 1, 0, 1, 21),
+('bx_courses_view_actions', 'bx_courses', 'profile-set-badges', '_sys_menu_item_title_system_set_badges', '_sys_menu_item_title_set_badges', 'javascript:void(0)', 'bx_menu_popup(''sys_set_badges'', window, {}, {module: ''bx_courses'', content_id: {content_id}});', '', 'check-circle', '', 0, 192, 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:19:"is_badges_avaliable";}', 1, 0, 1, 30),
+('bx_courses_view_actions', 'bx_courses', 'profile-actions-more', '_bx_courses_menu_item_title_system_more_actions', '_bx_courses_menu_item_title_more_actions', 'javascript:void(0)', 'bx_menu_popup(''bx_courses_view_actions_more'', this, {}, {profile_id:{profile_id}});', '', 'cog', 'bx_courses_view_actions_more', 1, 2147483647, '', 1, 0, 1, 9999);
+
+-- MENU: view actions more
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_view_actions_more', '_bx_courses_menu_title_view_profile_actions_more', 'bx_courses_view_actions_more', 'bx_courses', 6, 0, 1, 'BxCoursesMenuViewActions', 'modules/boonex/courses/classes/BxCoursesMenuViewActions.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_view_actions_more', 'bx_courses', '_bx_courses_menu_set_title_view_profile_actions_more', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `order`) VALUES 
+('bx_courses_view_actions_more', 'bx_courses', 'notes', '_sys_menu_item_title_system_va_notes', '_sys_menu_item_title_va_notes', 'javascript:void(0)', 'javascript:bx_get_notes(this,  ''{module_uri}'', {content_id});', '', 'exclamation-triangle', '', 192, '', 1, 0, 20),
+('bx_courses_view_actions_more', 'bx_courses', 'audit', '_sys_menu_item_title_system_va_audit', '_sys_menu_item_title_va_audit', 'page.php?i=dashboard-audit&module=bx_courses&content_id={content_id}', '', '', 'history', '', 192, '', 1, 0, 30),
+('bx_courses_view_actions_more', 'bx_courses', 'edit-course-profile', '_bx_courses_menu_item_title_system_edit_profile', '_bx_courses_menu_item_title_edit_profile', 'page.php?i=edit-course-profile&id={content_id}', '', '', 'pencil-alt', '', 2147483647, '', 1, 0, 40),
+('bx_courses_view_actions_more', 'bx_courses', 'edit-course-pricing', '_bx_courses_menu_item_title_system_edit_pricing', '_bx_courses_menu_item_title_edit_pricing', 'page.php?i=edit-course-pricing&profile_id={profile_id}', '', '', 'money-check-alt', '', 2147483647, 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:20:"is_pricing_avaliable";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 1, 0, 41),
+('bx_courses_view_actions_more', 'bx_courses', 'edit-course-content', '_bx_courses_menu_item_title_system_edit_content', '_bx_courses_menu_item_title_edit_content', 'page.php?i=edit-course-content&profile_id={profile_id}', '', '', 'folder-tree', '', 2147483647, 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:20:"is_content_avaliable";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 1, 0, 42),
+('bx_courses_view_actions_more', 'bx_courses', 'invite-to-course', '_bx_courses_menu_item_title_system_invite', '_bx_courses_menu_item_title_invite', 'page.php?i=invite-to-course&id={content_id}', '', '', 'user-friends', '', 2147483647, '', 1, 0, 45),
+('bx_courses_view_actions_more', 'bx_courses', 'delete-course-profile', '_bx_courses_menu_item_title_system_delete_profile', '_bx_courses_menu_item_title_delete_profile', 'page.php?i=delete-course-profile&id={content_id}', '', '', 'remove', '', 2147483647, '', 1, 0, 50),
+('bx_courses_view_actions_more', 'bx_courses', 'approve-course-profile', '_sys_menu_item_title_system_va_approve', '_sys_menu_item_title_va_approve', 'javascript:void(0)', 'javascript:bx_approve(this, ''{module_uri}'', {content_id});', '', 'check', '', 2147483647, '', 1, 0, 60);
+
+-- MENU: all actions menu for view entry 
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `persistent`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_view_actions_all', '_sys_menu_title_view_actions', 'bx_courses_view_actions_all', 'bx_courses', 15, 1, 0, 1, 'BxCoursesMenuViewActionsAll', 'modules/boonex/courses/classes/BxCoursesMenuViewActionsAll.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_view_actions_all', 'bx_courses', '_sys_menu_set_title_view_actions', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `order`) VALUES 
+('bx_courses_view_actions_all', 'bx_courses', 'join-course-profile', '_bx_courses_menu_item_title_system_pay_and_join', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 5),
+('bx_courses_view_actions_all', 'bx_courses', 'profile-fan-add', '_bx_courses_menu_item_title_system_become_fan', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 10),
+('bx_courses_view_actions_all', 'bx_courses', 'profile-fan-remove', '_bx_courses_menu_item_title_system_leave_entry', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 20),
+('bx_courses_view_actions_all', 'bx_courses', 'profile-subscribe-add', '_bx_courses_menu_item_title_system_subscribe', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 30),
+('bx_courses_view_actions_all', 'bx_courses', 'profile-subscribe-remove', '_bx_courses_menu_item_title_system_unsubscribe', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 40),
+('bx_courses_view_actions_all', 'bx_courses', 'profile-set-badges', '_sys_menu_item_title_system_set_badges', '', '', '', '', '', '', '', 0, 192, 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:19:"is_badges_avaliable";}', 1, 0, 50),
+('bx_courses_view_actions_all', 'bx_courses', 'comment', '_sys_menu_item_title_system_va_comment', '', '', '', '', '', '', '', 0, 2147483647, '', 0, 0, 200),
+('bx_courses_view_actions_all', 'bx_courses', 'view', '_sys_menu_item_title_system_va_view', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 210),
+('bx_courses_view_actions_all', 'bx_courses', 'vote', '_sys_menu_item_title_system_va_vote', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 220),
+('bx_courses_view_actions_all', 'bx_courses', 'score', '_sys_menu_item_title_system_va_score', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 230),
+('bx_courses_view_actions_all', 'bx_courses', 'favorite', '_sys_menu_item_title_system_va_favorite', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 240),
+('bx_courses_view_actions_all', 'bx_courses', 'feature', '_sys_menu_item_title_system_va_feature', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 250),
+('bx_courses_view_actions_all', 'bx_courses', 'repost', '_sys_menu_item_title_system_va_repost', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 260),
+('bx_courses_view_actions_all', 'bx_courses', 'report', '_sys_menu_item_title_system_va_report', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 270),
+('bx_courses_view_actions_all', 'bx_courses', 'social-sharing', '_sys_menu_item_title_system_social_sharing', '_sys_menu_item_title_social_sharing', 'javascript:void(0)', 'oBxDolPage.share(this, \'{url_encoded}\')', '', 'share', '', '', 0, 2147483647, '', 1, 0, 300),
+('bx_courses_view_actions_all', 'bx_courses', 'more-auto', '_sys_menu_item_title_system_va_more_auto', '_sys_menu_item_title_va_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', '', 0, 2147483647, '', 1, 0, 9999);
+
+-- MENU: meta (counters) menu for view entry
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_view_meta', '_bx_courses_menu_title_view_profile_meta', 'bx_courses_view_meta', 'bx_courses', 15, 0, 1, 'BxCoursesMenuViewMeta', 'modules/boonex/courses/classes/BxCoursesMenuViewMeta.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_view_meta', 'bx_courses', '_bx_courses_menu_set_title_view_profile_meta', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `order`) VALUES 
+('bx_courses_view_meta', 'bx_courses', 'members', '_sys_menu_item_title_system_sm_members', '_sys_menu_item_title_sm_members', '', '', '', '', '', 0, 2147483647, '', 1, 0, 10),
+('bx_courses_view_meta', 'bx_courses', 'subscribers', '_sys_menu_item_title_system_sm_subscribers', '_sys_menu_item_title_sm_subscribers', '', '', '', '', '', 0, 2147483647, '', 1, 0, 20),
+('bx_courses_view_meta', 'bx_courses', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 0, 2147483647, '', 1, 0, 30),
+('bx_courses_view_meta', 'bx_courses', 'votes', '_sys_menu_item_title_system_sm_votes', '_sys_menu_item_title_sm_votes', '', '', '', '', '', 0, 2147483647, '', 1, 0, 40),
+('bx_courses_view_meta', 'bx_courses', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 0, 2147483647, '', 1, 0, 50);
+
+-- MENU: add content menu for entry
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `persistent`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_content_add', '_bx_courses_menu_title_content_add', 'bx_courses_content_add', 'bx_courses', 6, 1, 0, 1, 'BxCoursesMenuContentAdd', 'modules/boonex/courses/classes/BxCoursesMenuContentAdd.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_content_add', 'bx_courses', '_bx_courses_menu_set_title_content_add', 0);
+
+-- MENU: actions menu for my entries
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_my', '_bx_courses_menu_title_entries_my', 'bx_courses_my', 'bx_courses', 9, 0, 1, 'BxCoursesMenu', 'modules/boonex/courses/classes/BxCoursesMenu.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_my', 'bx_courses', '_bx_courses_menu_set_title_entries_my', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_courses_my', 'bx_courses', 'create-course-profile', '_bx_courses_menu_item_title_system_create_profile', '_bx_courses_menu_item_title_create_profile', 'page.php?i=create-course-profile', '', '', 'plus', '', 2147483647, 1, 0, 0);
+
+-- MENU: module sub-menu
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_submenu', '_bx_courses_menu_title_submenu', 'bx_courses_submenu', 'bx_courses', 8, 0, 1, '', '');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_submenu', 'bx_courses', '_bx_courses_menu_set_title_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_courses_submenu', 'bx_courses', 'courses-home', '_bx_courses_menu_item_title_system_entries_recent', '_bx_courses_menu_item_title_entries_recent', 'page.php?i=courses-home', '', '', '', '', 2147483647, 1, 1, 1),
+('bx_courses_submenu', 'bx_courses', 'courses-top', '_bx_courses_menu_item_title_system_entries_top', '_bx_courses_menu_item_title_entries_top', 'page.php?i=courses-top', '', '', '', '', 2147483647, 1, 1, 2),
+('bx_courses_submenu', 'bx_courses', 'courses-search', '_bx_courses_menu_item_title_system_entries_search', '_bx_courses_menu_item_title_entries_search', 'page.php?i=courses-search', '', '', '', '', 2147483647, 1, 1, 3),
+('bx_courses_submenu', 'bx_courses', 'courses-joined', '_bx_courses_menu_item_title_system_entries_joined', '_bx_courses_menu_item_title_entries_joined', 'page.php?i=courses-joined', '', '', '', '', 2147483647, 1, 1, 4),
+('bx_courses_submenu', 'bx_courses', 'courses-manage', '_bx_courses_menu_item_title_system_entries_manage', '_bx_courses_menu_item_title_entries_manage', 'page.php?i=courses-manage', '', '', '', '', 2147483646, 1, 1, 5);
+
+-- MENU: view submenu
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_view_submenu', '_bx_courses_menu_title_view_profile_submenu', 'bx_courses_view_submenu', 'bx_courses', 18, 0, 1, 'BxCoursesMenuView', 'modules/boonex/courses/classes/BxCoursesMenuView.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_view_submenu', 'bx_courses', '_bx_courses_menu_set_title_view_profile_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_courses_view_submenu', 'bx_courses', 'view-course-profile', '_bx_courses_menu_item_title_system_view_profile_view', '_bx_courses_menu_item_title_view_profile_view', 'page.php?i=view-course-profile&id={content_id}', '', '', 'book-reader col-blue3-dark', '', '', 0, 2147483647, 1, 0, 1),
+('bx_courses_view_submenu', 'bx_courses', 'course-profile-info', '_bx_courses_menu_item_title_system_view_profile_info', '_bx_courses_menu_item_title_view_profile_info', 'page.php?i=course-profile-info&id={content_id}', '', '', 'info-circle col-gray', '', '', 0, 2147483647, 1, 0, 2),
+('bx_courses_view_submenu', 'bx_courses', 'course-profile-comments', '_bx_courses_menu_item_title_system_view_profile_comments', '_bx_courses_menu_item_title_view_profile_comments', 'page.php?i=course-profile-comments&id={content_id}', '', '', '', '', '', 0, 2147483647, 0, 0, 3),
+('bx_courses_view_submenu', 'bx_courses', 'course-fans', '_bx_courses_menu_item_title_system_view_fans', '_bx_courses_menu_item_title_view_fans', 'page.php?i=course-fans&profile_id={profile_id}', '', '', 'users col-blue3', '', '', 0, 2147483647, 1, 0, 4),
+('bx_courses_view_submenu', 'bx_courses', 'more-auto', '_bx_courses_menu_item_title_system_view_profile_more_auto', '_bx_courses_menu_item_title_view_profile_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', '', 0, 2147483647, 1, 0, 9999);
+
+-- MENU: custom menu for snippet meta info
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_snippet_meta', '_sys_menu_title_snippet_meta', 'bx_courses_snippet_meta', 'bx_courses', 15, 0, 1, 'BxCoursesMenuSnippetMeta', 'modules/boonex/courses/classes/BxCoursesMenuSnippetMeta.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_snippet_meta', 'bx_courses', '_sys_menu_set_title_snippet_meta', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `editable`, `order`) VALUES
+('bx_courses_snippet_meta', 'bx_courses', 'privacy', '_bx_courses_menu_item_title_system_sm_privacy', '_bx_courses_menu_item_title_sm_privacy', '', '', '', '', '', 2147483647, '', 1, 0, 1, 0),
+('bx_courses_snippet_meta', 'bx_courses', 'date', '_sys_menu_item_title_system_sm_date', '_sys_menu_item_title_sm_date', '', '', '', '', '', 2147483647, '', 0, 0, 1, 1),
+('bx_courses_snippet_meta', 'bx_courses', 'category', '_sys_menu_item_title_system_sm_category', '_sys_menu_item_title_sm_category', '', '', '', '', '', 2147483647, '', 0, 0, 1, 5),
+('bx_courses_snippet_meta', 'bx_courses', 'tags', '_sys_menu_item_title_system_sm_tags', '_sys_menu_item_title_sm_tags', '', '', '', '', '', 2147483647, '', 0, 0, 1, 10),
+('bx_courses_snippet_meta', 'bx_courses', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 2147483647, '', 0, 0, 1, 15),
+('bx_courses_snippet_meta', 'bx_courses', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 2147483647, '', 0, 0, 1, 20),
+('bx_courses_snippet_meta', 'bx_courses', 'members', '_bx_courses_menu_item_title_system_sm_members', '_bx_courses_menu_item_title_sm_members', '', '', '', '', '', 2147483647, '', 1, 0, 1, 25),
+('bx_courses_snippet_meta', 'bx_courses', 'subscribers', '_sys_menu_item_title_system_sm_subscribers', '_sys_menu_item_title_sm_subscribers', '', '', '', '', '', 2147483647, '', 0, 0, 1, 30),
+('bx_courses_snippet_meta', 'bx_courses', 'reports', '_bx_courses_menu_item_title_system_sm_reports', '', '', '', '', '', '', 2147483647, '', 0, 0, 1, 33),
+('bx_courses_snippet_meta', 'bx_courses', 'nl', '_sys_menu_item_title_system_sm_nl', '_sys_menu_item_title_sm_nl', '', '', '', '', '', 2147483647, '', 1, 0, 1, 35),
+('bx_courses_snippet_meta', 'bx_courses', 'join-paid', '_sys_menu_item_title_system_sm_join_paid', '_sys_menu_item_title_sm_join_paid', '', '', '', '', '', 2147483647, 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:33:"is_paid_join_avaliable_by_content";s:6:"params";a:1:{i:0;s:12:"{content_id}";}}', 1, 0, 1, 40),
+('bx_courses_snippet_meta', 'bx_courses', 'join', '_bx_courses_menu_item_title_system_sm_join', '_bx_courses_menu_item_title_sm_join', '', '', '', '', '', 2147483647, 'a:3:{s:6:"module";s:10:"bx_courses";s:6:"method";s:33:"is_free_join_avaliable_by_content";s:6:"params";a:1:{i:0;s:12:"{content_id}";}}', 1, 0, 1, 45),
+('bx_courses_snippet_meta', 'bx_courses', 'leave', '_sys_menu_item_title_system_sm_leave', '_sys_menu_item_title_sm_leave', '', '', '', '', '', 2147483647, '', 0, 0, 1, 50),
+('bx_courses_snippet_meta', 'bx_courses', 'subscribe', '_sys_menu_item_title_system_sm_subscribe', '_sys_menu_item_title_sm_subscribe', '', '', '', '', '', 2147483647, '', 0, 0, 1, 55),
+('bx_courses_snippet_meta', 'bx_courses', 'unsubscribe', '_sys_menu_item_title_system_sm_unsubscribe', '_sys_menu_item_title_sm_unsubscribe', '', '', '', '', '', 2147483647, '', 0, 0, 1, 60),
+('bx_courses_snippet_meta', 'bx_courses', 'pass', '_bx_courses_menu_item_title_system_sm_pass', '', '', '', '', '', '', 2147483647, '', 0, 0, 1, 65);
+
+-- MENU: profile stats
+SET @iNotifMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name` = 'sys_profile_stats' AND `active` = 1 LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
+('sys_profile_stats', 'bx_courses', 'profile-stats-my-courses', '_bx_courses_menu_item_title_system_manage_my_entries', '_bx_courses_menu_item_title_manage_my_entries', 'page.php?i=joined-courses&profile_id={member_id}', '', '_self', 'book-reader col-blue3-dark', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:41:"get_menu_addon_manage_tools_profile_stats";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 2);
+
+-- MENU: profile followings
+SET @iFollowingsMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_profile_followings' LIMIT 1);
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `collapsed`, `active`, `copyable`, `order`) VALUES 
+('sys_profile_followings', 'bx_courses', 'courses', '_bx_courses_menu_item_title_system_followings', '_bx_courses_menu_item_title_followings', 'javascript:void(0)', '', '_self', 'book-reader col-blue3-dark', '', '', 2147483647, 0, 1, 0, @iFollowingsMenuOrder + 1);
+
+-- MENU: manage tools submenu
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_courses_menu_manage_tools', '_bx_courses_menu_title_manage_tools', 'bx_courses_menu_manage_tools', 'bx_courses', 6, 0, 1, 'BxCoursesMenuManageTools', 'modules/boonex/courses/classes/BxCoursesMenuManageTools.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_courses_menu_manage_tools', 'bx_courses', '_bx_courses_menu_set_title_manage_tools', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_courses_menu_manage_tools', 'bx_courses', 'clear-reports', '_bx_courses_menu_item_title_system_clear_reports', '_bx_courses_menu_item_title_clear_reports', 'javascript:void(0)', 'javascript:{js_object}.onClickClearReports({content_id});', '_self', 'eraser', '', 2147483647, 1, 0, 1),
+('bx_courses_menu_manage_tools', 'bx_courses', 'delete', '_bx_courses_menu_item_title_system_delete', '_bx_courses_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2),
+('bx_courses_menu_manage_tools', 'bx_courses', 'delete-with-content', '_bx_courses_menu_item_title_system_delete_with_content', '_bx_courses_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 0, 0, 3);
+
+-- MENU: dashboard manage tools
+SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_account_dashboard_manage_tools', 'bx_courses', 'courses-administration', '_bx_courses_menu_item_title_system_admt_entries', '_bx_courses_menu_item_title_admt_entries', 'page.php?i=courses-administration', '', '_self', 'users', 'a:2:{s:6:"module";s:10:"bx_courses";s:6:"method";s:27:"get_menu_addon_manage_tools";}', '', 192, 1, 0, @iManageMenuOrder + 1);
+
+-- MENU: add menu item to profiles modules (trigger* menu sets are processed separately upon modules enable/disable)
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('trigger_profile_view_submenu', 'bx_courses', 'joined-courses', '_bx_courses_menu_item_title_system_view_joined_entries', '_bx_courses_menu_item_title_view_joined_entries', 'page.php?i=joined-courses&profile_id={profile_id}', '', '', 'book-reader col-blue3-dark', '', 2147483647, 1, 0, 0);
+
+-- ACL
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_courses', 'create entry', NULL, '_bx_courses_acl_action_create_profile', '', 1, 1);
+SET @iIdActionProfileCreate = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_courses', 'use paid join', NULL, '_bx_courses_acl_action_use_paid_join', '', 1, 1);
+SET @iIdActionUsePaidJoin = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_courses', 'delete entry', NULL, '_bx_courses_acl_action_delete_profile', '', 1, 1);
+SET @iIdActionProfileDelete = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_courses', 'view entry', NULL, '_bx_courses_acl_action_view_profile', '', 1, 0);
+SET @iIdActionProfileView = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_courses', 'edit any entry', NULL, '_bx_courses_acl_action_edit_any_profile', '', 1, 3);
+SET @iIdActionProfileEditAny = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_courses', 'delete any entry', NULL, '_bx_courses_acl_action_delete_any_profile', '', 1, 3);
+SET @iIdActionProfileDeleteAny = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_courses', 'delete invites', NULL, '_bx_courses_acl_action_delete_invites', '', 1, 3);
+SET @iIdActionProfileDeleteInvites = LAST_INSERT_ID();
+
+SET @iUnauthenticated = 1;
+SET @iAccount = 2;
+SET @iStandard = 3;
+SET @iUnconfirmed = 4;
+SET @iPending = 5;
+SET @iSuspended = 6;
+SET @iModerator = 7;
+SET @iAdministrator = 8;
+SET @iPremium = 9;
+
+INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
+
+-- profile create
+(@iAccount, @iIdActionProfileCreate),
+(@iStandard, @iIdActionProfileCreate),
+(@iUnconfirmed, @iIdActionProfileCreate),
+(@iPending, @iIdActionProfileCreate),
+(@iModerator, @iIdActionProfileCreate),
+(@iAdministrator, @iIdActionProfileCreate),
+(@iPremium, @iIdActionProfileCreate),
+
+-- use paid join
+(@iStandard, @iIdActionUsePaidJoin),
+(@iModerator, @iIdActionUsePaidJoin),
+(@iAdministrator, @iIdActionUsePaidJoin),
+(@iPremium, @iIdActionUsePaidJoin),
+
+-- profile delete
+(@iAccount, @iIdActionProfileDelete),
+(@iStandard, @iIdActionProfileDelete),
+(@iUnconfirmed, @iIdActionProfileDelete),
+(@iPending, @iIdActionProfileDelete),
+(@iModerator, @iIdActionProfileDelete),
+(@iAdministrator, @iIdActionProfileDelete),
+(@iPremium, @iIdActionProfileDelete),
+
+-- profile view
+(@iUnauthenticated, @iIdActionProfileView),
+(@iAccount, @iIdActionProfileView),
+(@iStandard, @iIdActionProfileView),
+(@iUnconfirmed, @iIdActionProfileView),
+(@iPending, @iIdActionProfileView),
+(@iModerator, @iIdActionProfileView),
+(@iAdministrator, @iIdActionProfileView),
+(@iPremium, @iIdActionProfileView),
+
+-- any profile edit
+(@iModerator, @iIdActionProfileEditAny),
+(@iAdministrator, @iIdActionProfileEditAny),
+
+-- any profile delete
+(@iAdministrator, @iIdActionProfileDeleteAny),
+
+-- any invites edit
+(@iModerator, @iIdActionProfileDeleteInvites),
+(@iAdministrator, @iIdActionProfileDeleteInvites);
+
+
+-- METATAGS
+INSERT INTO `sys_objects_metatags` (`object`, `module`, `table_keywords`, `table_locations`, `table_mentions`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses', 'bx_courses', 'bx_courses_meta_keywords', 'bx_courses_meta_locations', 'bx_courses_meta_mentions', '', '');
+
+-- CATEGORY
+INSERT INTO `sys_objects_category` (`object`, `module`, `search_object`, `form_object`, `list_name`, `table`, `field`, `join`, `where`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_cats', 'bx_courses',  'bx_courses', 'bx_course', 'bx_courses_cats', 'bx_courses_data', 'cat', 'INNER JOIN `sys_profiles` ON (`sys_profiles`.`content_id` = `bx_courses_data`.`id` AND `sys_profiles`.`type` = ''bx_courses'')', 'AND `sys_profiles`.`status` = ''active''', '', '');
+
+-- SEARCH
+SET @iSearchOrder = (SELECT IFNULL(MAX(`Order`), 0) FROM `sys_objects_search`);
+INSERT INTO `sys_objects_search` (`ObjectName`, `Title`, `Order`, `ClassName`, `ClassPath`) VALUES
+('bx_courses', '_bx_courses', @iSearchOrder + 1, 'BxCoursesSearchResult', 'modules/boonex/courses/classes/BxCoursesSearchResult.php');
+
+-- CONNECTIONS
+INSERT INTO `sys_objects_connection` (`object`, `table`, `profile_initiator`, `profile_content`, `type`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_fans', 'bx_courses_fans', 1, 1, 'mutual', 'BxCoursesConnectionFans', 'modules/boonex/courses/classes/BxCoursesConnectionFans.php');
+
+-- STATS
+SET @iMaxOrderStats = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_statistics`);
+INSERT INTO `sys_statistics` (`module`, `name`, `title`, `link`, `icon`, `query`, `order`) VALUES 
+('bx_courses', 'bx_courses', '_bx_courses', 'page.php?i=courses-home', 'book-reader col-blue3-dark', 'SELECT COUNT(*) FROM `bx_courses_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id` = `tp`.`content_id` AND `tp`.`type`=''bx_courses'' WHERE 1 AND `tp`.`status`=''active''', @iMaxOrderStats + 1);
+
+-- CHARTS
+SET @iMaxOrderCharts = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_objects_chart`);
+INSERT INTO `sys_objects_chart` (`object`, `title`, `table`, `field_date_ts`, `field_date_dt`, `field_status`, `query`, `active`, `order`, `class_name`, `class_file`) VALUES
+('bx_courses_growth', '_bx_courses_chart_growth', 'bx_courses_data', 'added', '', '', 'SELECT {field_date_formatted} AS `period`, COUNT(*) AS {object} FROM {table} LEFT JOIN `sys_profiles` AS `tp` ON {table}.`id` = `tp`.`content_id` AND `tp`.`type`=''bx_courses'' WHERE 1 AND `tp`.`status`=''active'' {where_inteval} GROUP BY `period` ORDER BY {table}.{field_date} ASC', 1, @iMaxOrderCharts + 1, 'BxDolChartGrowth', ''),
+('bx_courses_growth_speed', '_bx_courses_chart_growth_speed', 'bx_courses_data', 'added', '', '', 'SELECT {field_date_formatted} AS `period`, COUNT(*) AS {object} FROM {table} LEFT JOIN `sys_profiles` AS `tp` ON {table}.`id` = `tp`.`content_id` AND `tp`.`type`=''bx_courses'' WHERE 1 AND `tp`.`status`=''active'' {where_inteval} GROUP BY `period` ORDER BY {table}.{field_date} ASC', 1, @iMaxOrderCharts + 2, 'BxDolChartGrowthSpeed', '');
+
+-- GRID: connections
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `responsive`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_fans', 'Sql', 'SELECT `p`.`id`, `c`.`added`, `c`.`mutual` FROM `sys_profiles` AS `p` INNER JOIN `sys_accounts` AS `a` ON (`a`.`id` = `p`.`account_id`) {join_connections}', 'sys_profiles', 'id', 'c`.`added', '', '', 10, NULL, 'start', '', 'name,email', '', 'auto', '', '', 2147483647, 0, 'BxCoursesGridConnections', 'modules/boonex/courses/classes/BxCoursesGridConnections.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `params`, `order`) VALUES
+('bx_courses_fans', 'name', '_sys_name', '50%', '', 10),
+('bx_courses_fans', 'role', '_bx_courses_txt_role', '10%', '', 15),
+('bx_courses_fans', 'role_added', '_bx_courses_txt_role_added', '10%', '', 16),
+('bx_courses_fans', 'role_expired', '_bx_courses_txt_role_expired', '10%', '', 17),
+('bx_courses_fans', 'actions', '', '20%', '', 20);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
+('bx_courses_fans', 'single', 'accept', '_sys_accept', '', 0, 10),
+('bx_courses_fans', 'single', 'set_role', '_bx_courses_txt_set_role', '', 0, 20),
+('bx_courses_fans', 'single', 'set_role_submit', '', '', 0, 21),
+('bx_courses_fans', 'single', 'delete', '', 'remove', 1, 40);
+
+
+-- GRID: invites
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `responsive`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_invites', 'Sql', 'SELECT `bx_courses_invites`.`id`, `bx_courses_invites`.`invited_profile_id`, `bx_courses_invites`.`added`, `bx_courses_invites`.`author_profile_id` FROM `sys_profiles` AS `p` INNER JOIN `sys_accounts` AS `a` ON (`a`.`id` = `p`.`account_id`) INNER JOIN `bx_courses_invites` ON `bx_courses_invites`.`invited_profile_id` = `p`.`id` ', 'bx_courses_invites', 'id', 'bx_courses_invites`.`added', '', '', 10, NULL, 'start', '', 'name,email', '', 'auto', '', '', 2147483647, 0, 'BxCoursesGridInvites', 'modules/boonex/courses/classes/BxCoursesGridInvites.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `params`, `order`) VALUES
+('bx_courses_invites', 'name', '_sys_name', '33%', '', 10),
+('bx_courses_invites', 'added', '_sys_added', '33%', '', 20),
+('bx_courses_invites', 'actions', '', '34%', '', 30);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
+('bx_courses_invites', 'single', 'delete', '', 'remove', 1, 10);
+
+
+-- GRIDS: administration
+
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_administration', 'Sql', 'SELECT `td`.*, `td`.`name` AS `name`, `ta`.`email` AS `account`, `td`.`added` AS `added_ts`, `tp`.`status` AS `status_profile` FROM `bx_courses_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_courses'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_courses_data', 'id', 'added', 'status_admin', '', 20, NULL, 'start', '', 'name', '', 'like', 'reports', '', 192, 'BxCoursesGridAdministration', 'modules/boonex/courses/classes/BxCoursesGridAdministration.php'),
+('bx_courses_common', 'Sql', 'SELECT `td`.*, `td`.`name` AS `name`, `ta`.`email` AS `account`, `td`.`added` AS `added_ts`, `tp`.`status` AS `status_profile` FROM `bx_courses_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_courses'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_courses_data', 'id', 'added', 'status', '', 20, NULL, 'start', '', 'name', '', 'like', '', '', 2147483647, 'BxCoursesGridCommon', 'modules/boonex/courses/classes/BxCoursesGridCommon.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_courses_administration', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_courses_administration', 'switcher', '_bx_courses_grid_column_title_adm_active', '8%', 0, '', '', 2),
+('bx_courses_administration', 'reports', '_sys_txt_reports_title', '5%', 0, '', '', 3),
+('bx_courses_administration', 'name', '_bx_courses_grid_column_title_adm_name', '25%', 0, '', '', 4),
+('bx_courses_administration', 'added_ts', '_bx_courses_grid_column_title_adm_added', '20%', 1, '25', '', 5),
+('bx_courses_administration', 'account', '_bx_courses_grid_column_title_adm_account', '20%', 0, '25', '', 6),
+('bx_courses_administration', 'actions', '', '20%', 0, '', '', 7),
+
+('bx_courses_common', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_courses_common', 'switcher', '_bx_courses_grid_column_title_adm_active', '8%', 0, '', '', 2),
+('bx_courses_common', 'name', '_bx_courses_grid_column_title_adm_name', '40%', 0, '', '', 3),
+('bx_courses_common', 'added_ts', '_bx_courses_grid_column_title_adm_added', '30%', 1, '25', '', 4),
+('bx_courses_common', 'actions', '', '20%', 0, '', '', 5);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `active`, `order`) VALUES
+('bx_courses_administration', 'bulk', 'clear_reports', '_bx_courses_grid_action_title_adm_clear_reports', '', 0, 1, 1, 1),
+('bx_courses_administration', 'bulk', 'delete', '_bx_courses_grid_action_title_adm_delete', '', 0, 1, 1, 2),
+('bx_courses_administration', 'bulk', 'delete_with_content', '_bx_courses_grid_action_title_adm_delete_with_content', '', 0, 1, 0, 3),
+('bx_courses_administration', 'single', 'settings', '_bx_courses_grid_action_title_adm_more_actions', 'cog', 1, 0, 1, 2),
+('bx_courses_administration', 'single', 'audit_content', '_bx_courses_grid_action_title_adm_audit_content', 'search', 1, 0, 1, 3),
+('bx_courses_administration', 'single', 'audit_context', '_bx_courses_grid_action_title_adm_audit_context', 'search-location', 1, 0, 1, 4),
+
+('bx_courses_common', 'bulk', 'delete', '_bx_courses_grid_action_title_adm_delete', '', 0, 1, 1, 1),
+('bx_courses_common', 'bulk', 'delete_with_content', '_bx_courses_grid_action_title_adm_delete_with_content', '', 0, 1, 0, 2),
+('bx_courses_common', 'single', 'settings', '_bx_courses_grid_action_title_adm_more_actions', 'cog', 1, 0, 1, 1);
+
+-- GRIDS: Pricing
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_prices_manage', 'Sql', 'SELECT * FROM `bx_courses_prices` WHERE 1 ', 'bx_courses_prices', 'id', 'order', '', '', 100, NULL, 'start', '', 'period,period_unit,price', '', 'like', '', '', 2147483647, 'BxCoursesGridPricesManage', 'modules/boonex/courses/classes/BxCoursesGridPricesManage.php'),
+('bx_courses_prices_view', 'Sql', 'SELECT * FROM `bx_courses_prices` WHERE 1 ', 'bx_courses_prices', 'id', 'order', '', '', 100, NULL, 'start', '', 'period,period_unit,price', '', 'like', '', '', 2147483647, 'BxCoursesGridPricesView', 'modules/boonex/courses/classes/BxCoursesGridPricesView.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_courses_prices_manage', 'checkbox', '_sys_select', '1%', 0, '', '', 1),
+('bx_courses_prices_manage', 'order', '', '1%', 0, '', '', 2),
+('bx_courses_prices_manage', 'name', '_bx_courses_grid_column_title_name', '38%', 0, 32, '', 3),
+('bx_courses_prices_manage', 'price', '_bx_courses_grid_column_title_price', '20%', 0, 16, '', 4),
+('bx_courses_prices_manage', 'period', '_bx_courses_grid_column_title_period', '20%', 0, 16, '', 5),
+('bx_courses_prices_manage', 'actions', '', '20%', 0, '', '', 6),
+
+('bx_courses_prices_view', 'role_id', '_bx_courses_grid_column_title_role_id', '40%', 0, 32, '', 1),
+('bx_courses_prices_view', 'price', '_bx_courses_grid_column_title_price', '20%', 0, 16, '', 2),
+('bx_courses_prices_view', 'period', '_bx_courses_grid_column_title_period', '20%', 0, 16, '', 3),
+('bx_courses_prices_view', 'actions', '', '20%', 0, '', '', 4);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_courses_prices_manage', 'independent', 'add', '_bx_courses_grid_action_title_add', '', 0, 0, 1),
+('bx_courses_prices_manage', 'single', 'edit', '_bx_courses_grid_action_title_edit', 'pencil-alt', 1, 0, 1),
+('bx_courses_prices_manage', 'single', 'delete', '_bx_courses_grid_action_title_delete', 'remove', 1, 1, 2),
+('bx_courses_prices_manage', 'bulk', 'delete', '_bx_courses_grid_action_title_delete', '', 0, 1, 1),
+
+('bx_courses_prices_view', 'single', 'buy', '_bx_courses_grid_action_title_buy', 'cart-plus', 0, 0, 1),
+('bx_courses_prices_view', 'single', 'subscribe', '_bx_courses_grid_action_title_subscribe', 'credit-card', 0, 0, 2),
+('bx_courses_prices_view', 'single', 'choose', '_bx_courses_grid_action_title_choose', 'far check-square', 0, 0, 3);
+
+
+-- GRIDS: content structure
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('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', '', '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', '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
+('bx_courses_cnt_structure_manage', 'independent', 'back', '_bx_courses_grid_action_title_cnt_back', 'arrow-left', 0, 0, 1),
+('bx_courses_cnt_structure_manage', 'independent', 'add', '_bx_courses_grid_action_title_cnt_add', '', 0, 0, 2),
+('bx_courses_cnt_structure_manage', 'single', 'edit', '_bx_courses_grid_action_title_cnt_edit', 'pencil-alt', 1, 0, 1),
+('bx_courses_cnt_structure_manage', 'single', 'delete', '_bx_courses_grid_action_title_cnt_delete', 'remove', 1, 1, 2),
+('bx_courses_cnt_structure_manage', 'bulk', 'delete', '_bx_courses_grid_action_title_cnt_delete', '', 0, 1, 1);
+
+-- GRIDS: content data
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_cnt_data_manage', 'Sql', 'SELECT * FROM `bx_courses_content_data` WHERE 1 ', 'bx_courses_content_data', 'id', 'order', '', '', 20, NULL, 'start', '', '', '', 'like', '', '', 2147483647, 'BxCoursesGridCntDataManage', 'modules/boonex/courses/classes/BxCoursesGridCntDataManage.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_courses_cnt_data_manage', 'order', '', '4%', 0, 0, '', 1),
+('bx_courses_cnt_data_manage', 'checkbox', '_sys_select', '4%', 0, 0, '', 2),
+('bx_courses_cnt_data_manage', 'content_id', '_bx_courses_grid_column_title_cnt_content_id', '42%', 0, 0, '', 3),
+('bx_courses_cnt_data_manage', 'content_type', '_bx_courses_grid_column_title_cnt_content_type', '10%', 0, 0, '', 4),
+('bx_courses_cnt_data_manage', 'usage', '_bx_courses_grid_column_title_cnt_usage', '10%', 0, 0, '', 5),
+('bx_courses_cnt_data_manage', 'added', '_bx_courses_grid_column_title_cnt_added', '10%', 0, 0, '', 6),
+('bx_courses_cnt_data_manage', 'actions', '', '20%', 0, '', '', 7);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_courses_cnt_data_manage', 'independent', 'back', '_bx_courses_grid_action_title_cnt_back', 'arrow-left', 0, 0, 1),
+('bx_courses_cnt_data_manage', 'independent', 'add_st', '_bx_courses_grid_action_title_cnt_add_step', '', 0, 0, 2),
+('bx_courses_cnt_data_manage', 'independent', 'add_at', '_bx_courses_grid_action_title_cnt_add_attachment', '', 0, 0, 3),
+('bx_courses_cnt_data_manage', 'single', 'delete', '_bx_courses_grid_action_title_cnt_delete', 'remove', 1, 1, 1),
+('bx_courses_cnt_data_manage', 'bulk', 'delete', '_bx_courses_grid_action_title_cnt_delete', '', 0, 1, 1);
+
+
+-- ALERTS
+
+INSERT INTO `sys_alerts_handlers` (`name`, `class`, `file`, `service_call`) VALUES 
+('bx_courses', 'BxCoursesAlertsResponse', 'modules/boonex/courses/classes/BxCoursesAlertsResponse.php', '');
+SET @iHandler := LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('system', 'save_setting', @iHandler),
+('bx_timeline', 'post_common', @iHandler),
+('bx_courses_pics', 'file_deleted', @iHandler),
+('bx_courses_fans', 'connection_added', @iHandler),
+('bx_courses_fans', 'connection_removed', @iHandler),
+('profile', 'delete', @iHandler),
+('bx_courses', 'fan_added', @iHandler),
+('bx_courses', 'join_invitation', @iHandler),
+('bx_courses', 'join_request', @iHandler),
+('bx_courses', 'join_request_accepted', @iHandler),
+('bx_courses', 'timeline_view', @iHandler),
+('bx_courses', 'timeline_post', @iHandler),
+('bx_courses', 'timeline_delete', @iHandler),
+('bx_courses', 'timeline_comment', @iHandler),
+('bx_courses', 'timeline_vote', @iHandler),
+('bx_courses', 'timeline_score', @iHandler),
+('bx_courses', 'timeline_report', @iHandler),
+('bx_courses', 'timeline_repost', @iHandler),
+('bx_courses', 'timeline_pin', @iHandler),
+('bx_courses', 'timeline_promote', @iHandler);
+
+-- PRIVACY
+
+INSERT INTO `sys_objects_privacy` (`object`, `module`, `action`, `title`, `default_group`, `spaces`, `table`, `table_field_id`, `table_field_author`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_allow_view_to', 'bx_courses', 'view', '_bx_courses_form_profile_input_allow_view_to', '3', '', 'bx_courses_data', 'id', 'author', 'BxCoursesPrivacy', 'modules/boonex/courses/classes/BxCoursesPrivacy.php'),
+('bx_courses_allow_view_notification_to', 'bx_courses', 'view_event', '_bx_courses_form_profile_input_allow_view_notification_to', '3', '', 'bx_notifications_events', 'id', 'object_owner_id', 'BxCoursesPrivacyNotifications', 'modules/boonex/courses/classes/BxCoursesPrivacyNotifications.php'),
+('bx_courses_allow_post_to', 'bx_courses', 'post', '_bx_courses_form_profile_input_allow_post_to', 'p', '', 'bx_courses_data', 'id', 'author', 'BxCoursesPrivacyPost', 'modules/boonex/courses/classes/BxCoursesPrivacyPost.php'),
+('bx_courses_allow_view_favorite_list', 'bx_courses', 'view_favorite_list', '_bx_courses_form_profile_input_allow_view_favorite_list', '3', '', 'bx_courses_favorites_lists', 'id', 'author_id', 'BxCoursesPrivacy', 'modules/boonex/courses/classes/BxCoursesPrivacy.php');
+
+-- EMAIL TEMPLATES
+
+INSERT INTO `sys_email_templates` (`Module`, `NameSystem`, `Name`, `Subject`, `Body`) VALUES
+('bx_courses', '_bx_courses_email_join_request', 'bx_courses_join_request', '_bx_courses_email_join_request_subject', '_bx_courses_email_join_request_body'),
+('bx_courses', '_bx_courses_email_join_reject', 'bx_courses_join_reject', '_bx_courses_email_join_reject_subject', '_bx_courses_email_join_reject_body'),
+('bx_courses', '_bx_courses_email_join_confirm', 'bx_courses_join_confirm', '_bx_courses_email_join_confirm_subject', '_bx_courses_email_join_confirm_body'),
+('bx_courses', '_bx_courses_email_fan_remove', 'bx_courses_fan_remove', '_bx_courses_email_fan_remove_subject', '_bx_courses_email_fan_remove_body'),
+('bx_courses', '_bx_courses_email_fan_become_admin', 'bx_courses_fan_become_admin', '_bx_courses_email_fan_become_admin_subject', '_bx_courses_email_fan_become_admin_body'),
+('bx_courses', '_bx_courses_email_admin_become_fan', 'bx_courses_admin_become_fan', '_bx_courses_email_admin_become_fan_subject', '_bx_courses_email_admin_become_fan_body'),
+('bx_courses', '_bx_courses_email_set_role', 'bx_courses_set_role', '_bx_courses_email_set_role_subject', '_bx_courses_email_set_role_body'),
+('bx_courses', '_bx_courses_email_invitation', 'bx_courses_invitation', '_bx_courses_email_invitation_subject', '_bx_courses_email_invitation_body');
+
+-- UPLOADERS
+
+INSERT INTO `sys_objects_uploader` (`object`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_courses_cover_crop', 1, 'BxCoursesUploaderCoverCrop', 'modules/boonex/courses/classes/BxCoursesUploaderCoverCrop.php'),
+('bx_courses_picture_crop', 1, 'BxCoursesUploaderPictureCrop', 'modules/boonex/courses/classes/BxCoursesUploaderPictureCrop.php');
+
+-- CRON
+
+INSERT INTO `sys_cron_jobs` (`name`, `time`, `class`, `file`, `service_call`) VALUES
+('bx_courses_pruning', '0 0 * * *', 'BxCoursesCronPruning', 'modules/boonex/courses/classes/BxCoursesCronPruning.php', '');

+ 590 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/install/sql/install.sql

@@ -0,0 +1,590 @@
+SET @sStorageEngine = (SELECT `value` FROM `sys_options` WHERE `name` = 'sys_storage_default');
+
+-- TABLE: PROFILES
+CREATE TABLE IF NOT EXISTS `bx_courses_data` (
+  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+  `author` int(10) unsigned NOT NULL,
+  `added` int(11) NOT NULL,
+  `changed` int(11) NOT NULL,
+  `picture` int(11) NOT NULL,
+  `cover` int(11) NOT NULL,
+  `cover_data` varchar(50) NOT NULL,
+  `name` varchar(255) NOT NULL,
+  `cat` int(11) NOT NULL,
+  `desc` text NOT NULL,
+  `labels` text NOT NULL,
+  `location` text NOT NULL,
+  `views` int(11) NOT NULL default '0',
+  `rate` float NOT NULL default '0',
+  `votes` int(11) NOT NULL default '0',
+  `score` int(11) NOT NULL default '0',
+  `sc_up` int(11) NOT NULL default '0',
+  `sc_down` int(11) NOT NULL default '0',
+  `favorites` int(11) NOT NULL default '0',
+  `comments` int(11) NOT NULL default '0',
+  `reports` int(11) NOT NULL default '0',
+  `featured` int(11) NOT NULL default '0',
+  `cf` int(11) NOT NULL default '1',
+  `join_confirmation` tinyint(4) NOT NULL DEFAULT '0',
+  `allow_view_to` varchar(16) NOT NULL DEFAULT '3',
+  `allow_post_to` varchar(16) NOT NULL DEFAULT '3',
+  `status` enum('active','awaiting','hidden') NOT NULL DEFAULT 'active',
+  `status_admin` enum('active','hidden','pending') NOT NULL DEFAULT 'active',
+  PRIMARY KEY (`id`),
+  FULLTEXT KEY `search_fields` (`name`, `desc`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_content_structure` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `entry_id` int(11) NOT NULL default '0',
+  `parent_id` int(11) NOT NULL DEFAULT '0',
+  `node_id` int(11) NOT NULL DEFAULT '0',
+  `level` tinyint(4) NOT NULL DEFAULT '0',
+  `order` int(11) NOT NULL DEFAULT '0',
+  `cn_l2` int(11) NOT NULL DEFAULT '0',
+  `cn_l3` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `node_id` (`node_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_content_nodes` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `entry_id` int(11) NOT NULL default '0',
+  `title` varchar(255) NOT NULL DEFAULT '',
+  `text` text NOT NULL,
+  `passing` tinyint(4) NOT NULL DEFAULT '0',
+  `counters` text NOT NULL,
+  `added` int(11) NOT NULL,
+  `status` enum('active','hidden') NOT NULL DEFAULT 'active',
+  PRIMARY KEY (`id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_content_nodes2users` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `node_id` int(11) NOT NULL default '0',
+  `profile_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `pass` (`node_id`, `profile_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_content_data` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `entry_id` int(11) NOT NULL default '0',
+  `node_id` int(11) NOT NULL DEFAULT '0',
+  `content_type` varchar(32) NOT NULL DEFAULT '',
+  `content_id` int(11) NOT NULL DEFAULT '0',
+  `usage` tinyint(4) NOT NULL DEFAULT '0',
+  `added` int(11) NOT NULL DEFAULT '0',
+  `order` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_content_data2users` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `data_id` int(11) NOT NULL default '0',
+  `profile_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `pass` (`data_id`, `profile_id`)
+);
+
+-- TABLE: STORAGES & TRANSCODERS
+CREATE TABLE IF NOT EXISTS `bx_courses_pics` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_pics_resized` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
+-- TABLE: comments
+CREATE TABLE IF NOT EXISTS `bx_courses_cmts` (
+  `cmt_id` int(11) NOT NULL AUTO_INCREMENT,
+  `cmt_parent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_vparent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_object_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_author_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_level` int(11) NOT NULL DEFAULT '0',
+  `cmt_text` text NOT NULL,
+  `cmt_mood` tinyint(4) NOT NULL DEFAULT '0',
+  `cmt_rate` int(11) NOT NULL DEFAULT '0',
+  `cmt_rate_count` int(11) NOT NULL DEFAULT '0',
+  `cmt_time` int(11) unsigned NOT NULL DEFAULT '0',
+  `cmt_replies` int(11) NOT NULL DEFAULT '0',
+  `cmt_pinned` int(11) NOT NULL default '0',
+  `cmt_cf` int(11) NOT NULL default '1',
+  PRIMARY KEY (`cmt_id`),
+  KEY `cmt_object_id` (`cmt_object_id`,`cmt_parent_id`),
+  FULLTEXT KEY `search_fields` (`cmt_text`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_cmts_notes` (
+  `cmt_id` int(11) NOT NULL AUTO_INCREMENT,
+  `cmt_parent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_vparent_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_object_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_author_id` int(11) NOT NULL DEFAULT '0',
+  `cmt_level` int(11) NOT NULL DEFAULT '0',
+  `cmt_text` text NOT NULL,
+  `cmt_mood` tinyint(4) NOT NULL DEFAULT '0',
+  `cmt_rate` int(11) NOT NULL DEFAULT '0',
+  `cmt_rate_count` int(11) NOT NULL DEFAULT '0',
+  `cmt_time` int(11) unsigned NOT NULL DEFAULT '0',
+  `cmt_replies` int(11) NOT NULL DEFAULT '0',
+  `cmt_pinned` int(11) NOT NULL default '0',
+  PRIMARY KEY (`cmt_id`),
+  KEY `cmt_object_id` (`cmt_object_id`,`cmt_parent_id`),
+  FULLTEXT KEY `search_fields` (`cmt_text`)
+);
+
+-- TABLE: VIEWS
+CREATE TABLE IF NOT EXISTS `bx_courses_views_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `viewer_id` int(11) NOT NULL default '0',
+  `viewer_nip` int(11) unsigned NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `id` (`object_id`,`viewer_id`,`viewer_nip`)
+);
+
+-- TABLE: VOTES
+CREATE TABLE IF NOT EXISTS `bx_courses_votes` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_votes_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `value` tinyint(4) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+-- TABLE: REPORTS
+CREATE TABLE IF NOT EXISTS `bx_courses_reports` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_reports_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `type` varchar(32) NOT NULL default '',
+  `text` text NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  `checked_by` int(11) NOT NULL default '0',
+  `status` tinyint(11) NOT NULL default '0',	
+  PRIMARY KEY (`id`),
+  KEY `report` (`object_id`, `author_nip`)
+);
+
+-- TABLE: metas
+CREATE TABLE IF NOT EXISTS `bx_courses_meta_keywords` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(10) unsigned NOT NULL,
+  `keyword` varchar(255) NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `object_id` (`object_id`),
+  KEY `keyword` (`keyword`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_meta_locations` (
+  `object_id` int(10) unsigned NOT NULL,
+  `lat` double NOT NULL,
+  `lng` double NOT NULL,
+  `country` varchar(2) NOT NULL,
+  `state` varchar(255) NOT NULL,
+  `city` varchar(255) NOT NULL,
+  `zip` varchar(255) NOT NULL,
+  `street` varchar(255) NOT NULL,
+  `street_number` varchar(255) NOT NULL,
+  PRIMARY KEY (`object_id`),
+  KEY `country_state_city` (`country`,`state`(8),`city`(8))
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_meta_mentions` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(10) unsigned NOT NULL,
+  `profile_id` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  KEY `object_id` (`object_id`),
+  KEY `profile_id` (`profile_id`)
+);
+
+-- TABLE: fans
+CREATE TABLE IF NOT EXISTS `bx_courses_fans` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `initiator` int(11) NOT NULL,
+  `content` int(11) NOT NULL,
+  `mutual` tinyint(4) NOT NULL,
+  `added` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `initiator` (`initiator`,`content`),
+  KEY `content` (`content`)
+);
+
+-- TABLE: admins
+CREATE TABLE IF NOT EXISTS `bx_courses_admins` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `group_profile_id` int(10) unsigned NOT NULL,
+  `fan_id` int(10) unsigned NOT NULL,
+  `role` int(10) unsigned NOT NULL default '0',
+  `order` varchar(32) NOT NULL default '',
+  `added` int(11) unsigned NOT NULL default '0',
+  `expired` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `admin` (`group_profile_id`,`fan_id`)
+);
+
+-- TABLE: favorites
+CREATE TABLE IF NOT EXISTS `bx_courses_favorites_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `list_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `id` (`object_id`,`author_id`)
+);
+
+CREATE TABLE `bx_courses_favorites_lists` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `title` varchar(255) NOT NULL,
+  `author_id` int(11) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  `allow_view_favorite_list_to` varchar(16) NOT NULL DEFAULT '3',
+   PRIMARY KEY (`id`)
+);
+
+
+-- TABLE: scores
+CREATE TABLE IF NOT EXISTS `bx_courses_scores` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `count_up` int(11) NOT NULL default '0',
+  `count_down` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `object_id` (`object_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_scores_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `author_id` int(11) NOT NULL default '0',
+  `author_nip` int(11) unsigned NOT NULL default '0',
+  `type` varchar(8) NOT NULL default '',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_courses_invites` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `key` varchar(128) NOT NULL default '0',
+  `group_profile_id` int(11) NOT NULL default '0',
+  `author_profile_id` int(11) NOT NULL default '0',
+  `invited_profile_id` int(11) NOT NULL default '0',
+  `added` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`)
+);
+
+-- TABLE: Pricing
+CREATE TABLE IF NOT EXISTS `bx_courses_prices` (
+  `id` int(11) NOT NULL auto_increment,
+  `profile_id` int(11) NOT NULL default '0',
+  `role_id` int(11) unsigned NOT NULL default '0',
+  `name` varchar(128) NOT NULL default '',
+  `period` int(11) unsigned NOT NULL default '1',
+  `period_unit` varchar(32) NOT NULL default '',
+  `price` float unsigned NOT NULL default '1',
+  `order` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`),
+  UNIQUE KEY `type` (`profile_id`, `role_id`,`period`, `period_unit`)
+);
+
+-- STORAGES & TRANSCODERS
+INSERT INTO `sys_objects_storage` (`object`, `engine`, `params`, `token_life`, `cache_control`, `levels`, `table_files`, `ext_mode`, `ext_allow`, `ext_deny`, `quota_size`, `current_size`, `quota_number`, `current_number`, `max_file_size`, `ts`) VALUES
+('bx_courses_pics', @sStorageEngine, '', 360, 2592000, 3, 'bx_courses_pics', 'allow-deny', '{image}', '', 0, 0, 0, 0, 0, 0),
+('bx_courses_pics_resized', @sStorageEngine, '', 360, 2592000, 3, 'bx_courses_pics_resized', 'allow-deny', '{image}', '', 0, 0, 0, 0, 0, 0);
+
+INSERT INTO `sys_objects_transcoder` (`object`, `storage_object`, `source_type`, `source_params`, `private`, `atime_tracking`, `atime_pruning`, `ts`) VALUES 
+('bx_courses_icon', 'bx_courses_pics_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_courses_pics";}', 'no', '1', '2592000', '0'),
+('bx_courses_thumb', 'bx_courses_pics_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_courses_pics";}', 'no', '1', '2592000', '0'),
+('bx_courses_avatar', 'bx_courses_pics_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_courses_pics";}', 'no', '1', '2592000', '0'),
+('bx_courses_avatar_big', 'bx_courses_pics_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_courses_pics";}', 'no', '1', '2592000', '0'),
+('bx_courses_picture', 'bx_courses_pics_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_courses_pics";}', 'no', '1', '2592000', '0'),
+('bx_courses_cover', 'bx_courses_pics_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_courses_pics";}', 'no', '1', '2592000', '0'),
+('bx_courses_cover_thumb', 'bx_courses_pics_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_courses_pics";}', 'no', '1', '2592000', '0'),
+('bx_courses_gallery', 'bx_courses_pics_resized', 'Storage', 'a:1:{s:6:"object";s:15:"bx_courses_pics";}', 'no', '1', '2592000', '0');
+
+INSERT INTO `sys_transcoder_filters` (`transcoder_object`, `filter`, `filter_params`, `order`) VALUES 
+('bx_courses_icon', 'Resize', 'a:3:{s:1:"w";s:2:"30";s:1:"h";s:2:"30";s:13:"square_resize";s:1:"1";}', '0'),
+('bx_courses_thumb', 'Resize', 'a:3:{s:1:"w";s:2:"50";s:1:"h";s:2:"50";s:13:"square_resize";s:1:"1";}', '0'),
+('bx_courses_avatar', 'Resize', 'a:3:{s:1:"w";s:3:"100";s:1:"h";s:3:"100";s:13:"square_resize";s:1:"1";}', '0'),
+('bx_courses_avatar_big', 'Resize', 'a:3:{s:1:"w";s:3:"200";s:1:"h";s:3:"200";s:13:"square_resize";s:1:"1";}', '0'),
+('bx_courses_picture', 'Resize', 'a:3:{s:1:"w";s:4:"1024";s:1:"h";s:4:"1024";s:13:"square_resize";s:1:"0";}', '0'),
+('bx_courses_cover', 'Resize', 'a:1:{s:1:"w";s:4:"1200";}', '0'),
+('bx_courses_cover_thumb', 'Resize', 'a:3:{s:1:"w";s:2:"48";s:1:"h";s:2:"48";s:13:"square_resize";s:1:"1";}', '0'),
+('bx_courses_gallery', 'Resize', 'a:1:{s:1:"w";s:3:"500";}', '0');
+
+-- FORMS
+INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_course', 'bx_courses', '_bx_courses_form_profile', '', 'a:1:{s:7:\"enctype\";s:19:\"multipart/form-data\";}', 'bx_courses_data', 'id', '', '', 'do_submit', '', 0, 1, 'BxCoursesFormEntry', 'modules/boonex/courses/classes/BxCoursesFormEntry.php');
+
+INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
+('bx_course', 'bx_course_add', 'bx_courses', 0, '_bx_courses_form_profile_display_add'),
+('bx_course', 'bx_course_delete', 'bx_courses', 0, '_bx_courses_form_profile_display_delete'),
+('bx_course', 'bx_course_edit', 'bx_courses', 0, '_bx_courses_form_profile_display_edit'),
+('bx_course', 'bx_course_edit_cover', 'bx_courses', 0, '_bx_courses_form_profile_display_edit_cover'),
+('bx_course', 'bx_course_view', 'bx_courses', 1, '_bx_courses_form_profile_display_view'),
+('bx_course', 'bx_course_view_full', 'bx_courses', 1, '_bx_courses_form_profile_display_view_full'),
+('bx_course', 'bx_course_invite', 'bx_courses', 0, '_bx_courses_form_profile_display_invite');
+
+INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
+('bx_course', 'bx_courses', 'cf', '1', '#!sys_content_filter', 0, 'select', '_sys_form_entry_input_sys_cf', '_sys_form_entry_input_cf', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_course', 'bx_courses', 'allow_view_to', 3, '', 0, 'custom', '_bx_courses_form_profile_input_sys_allow_view_to', '_bx_courses_form_profile_input_allow_view_to', '_bx_courses_form_profile_input_allow_view_to_desc', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_course', 'bx_courses', 'allow_post_to', 3, '', 0, 'custom', '_bx_courses_form_profile_input_sys_allow_post_to', '_bx_courses_form_profile_input_allow_post_to', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_course', 'bx_courses', 'delete_confirm', 1, '', 0, 'checkbox', '_bx_courses_form_profile_input_sys_delete_confirm', '_bx_courses_form_profile_input_delete_confirm', '_bx_courses_form_profile_input_delete_confirm_info', 1, 0, 0, '', '', '', 'avail', '', '_bx_courses_form_profile_input_delete_confirm_error', '', '', 1, 0),
+('bx_course', 'bx_courses', 'do_submit', '_sys_form_account_input_submit', '', 0, 'submit', '_bx_courses_form_profile_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_course', 'bx_courses', 'desc', '', '', 0, 'textarea', '_bx_courses_form_profile_input_sys_desc', '_bx_courses_form_profile_input_desc', '', 0, 0, 2, '', '', '', '', '', '', 'XssHtml', '', 1, 1),
+('bx_course', 'bx_courses', 'cat', '', '#!bx_courses_cats', 0, 'select', '_bx_courses_form_profile_input_sys_cat', '_bx_courses_form_profile_input_cat', '', 1, 0, 0, '', '', '', 'avail', '', '_bx_courses_form_profile_input_cat_err', 'Xss', '', 1, 1),
+('bx_course', 'bx_courses', 'name', '', '', 0, 'text', '_bx_courses_form_profile_input_sys_name', '_bx_courses_form_profile_input_name', '', 1, 0, 0, '', '', '', 'avail', '', '_bx_courses_form_profile_input_name_err', 'Xss', '', 1, 0),
+('bx_course', 'bx_courses', 'initial_members', '', '', 0, 'custom', '_bx_courses_form_profile_input_sys_initial_members', '_bx_courses_form_profile_input_initial_members', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 1),
+('bx_course', 'bx_courses', 'join_confirmation', 1, '', 0, 'switcher', '_bx_courses_form_profile_input_sys_join_confirm', '_bx_courses_form_profile_input_join_confirm', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_course', 'bx_courses', 'cover', 'a:1:{i:0;s:21:"bx_courses_cover_crop";}', 'a:1:{s:21:"bx_courses_cover_crop";s:24:"_sys_uploader_crop_title";}', 0, 'files', '_bx_courses_form_profile_input_sys_cover', '_bx_courses_form_profile_input_cover', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_course', 'bx_courses', 'picture', 'a:1:{i:0;s:23:"bx_courses_picture_crop";}', 'a:1:{s:23:"bx_courses_picture_crop";s:24:"_sys_uploader_crop_title";}', 0, 'files', '_bx_courses_form_profile_input_sys_picture', '_bx_courses_form_profile_input_picture', '', 0, 0, 0, '', '', '', '', '', '_bx_courses_form_profile_input_picture_err', '', '', 1, 0),
+('bx_course', 'bx_courses', 'location', '', '', 0, 'location', '_sys_form_input_sys_location', '_sys_form_input_location', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_course', 'bx_courses', 'labels', '', '', 0, 'custom', '_sys_form_input_sys_labels', '_sys_form_input_labels', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES 
+('bx_course_add', 'initial_members', 2147483647, 1, 1),
+('bx_course_add', 'name', 2147483647, 1, 2),
+('bx_course_add', 'cat', 2147483647, 1, 3),
+('bx_course_add', 'desc', 2147483647, 1, 4),
+('bx_course_add', 'location', 2147483647, 1, 5),
+('bx_course_add', 'join_confirmation', 2147483647, 1, 6),
+('bx_course_add', 'allow_view_to', 2147483647, 1, 7),
+('bx_course_add', 'allow_post_to', 2147483647, 1, 8),
+('bx_course_add', 'cf', 2147483647, 1, 9),
+('bx_course_add', 'do_submit', 2147483647, 1, 10),
+
+('bx_course_invite', 'initial_members', 2147483647, 1, 1),
+('bx_course_invite', 'do_submit', 2147483647, 1, 2),
+
+('bx_course_delete', 'delete_confirm', 2147483647, 1, 1),
+('bx_course_delete', 'do_submit', 2147483647, 1, 2),
+
+('bx_course_edit', 'name', 2147483647, 1, 1),
+('bx_course_edit', 'cat', 2147483647, 1, 2),
+('bx_course_edit', 'desc', 2147483647, 1, 3),
+('bx_course_edit', 'location', 2147483647, 1, 4),
+('bx_course_edit', 'join_confirmation', 2147483647, 1, 5),
+('bx_course_edit', 'allow_view_to', 2147483647, 1, 6),
+('bx_course_edit', 'allow_post_to', 2147483647, 1, 7),
+('bx_course_edit', 'cf', 2147483647, 1, 8),
+('bx_course_edit', 'do_submit', 2147483647, 1, 9),
+
+('bx_course_edit_cover', 'cover', 2147483647, 1, 1),
+('bx_course_edit_cover', 'do_submit', 2147483647, 1, 2),
+
+('bx_course_view', 'name', 2147483647, 1, 1),
+('bx_course_view', 'cat', 2147483647, 1, 2),
+
+('bx_course_view_full', 'name', 2147483647, 1, 1),
+('bx_course_view_full', 'cat', 2147483647, 1, 2),
+('bx_course_view_full', 'desc', 2147483647, 1, 3);
+
+-- FORMS: Price
+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
+('bx_courses_price', 'bx_courses', '_bx_courses_form_price', '', '', 'do_submit', 'bx_courses_prices', 'id', '', '', '', 0, 1, 'BxCoursesFormPrice', 'modules/boonex/courses/classes/BxCoursesFormPrice.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_courses_price_add', 'bx_courses', 'bx_courses_price', '_bx_courses_form_price_display_add', 0),
+('bx_courses_price_edit', 'bx_courses', 'bx_courses_price', '_bx_courses_form_price_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_courses_price', 'bx_courses', 'id', '', '', 0, 'hidden', '_bx_courses_form_price_input_sys_id', '', '', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_courses_price', 'bx_courses', 'role_id', '', '', 0, 'hidden', '_bx_courses_form_price_input_sys_role_id', '', '', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_courses_price', 'bx_courses', 'name', '', '', 0, 'text', '_bx_courses_form_price_input_sys_name', '_bx_courses_form_price_input_name', '_bx_courses_form_price_input_inf_name', 1, 0, 0, '', '', '', 'Avail', '', '_bx_courses_form_price_input_err_name', 'Xss', '', 1, 0),
+('bx_courses_price', 'bx_courses', 'period', '', '', 0, 'text', '_bx_courses_form_price_input_sys_period', '_bx_courses_form_price_input_period', '_bx_courses_form_price_input_inf_period', 1, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_courses_price', 'bx_courses', 'period_unit', '', '#!bx_courses_period_units', 0, 'select', '_bx_courses_form_price_input_sys_period_unit', '_bx_courses_form_price_input_period_unit', '_bx_courses_form_price_input_inf_period_unit', 1, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_courses_price', 'bx_courses', 'price', '', '', 0, 'price', '_bx_courses_form_price_input_sys_price', '_bx_courses_form_price_input_price', '_bx_courses_form_price_input_inf_price', 1, 0, 0, '', '', '', '', '', '', 'Float', '', 1, 0),
+('bx_courses_price', 'bx_courses', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_courses_price', 'bx_courses', 'do_submit', '_bx_courses_form_price_input_do_submit', '', 0, 'submit', '_bx_courses_form_price_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_courses_price', 'bx_courses', 'do_cancel', '_bx_courses_form_price_input_do_cancel', '', 0, 'button', '_bx_courses_form_price_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_courses_price_add', 'id', 2147483647, 0, 1),
+('bx_courses_price_add', 'role_id', 2147483647, 1, 2),
+('bx_courses_price_add', 'name', 2147483647, 1, 3),
+('bx_courses_price_add', 'price', 2147483647, 1, 4),
+('bx_courses_price_add', 'period', 2147483647, 1, 5),
+('bx_courses_price_add', 'period_unit', 2147483647, 1, 6),
+('bx_courses_price_add', 'controls', 2147483647, 1, 7),
+('bx_courses_price_add', 'do_submit', 2147483647, 1, 8),
+('bx_courses_price_add', 'do_cancel', 2147483647, 1, 9),
+
+('bx_courses_price_edit', 'id', 2147483647, 1, 1),
+('bx_courses_price_edit', 'role_id', 2147483647, 1, 2),
+('bx_courses_price_edit', 'name', 2147483647, 1, 3),
+('bx_courses_price_edit', 'price', 2147483647, 1, 4),
+('bx_courses_price_edit', 'period', 2147483647, 1, 5),
+('bx_courses_price_edit', 'period_unit', 2147483647, 1, 6),
+('bx_courses_price_edit', 'controls', 2147483647, 1, 7),
+('bx_courses_price_edit', 'do_submit', 2147483647, 1, 8),
+('bx_courses_price_edit', 'do_cancel', 2147483647, 1, 9);
+
+-- FORMS: Content Nodes
+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
+('bx_courses_content_node', 'bx_courses', '_bx_courses_form_content_node', '', '', 'do_submit', 'bx_courses_content_nodes', 'id', '', '', '', 0, 1, 'BxCoursesFormContentNode', 'modules/boonex/courses/classes/BxCoursesFormContentNode.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_courses_content_node_add', 'bx_courses', 'bx_courses_content_node', '_bx_courses_form_content_node_display_add', 0),
+('bx_courses_content_node_edit', 'bx_courses', 'bx_courses_content_node', '_bx_courses_form_content_node_display_edit', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+('bx_courses_content_node', 'bx_courses', 'title', '', '', 0, 'text', '_bx_courses_form_content_node_input_sys_title', '_bx_courses_form_content_node_input_title', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_courses_form_content_node_input_title_err', 'Xss', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'text', '', '', 0, 'textarea', '_bx_courses_form_content_node_input_sys_text', '_bx_courses_form_content_node_input_text', '', 0, 0, 2, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'passing', '', '', 0, 'select', '_bx_courses_form_content_node_input_sys_passing', '_bx_courses_form_content_node_input_passing', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'do_submit', '_bx_courses_form_content_node_input_do_submit', '', 0, 'submit', '_bx_courses_form_content_node_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_courses_content_node', 'bx_courses', 'do_cancel', '_bx_courses_form_content_node_input_do_cancel', '', 0, 'button', '_bx_courses_form_content_node_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_courses_content_node_add', 'id', 2147483647, 0, 1),
+('bx_courses_content_node_add', 'title', 2147483647, 1, 2),
+('bx_courses_content_node_add', 'text', 2147483647, 1, 3),
+('bx_courses_content_node_add', 'passing', 2147483647, 1, 4),
+('bx_courses_content_node_add', 'controls', 2147483647, 1, 5),
+('bx_courses_content_node_add', 'do_submit', 2147483647, 1, 6),
+('bx_courses_content_node_add', 'do_cancel', 2147483647, 1, 7),
+
+('bx_courses_content_node_edit', 'id', 2147483647, 1, 1),
+('bx_courses_content_node_edit', 'title', 2147483647, 1, 2),
+('bx_courses_content_node_edit', 'text', 2147483647, 1, 3),
+('bx_courses_content_node_edit', 'passing', 2147483647, 1, 4),
+('bx_courses_content_node_edit', 'controls', 2147483647, 1, 5),
+('bx_courses_content_node_edit', 'do_submit', 2147483647, 1, 6),
+('bx_courses_content_node_edit', 'do_cancel', 2147483647, 1, 7);
+
+-- PRE-VALUES
+INSERT INTO `sys_form_pre_lists`(`key`, `title`, `module`, `use_for_sets`) VALUES
+('bx_courses_cats', '_bx_courses_pre_lists_cats', 'bx_courses', '0');
+
+INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`) VALUES
+('bx_courses_cats', '', 0, '_sys_please_select', ''),
+('bx_courses_cats', '1', 1, '_bx_courses_cat_General', '');
+
+INSERT INTO `sys_form_pre_lists`(`key`, `title`, `module`, `use_for_sets`) VALUES
+('bx_courses_roles', '_bx_courses_pre_lists_roles', 'bx_courses', '1');
+
+INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`) VALUES
+('bx_courses_roles', '0', 1, '_bx_courses_role_regular', ''),
+('bx_courses_roles', '1', 2, '_bx_courses_role_administrator', ''),
+('bx_courses_roles', '2', 3, '_bx_courses_role_moderator', '');
+
+INSERT INTO `sys_form_pre_lists`(`key`, `title`, `module`, `use_for_sets`) VALUES
+('bx_courses_period_units', '_bx_courses_pre_lists_period_units', 'bx_courses', '0');
+
+INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`) VALUES
+('bx_courses_period_units', '', 0, '_sys_please_select', ''),
+('bx_courses_period_units', 'day', 1, '_bx_courses_period_unit_day', ''),
+('bx_courses_period_units', 'week', 2, '_bx_courses_period_unit_week', ''),
+('bx_courses_period_units', 'month', 3, '_bx_courses_period_unit_month', ''),
+('bx_courses_period_units', 'year', 4, '_bx_courses_period_unit_year', '');
+
+-- COMMENTS
+INSERT INTO `sys_objects_cmts` (`Name`, `Module`, `Table`, `CharsPostMin`, `CharsPostMax`, `CharsDisplayMax`, `Html`, `PerView`, `PerViewReplies`, `BrowseType`, `IsBrowseSwitch`, `PostFormPosition`, `NumberOfLevels`, `IsDisplaySwitch`, `IsRatable`, `ViewingThreshold`, `IsOn`, `RootStylePrefix`, `BaseUrl`, `ObjectVote`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldTitle`, `TriggerFieldComments`, `ClassName`, `ClassFile`) VALUES
+('bx_courses', 'bx_courses', 'bx_courses_cmts', 1, 5000, 1000, 3, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-course-profile&id={object_id}', '', 'bx_courses_data', 'id', 'author', 'name', 'comments', 'BxCoursesCmts', 'modules/boonex/courses/classes/BxCoursesCmts.php'),
+('bx_courses_notes', 'bx_courses', 'bx_courses_cmts_notes', 1, 5000, 1000, 0, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-course-profile&id={object_id}', '', 'bx_courses_data', 'id', 'author', 'name', '', 'BxTemplCmtsNotes', '');
+
+-- VIEWS
+INSERT INTO `sys_objects_view` (`name`, `module`, `table_track`, `period`, `is_on`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_courses', 'bx_courses', 'bx_courses_views_track', '86400', '1', 'bx_courses_data', 'id', 'author', 'views', '', '');
+
+-- VOTES
+INSERT INTO `sys_objects_vote` (`Name`, `Module`, `TableMain`, `TableTrack`, `PostTimeout`, `MinValue`, `MaxValue`, `IsUndo`, `IsOn`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldRate`, `TriggerFieldRateCount`, `ClassName`, `ClassFile`) VALUES 
+('bx_courses', 'bx_courses', 'bx_courses_votes', 'bx_courses_votes_track', '604800', '1', '1', '0', '1', 'bx_courses_data', 'id', 'author', 'rate', 'votes', '', '');
+
+-- SCORES
+INSERT INTO `sys_objects_score` (`name`, `module`, `table_main`, `table_track`, `post_timeout`, `is_on`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_score`, `trigger_field_cup`, `trigger_field_cdown`, `class_name`, `class_file`) VALUES 
+('bx_courses', 'bx_courses', 'bx_courses_scores', 'bx_courses_scores_track', '604800', '0', 'bx_courses_data', 'id', 'author', 'score', 'sc_up', 'sc_down', '', '');
+
+-- REPORTS
+INSERT INTO `sys_objects_report` (`name`, `module`, `table_main`, `table_track`, `is_on`, `base_url`, `object_comment`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_courses', 'bx_courses', 'bx_courses_reports', 'bx_courses_reports_track', '1', 'page.php?i=view-course-profile&id={object_id}', 'bx_courses_notes', 'bx_courses_data', 'id', 'author', 'reports', '', '');
+
+-- FAFORITES
+INSERT INTO `sys_objects_favorite` (`name`, `table_track`, `table_lists`, `is_on`, `is_undo`, `is_public`, `base_url`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_count`, `class_name`, `class_file`) VALUES 
+('bx_courses', 'bx_courses_favorites_track', 'bx_courses_favorites_lists', '1', '1', '1', 'page.php?i=view-course-profile&id={object_id}', 'bx_courses_data', 'id', 'author', 'favorites', '', '');
+
+-- FEATURED
+INSERT INTO `sys_objects_feature` (`name`, `module`, `is_on`, `is_undo`, `base_url`, `trigger_table`, `trigger_field_id`, `trigger_field_author`, `trigger_field_flag`, `class_name`, `class_file`) VALUES 
+('bx_courses', 'bx_courses', '1', '1', 'page.php?i=view-course-profile&id={object_id}', 'bx_courses_data', 'id', 'author', 'featured', '', '');
+
+-- CONTENT INFO
+INSERT INTO `sys_objects_content_info` (`name`, `title`, `alert_unit`, `alert_action_add`, `alert_action_update`, `alert_action_delete`, `class_name`, `class_file`) VALUES
+('bx_courses', '_bx_courses', 'bx_courses', 'added', 'edited', 'deleted', '', ''),
+('bx_courses_cmts', '_bx_courses_cmts', 'bx_courses', 'commentPost', 'commentUpdated', 'commentRemoved', 'BxDolContentInfoCmts', '');
+
+INSERT INTO `sys_content_info_grids` (`object`, `grid_object`, `grid_field_id`, `condition`, `selection`) VALUES
+('bx_courses', 'bx_courses_administration', 'td`.`id', '', ''),
+('bx_courses', 'bx_courses_common', 'td`.`id', '', '');
+
+-- SEARCH EXTENDED
+INSERT INTO `sys_objects_search_extended` (`object`, `object_content_info`, `module`, `title`, `active`, `class_name`, `class_file`) VALUES
+('bx_courses', 'bx_courses', 'bx_courses', '_bx_courses_search_extended', 1, '', ''),
+('bx_courses_cmts', 'bx_courses_cmts', 'bx_courses', '_bx_courses_search_extended_cmts', 1, 'BxTemplSearchExtendedCmts', '');
+
+-- STUDIO PAGE & WIDGET
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, 'bx_courses', '_bx_courses', '_bx_courses', 'bx_courses@modules/boonex/courses/|std-icon.svg');
+SET @iPageId = LAST_INSERT_ID();
+
+SET @iParentPageId = (SELECT `id` FROM `sys_std_pages` WHERE `name` = 'home');
+SET @iParentPageOrder = (SELECT MAX(`order`) FROM `sys_std_pages_widgets` WHERE `page_id` = @iParentPageId);
+INSERT INTO `sys_std_widgets` (`page_id`, `module`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, 'bx_courses', 'content', '{url_studio}module.php?name=bx_courses', '', 'bx_courses@modules/boonex/courses/|std-icon.svg', '_bx_courses', '', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_actions";s:6:"params";a:0:{}s:5:"class";s:18:"TemplStudioModules";}');
+INSERT INTO `sys_std_pages_widgets` (`page_id`, `widget_id`, `order`) VALUES
+(@iParentPageId, LAST_INSERT_ID(), IF(ISNULL(@iParentPageOrder), 1, @iParentPageOrder + 1));
+

+ 64 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/install/sql/uninstall.sql

@@ -0,0 +1,64 @@
+
+-- TABLES
+
+DROP TABLE IF EXISTS `bx_courses_data`, `bx_courses_content_structure`, `bx_courses_content_nodes`, `bx_courses_content_nodes2users`, `bx_courses_content_data`, `bx_courses_content_data2users`, `bx_courses_pics`, `bx_courses_pics_resized`, `bx_courses_cmts`, `bx_courses_cmts_notes`, `bx_courses_views_track`, `bx_courses_meta_keywords`, `bx_courses_meta_locations`, `bx_courses_meta_mentions`, `bx_courses_fans`, `bx_courses_admins`, `bx_courses_votes`, `bx_courses_votes_track`, `bx_courses_reports`, `bx_courses_reports_track`, `bx_courses_favorites_track`, `bx_courses_favorites_lists`, `bx_courses_scores`, `bx_courses_scores_track`, `bx_courses_invites`, `bx_courses_prices`;
+
+-- PROFILES
+
+DELETE FROM sys_profiles WHERE `type` = 'bx_courses';
+
+-- STORAGES & TRANSCODERS
+
+DELETE FROM `sys_objects_storage` WHERE `object` IN('bx_courses_pics', 'bx_courses_pics_resized');
+DELETE FROM `sys_storage_tokens` WHERE `object` IN('bx_courses_pics', 'bx_courses_pics_resized');
+
+DELETE FROM `sys_objects_transcoder` WHERE `object` IN('bx_courses_icon', 'bx_courses_thumb', 'bx_courses_avatar', 'bx_courses_avatar_big', 'bx_courses_picture', 'bx_courses_cover', 'bx_courses_cover_thumb', 'bx_courses_gallery');
+DELETE FROM `sys_transcoder_filters` WHERE `transcoder_object` IN('bx_courses_icon', 'bx_courses_thumb', 'bx_courses_avatar', 'bx_courses_avatar_big', 'bx_courses_picture', 'bx_courses_cover', 'bx_courses_cover_thumb', 'bx_courses_gallery');
+DELETE FROM `sys_transcoder_images_files` WHERE `transcoder_object` IN('bx_courses_icon', 'bx_courses_thumb', 'bx_courses_avatar', 'bx_courses_avatar_big', 'bx_courses_picture', 'bx_courses_cover', 'bx_courses_cover_thumb', 'bx_courses_gallery');
+
+-- FORMS
+
+DELETE FROM `sys_objects_form` WHERE `module` = 'bx_courses';
+DELETE FROM `sys_form_displays` WHERE `module` = 'bx_courses';
+DELETE FROM `sys_form_inputs` WHERE `module` = 'bx_courses';
+DELETE FROM `sys_form_display_inputs` WHERE `display_name` IN('bx_course_add', 'bx_course_delete', 'bx_course_edit', 'bx_course_edit_cover', 'bx_course_view', 'bx_course_view_full', 'bx_course_invite', 'bx_courses_price_add', 'bx_courses_price_edit', 'bx_courses_content_node_add', 'bx_courses_content_node_edit');
+
+-- PRE-VALUES
+
+DELETE FROM `sys_form_pre_lists` WHERE `module` = 'bx_courses';
+
+DELETE FROM `sys_form_pre_values` WHERE `Key` LIKE 'bx_courses%';
+
+-- COMMENTS
+DELETE FROM `sys_objects_cmts` WHERE `Name` = 'bx_courses';
+
+-- VIEWS
+DELETE FROM `sys_objects_view` WHERE `name` = 'bx_courses';
+
+-- VOTES
+DELETE FROM `sys_objects_vote` WHERE `Name` = 'bx_courses';
+
+-- SCORES
+DELETE FROM `sys_objects_score` WHERE `name` = 'bx_courses';
+
+-- REPORTS
+DELETE FROM `sys_objects_report` WHERE `name` = 'bx_courses';
+
+-- FAFORITES
+DELETE FROM `sys_objects_favorite` WHERE `name` = 'bx_courses';
+
+-- FEATURED
+DELETE FROM `sys_objects_feature` WHERE `name` = 'bx_courses';
+
+-- CONTENT INFO
+DELETE FROM `sys_objects_content_info` WHERE `name` IN ('bx_courses', 'bx_courses_cmts');
+
+DELETE FROM `sys_content_info_grids` WHERE `object` IN ('bx_courses');
+
+-- SEARCH EXTENDED
+DELETE FROM `sys_objects_search_extended` WHERE `module` = 'bx_courses';
+
+-- STUDIO PAGE & WIDGET
+DELETE FROM `tp`, `tw`, `twb`, `tpw` 
+USING `sys_std_pages` AS `tp` LEFT JOIN `sys_std_widgets` AS `tw` ON `tp`.`id` = `tw`.`page_id` LEFT JOIN `sys_std_widgets_bookmarks` AS `twb` ON `tw`.`id` = `twb`.`widget_id` LEFT JOIN `sys_std_pages_widgets` AS `tpw` ON `tw`.`id` = `tpw`.`widget_id`
+WHERE  `tp`.`name` = 'bx_courses';

+ 66 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/js/entry.js

@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxCoursesEntry(oOptions) {
+    BxBaseModGroupsEntry.call(this, oOptions);
+
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxCoursesEntry' : oOptions.sObjName;
+}
+
+BxCoursesEntry.prototype = Object.create(BxBaseModGroupsEntry.prototype);
+BxCoursesEntry.prototype.constructor = BxCoursesEntry;
+
+BxCoursesEntry.prototype.passNode = function(oElement, iNodeId) {
+    var $this = this;
+    var oParams = this._getDefaultData();
+    oParams['node_id'] = iNodeId;
+
+    if(oElement)
+        this.loadingInButton(oElement, true);
+
+    jQuery.get (
+        this._sActionsUrl + 'pass_node',
+        oParams,
+        function(oData) {
+            if(oElement)
+                $this.loadingInButton(oElement, false);
+
+            processJsonData(oData);
+        },
+        'json'
+    );
+
+    return false;
+};
+
+BxCoursesEntry.prototype.passData = function(oElement, iDataId) {
+    var $this = this;
+    var oParams = this._getDefaultData();
+    oParams['data_id'] = iDataId;
+
+    if(oElement)
+        this.loadingInButton(oElement, true);
+
+    jQuery.get (
+        this._sActionsUrl + 'pass_data',
+        oParams,
+        function(oData) {
+            if(oElement)
+                $this.loadingInButton(oElement, false);
+
+            processJsonData(oData);
+        },
+        'json'
+    );
+
+    return false;
+};
+
+/** @} */

+ 20 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/js/main.js

@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Courses Courses
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxCoursesMain(oOptions) {
+    BxBaseModGroupsMain.call(this, oOptions);
+    
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxCoursesMain' : oOptions.sObjName;
+}
+
+BxCoursesMain.prototype = Object.create(BxBaseModGroupsMain.prototype);
+BxCoursesMain.prototype.constructor = BxCoursesMain;
+
+/** @} */

+ 18 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/counters.html

@@ -0,0 +1,18 @@
+<div class="bx-courses-counters flex flex-col">
+    <bx_if:show_st>
+        <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>
+            </bx_repeat:counters_st>
+        </div>
+    </bx_if:show_st>
+    <bx_if:show_at>
+        <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>
+            </bx_repeat:counters_at>
+        </div>
+    </bx_if:show_at>
+</div>

+ 24 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml1_node_l1.html

@@ -0,0 +1,24 @@
+<div class="bx-courses-ml__level_max__-node-l__level__ bx-def-box bx-def-box-round-corners bx-def-color-bg-box relative my-2">
+    <div class="bx-courses-ml__level_max__-node-l__level__-gap p-4">
+        <div class="bx-courses-ml__level_max__-node-l__level__-cnt relative flex space-x-4">
+            <div class="bx-courses-ml__level_max__-node-l__level__-progress pr-4 border-r">
+                <div class="bx-courses-percent flex justify-center items-center w-16 h-16 border-2 rounded-full">
+                    <span>__pass_percent__%</span>
+                </div>
+                <div class="bx-courses-percent text-center">__pass_progress__</div>
+            </div>
+            <div class="bx-courses-ml__level_max__-node-l__level__-info flex-1">
+                <div class="bx-courses-index bx-def-font-grayed text-xs"><span class="capitalize"><bx_text:_bx_courses_txt_sample_l3_single /></span> __index__:</div>
+                <div class="bx-courses-title text-xl font-bold">
+                    <a href="__link__">__title__</a>
+                </div>
+                <div class="bx-courses-status flex pt-2"><div class="px-4 bg-gray-300 rounded-full">__pass_status__</div></div>
+            </div>
+            <div class="bx-courses-ml__level_max__-node-l__level__-actions flex items-center">
+                <bx_if:show_pass>
+                    <a class="bx-btn bx-btn-small" href="__pass_href__" onclick="javascript:return __js_object__.passNode(this, __id__);">__pass_title__</a>
+                </bx_if:show_pass>
+            </div>
+        </div>
+    </div>
+</div>

+ 3 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml1_nodes_l1.html

@@ -0,0 +1,3 @@
+<div class="bx-courses-ml__level_max__-nodes-l__level__ flex flex-col">
+    <bx_repeat:nodes>__node__</bx_repeat:nodes>
+</div>

+ 1 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml2_node_l2.html

@@ -0,0 +1 @@
+<bx_include_auto:ml1_node_l1.html />

+ 1 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml2_nodes_l2.html

@@ -0,0 +1 @@
+<bx_include_auto:ml1_nodes_l1.html />

+ 1 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml3_node_l3.html

@@ -0,0 +1 @@
+<bx_include_auto:ml1_node_l1.html />

+ 1 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/ml3_nodes_l3.html

@@ -0,0 +1 @@
+<bx_include_auto:ml1_nodes_l1.html />

+ 18 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/node_data_view.html

@@ -0,0 +1,18 @@
+<div class="bx-courses-node-data-view">
+    <div class="bx-courses-node-data-view-cnt px-2 pb-2">
+        <div class="bx-courses-ndv-view">__view__</div>
+        <div class="bx-courses-ndv-actions flex items-center mt-4">
+            <bx_if:show_back>
+                <div class="bx-courses-ndva-back">
+                    <a class="bx-btn bx-btn-small" href="javascript:void(0)" onclick="javascript:return __js_object__.passData(this, __id_back__);"><bx_text:_bx_courses_txt_back_step /></a>
+                </div>
+            </bx_if:show_back>
+            <div class="bx-courses-ndva-stretcher flex-1"></div>
+            <bx_if:show_next>
+                <div class="bx-courses-ndva-next">
+                    <a class="bx-btn bx-btn-small" href="javascript:void(0)" onclick="javascript:return __js_object__.passData(this, __id_next__);"><bx_text:_bx_courses_txt_next_step /></a>
+                </div>
+            </bx_if:show_next>
+        </div>
+    </div>
+</div>

+ 29 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/node_l1.html

@@ -0,0 +1,29 @@
+<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:__percent__%"></div>
+                    </div>
+                    <!--
+                    <div class="bx-courses-progress-percent text-xs"><bx_text:_bx_courses_txt_passed />: __progress__%</div>
+                    -->
+                </div>
+                <div class="bx-courses-index bx-def-font-grayed pt-2 text-xs"><span class="capitalize"><bx_text:_bx_courses_txt_sample_l1_single /></span> __index__:</div>
+                <div class="bx-courses-title font-bold">__title__</div>
+                <div class="bx-courses-stats flex flex-row flex-wrap items-center -m-px pt-2">
+                    <div class="bx-courses-counters flex flex-wrap flex-1">
+                        <bx_repeat:counters>
+                            <div class="bx-courses-counter m-px px-4 bg-gray-300 rounded-full flex flex-nowrap">
+                                <div class="capitalize mr-1">__cn_title__:</div>
+                                <div class="font-bold">__cn_progress__</div>
+                            </div>
+                        </bx_repeat:counters>
+                    </div>
+                    <div class="bx-courses-status m-px px-4 bg-gray-300 rounded-full whitespace-nowrap">__status__</div>
+                </div>
+            </div>
+        </div>
+    </a>
+</div>

+ 39 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/node_view.html

@@ -0,0 +1,39 @@
+<div class="bx-courses-node-view">
+    <div class="bx-courses-node-view-cnt">
+        <div class="bx-courses-index bx-def-font-grayed text-xs"><span class="capitalize">__sample__</span> __index__:</div>
+        <div class="bx-courses-title text-2xl font-bold">__title__</div>
+        <div class="bx-courses-text bx-def-font-grayed text-xs">__text__</div>
+        <div class="bx-courses-node-items">
+            <bx_repeat:items>
+                <div class="bx-courses-node-item flex items-center space-x-4 py-4 border-t first:border-0">
+                    <div class="bx-courses-ni-image">
+                        <div class="bx-courses-nii w-48 h-28 box-content border rounded-lg overflow-hidden">
+                            <bx_if:show_image><img class="block w-48 h-28 object-fill" src="__image__" /></bx_if:show_image>
+                            <bx_if:show_image_empty>
+                                <div class="flex justify-center items-center w-48 h-28 bg-gray-100 text-2xl uppercase">__type__</div>
+                            </bx_if:show_image_empty>
+                        </div>
+                    </div>
+                    <div class="bx-courses-ni-data flex-1">
+                        <div class="bx-courses-nid-type flex"><div class="px-4 bg-gray-300 rounded-full capitalize">__type__</div></div>
+                        <div class="bx-courses-nid-title text-lg font-bold">
+                            <bx_if:show_link><a href="__link__" target="_blank">__title__</a></bx_if:show_link>
+                            <bx_if:show_text>__title__</bx_if:show_text>
+                        </div>
+                        <bx_if:show_size>
+                            <div class="bx-courses-nid-size">__size__</div>
+                        </bx_if:show_size>
+                    </div>
+                    <div class="bx-courses-ni-action flex items-center">
+                        <bx_if:show_pass>
+                            <a class="bx-btn bx-btn-small" href="__link__" onclick="javascript:return __js_object__.passData(this, __id__);">__title__</a>
+                        </bx_if:show_pass>
+                        <bx_if:show_download>
+                            <a class="bx-btn bx-btn-small" href="__link__"><bx_text:_bx_courses_txt_download /></a>
+                        </bx_if:show_download>
+                    </div>
+                </div>
+            </bx_repeat:items>
+        </div>
+    </div>
+</div>

+ 4 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/nodes_l1.html

@@ -0,0 +1,4 @@
+<div class="bx-courses-nodes-l__level__ flex">
+    <bx_repeat:nodes>__node__</bx_repeat:nodes>
+</div>
+<div class="bx-courses-paginate bx-def-margin-sec-top">__paginate__</div>

+ 19 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/popup_content_node.html

@@ -0,0 +1,19 @@
+<div class="bx-base-group-price-form">
+    <div class="bx-base-group-price-form-cnt">
+        __form__
+        <script language="javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function (formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function (data) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        glGrids.__object__.processJson(data, '__action__');
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 56 - 0
modules/boonex/courses/updates/14.0.2_14.0.3/source/template/unit_personal.html

@@ -0,0 +1,56 @@
+<div class="__class__ p-2 box-border">
+    <div class="bx-base-pofile-unit-cnt bx-def-box bx-def-box-round-corners bx-def-color-bg-box relative h-full overflow-hidden">
+        <div class="absolute z-0 w-full h-40 bg-gradient-to-b from-green-400 to-green-500"></div>
+        <a class="bx-base-pofile-unit-cover relative flex items-center justify-start w-full h-40 bg-no-repeat bg-center bg-cover hover:no-underline" style="background-image:url(__cover_url__); __cover_settings__" href="__content_url__" onclick="__content_click__">
+            <bx_if:show_thumbnail>
+                <div class="bx-def-unit-img bx-def-__size__-size __class_size__ relative ml-5">
+                    <bx_if:show_thumb_image>
+                        <img class="bx-base-pofile-unit-thumb bx-def-__size__ bx-def-__size__-size __class_size__ ring-4 ring-white dark:ring-gray-900 rounded-md box-border" src="__thumb_url__" />
+                    </bx_if:show_thumb_image>
+                    <bx_if:show_thumb_letter>
+                        <p class="bx-base-pofile-unit-thumb bx-def-__size__ bx-def-__size__-size __class_size__ flex items-center justify-center m-0 text-white font-bold ring-4 ring-white dark:ring-gray-900 rounded-md box-border" style="background-color:rgba(__color__)">__letter__</p>
+                    </bx_if:show_thumb_letter>
+                    <bx_if:show_online>
+                        <div class="bx-base-pofile-unit-online"></div>
+                    </bx_if:show_online>
+                </div>
+            </bx_if:show_thumbnail>
+        </a>
+        <div class="bx-base-pofile-unit-info p-4 text-left">
+            <bx_if:show_pass_progress>
+                <div class="bx-courses-progress mb-2">
+                    <div class="bx-courses-progress-bar border rounded-full">
+                        <div class="h-1 bg-gray-300 rounded-full" style="width:__progress__%"></div>
+                    </div>
+                    <div class="bx-courses-progress-percent text-xs"><bx_text:_bx_courses_txt_passed />: __progress__%</div>
+                </div>
+            </bx_if:show_pass_progress>
+            <div class="bx-base-pofile-unit-title">
+                <div class="bx-base-put-title truncate">
+                    <a href="__content_url__" onclick="__content_click__" title="__title_attr__" class="hover:underline text-gray-700 dark:text-gray-200 hover:text-gray-900 dark:hover:text-white text-xl font-semibold">__title__</a>__badges__
+                </div>
+                <div class="bx-base-put-addon text-sm text-gray-500 dark:text-gray-400">__addon__</div>
+            </div>
+            <bx_if:meta>
+                <div class="bx-base-general-unit-meta flex flex-col items-stretch justify-center -mx-1 mt-2">__meta__</div>
+            </bx_if:meta>
+            <bx_if:show_pass_stats>
+                <div class="bx-courses-stats flex flex-row flex-wrap items-center -m-px pt-2">
+                    <div class="bx-courses-counters flex flex-row flex-wrap flex-1">
+                        <bx_repeat:counters>
+                            <div class="bx-courses-counter m-px px-4 bg-gray-300 rounded-full flex flex-nowrap">
+                                <div class="capitalize mr-1">__cn_title__:</div>
+                                <div class="font-bold">__cn_progress__</div>
+                            </div>
+                        </bx_repeat:counters>
+                    </div>
+                    <bx_if:show_pass>
+                        <div class="bx-courses-pass m-px">
+                            <a class="bx-btn bx-btn-small" href="__pass_link__">__pass_title__</a>
+                        </div>
+                    </bx_if:show_pass>
+                </div>
+            </bx_if:show_pass_stats>
+        </div>
+    </div>
+</div>

+ 51 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Credits',
+    'version_from' => '14.0.3',
+    'version_to' => '14.0.4',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/credits/updates/update_14.0.3_14.0.4/',
+    'home_uri' => 'credits_update_1403_1404',
+
+    'module_dir' => 'boonex/credits/',
+    'module_uri' => 'credits',
+
+    'db_prefix' => 'bx_credits_',
+    'class_prefix' => 'BxCredits',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Credits',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxCreditsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 7 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/install/langs/en.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_add name="_bx_credits_form_credit_input_amount_inf_wr"><![CDATA[Minimum amount is {wdw_minimum} credits and remaining balance after withdrawal should be {wdw_remaining} credits.]]></string_add>
+
+	<string_del name="_bx_credits_form_credit_input_amount_inf"><![CDATA[Minimum amount is {wdw_minimum} credits and remaining balance after withdrawal should be {wdw_remaining} credits.]]></string_del>
+	<string_del name="_bx_credits_form_credit_input_message_inf_wc"><![CDATA[Payment confirmation and other notes]]></string_del>
+</resources>

+ 2 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/install/sql/install.sql

@@ -0,0 +1,2 @@
+-- FORMS
+UPDATE `sys_form_inputs` SET `info`='' WHERE `object`='bx_credits_credit' AND `name`='amount';

+ 338 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/source/classes/BxCreditsConfig.php

@@ -0,0 +1,338 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxCreditsConfig extends BxBaseModGeneralConfig
+{
+    protected $_iAuthor;
+    protected $_aCurrency;
+
+    protected $_sCheckoutSessionKey;
+
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->CNF = array (
+
+            // module icon
+            'ICON' => 'copyright col-green3',
+
+            // database tables
+            'TABLE_BUNDLES' => $aModule['db_prefix'] . 'bundles',
+            'TABLE_ORDERS' => $aModule['db_prefix'] . 'orders',
+            'TABLE_ORDERS_DELETED' => $aModule['db_prefix'] . 'orders_deleted',
+            'TABLE_PROFILES' => $aModule['db_prefix'] . 'profiles',
+            'TABLE_HISTORY' => $aModule['db_prefix'] . 'history',
+            'TABLE_WITHDRAWALS' => $aModule['db_prefix'] . 'withdrawals',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_AUTHOR' => '',
+            'FIELD_ADDED' => 'added',
+            'FIELD_CHANGED' => '',
+            'FIELD_NAME' => 'name',
+            'FIELD_TITLE' => 'title',
+            'FIELD_DESCRIPTION' => 'description',
+            'FIELD_AMOUNT' => 'amount',
+            'FIELD_BONUS' => 'bonus',
+            'FIELD_PRICE' => 'price',
+            'FIELD_STATUS' => 'active',
+
+            'FIELD_C_CLEARED' => 'cleared',
+            'FIELD_C_AMOUNT' => 'amount',
+            'FIELD_C_MESSAGE' => 'message',
+
+            'FIELD_H_ID' => 'id',
+            'FIELD_H_DIRECTION' => 'direction',
+            'FIELD_H_DATE' => 'date',
+            'FIELD_H_CLEARED' => 'cleared',
+
+            // page URIs
+            'URI_HOME' => 'credits-home',
+            'URL_HOME' => 'page.php?i=credits-home',
+            'URL_CHECKOUT' => 'page.php?i=credits-checkout',
+            'URL_ORDERS_COMMON' => 'page.php?i=credits-orders',
+            'URL_ORDERS_ADMINISTRATION' => 'page.php?i=credits-orders-administration',
+            'URL_HISTORY_COMMON' => 'page.php?i=credits-history-common',
+            'URL_HISTORY_ADMINISTRATION' => 'page.php?i=credits-history-administration',
+            'URL_WITHDRAWALS_COMMON' => 'page.php?i=credits-withdrawals-common',
+            'URL_WITHDRAWALS_ADMINISTRATION' => 'page.php?i=credits-withdrawals-administration',
+
+            // some params
+            'PARAM_WITHDRAW' => 'bx_credits_enable_withdraw',
+            'PARAM_WITHDRAW_EMAIL' => 'bx_credits_withdraw_email',
+            'PARAM_WITHDRAW_CLEARING' => 'bx_credits_withdraw_clearing',
+            'PARAM_WITHDRAW_MINIMUM' => 'bx_credits_withdraw_minimum',
+            'PARAM_WITHDRAW_REMAINING' => 'bx_credits_withdraw_remaining',
+            'PARAM_PRECISION' => 'bx_credits_precision',
+            'PARAM_CR_USE' => 'bx_credits_conversion_rate_use',
+            'PARAM_CR_WITHDRAW' => 'bx_credits_conversion_rate_withdraw',
+            'PARAM_PROVIDER_NAME' => 'credits',
+            'PARAM_PROVIDER_ENABLE' => 'bx_credits_enable_provider',
+            'PARAM_CODE' => 'bx_credits_code',
+            'PARAM_ICON' => 'bx_credits_icon',
+
+            // objects
+            'OBJECT_FORM_CREDIT' => 'bx_credits_credit',
+            'OBJECT_FORM_CREDIT_DISPLAY_GRANT' => 'bx_credits_credit_grant',
+            'OBJECT_FORM_CREDIT_DISPLAY_SEND' => 'bx_credits_credit_send',
+            'OBJECT_FORM_CREDIT_DISPLAY_WITHDRAW_REQUEST' => 'bx_credits_credit_withdraw_request',
+            'OBJECT_FORM_BUNDLE' => 'bx_credits_bundle',
+            'OBJECT_FORM_BUNDLE_DISPLAY_ADD' => 'bx_credits_bundle_add',
+            'OBJECT_FORM_BUNDLE_DISPLAY_EDIT' => 'bx_credits_bundle_edit',
+            'OBJECT_FORM_PROFILE' => 'bx_credits_profile',
+            'OBJECT_FORM_PROFILE_DISPLAY_EDIT' => 'bx_credits_profile_edit',
+            'OBJECT_MENU_SUBMENU' => 'bx_credits_submenu', 
+            'OBJECT_MENU_MANAGE_SUBMENU' => 'bx_credits_manage_submenu',
+            'OBJECT_GRID_BUNDLES' => 'bx_credits_bundles',
+            'OBJECT_GRID_ORDERS_ADMINISTRATION' => 'bx_credits_orders_administration',
+            'OBJECT_GRID_ORDERS_COMMON' => 'bx_credits_orders_common',
+            'OBJECT_GRID_HISTORY_ADMINISTRATION' => 'bx_credits_history_administration',
+            'OBJECT_GRID_HISTORY_COMMON' => 'bx_credits_history_common',
+            'OBJECT_GRID_WITHDRAWALS_ADMINISTRATION' => 'bx_credits_withdrawals_administration',
+            'OBJECT_GRID_WITHDRAWALS_COMMON' => 'bx_credits_withdrawals_common',
+            'OBJECT_GRID_PROFILES_ADMINISTRATION' => 'bx_credits_profiles_administration',
+
+            // email templates
+            'ETEMPLATE_GRANTED' => 'bx_credits_granted',
+            'ETEMPLATE_RECEIVED' => 'bx_credits_received',
+            'ETEMPLATE_PURCHASED' => 'bx_credits_purchased',
+            'ETEMPLATE_IN' => 'bx_credits_in',
+            'ETEMPLATE_OUT' => 'bx_credits_out',
+            'ETEMPLATE_WITHDRAW_REQUESTED' => 'bx_credits_withdraw_requested',
+            'ETEMPLATE_WITHDRAW_CANCELED' => 'bx_credits_withdraw_canceled',
+            'ETEMPLATE_WITHDRAW_SENT' => 'bx_credits_withdraw_sent',
+
+            // some language keys
+            'T' => array (
+                'txt_sample_single' => '_bx_credits_txt_sample_single',
+                'grant_popup' => '_bx_credits_grid_popup_title_htr_grant',
+                'send_popup' => '_bx_credits_grid_popup_title_htr_send',
+                'withdraw_request_popup' => '_bx_credits_grid_popup_title_wdw_withdraw_request',
+            ),
+        );
+
+        $this->_aJsClasses = array_merge($this->_aJsClasses, array(
+            'studio' => 'BxCreditsStudio',
+            'checkout' => 'BxCreditsCheckout',
+            'subscribe' => 'BxCreditsSubscribe',
+            'withdraw' => 'BxCreditsWithdraw',
+        ));
+
+        $this->_aJsObjects = array_merge($this->_aJsObjects, array(
+            'studio' => 'oBxCreditsStudio',
+            'checkout' => 'oBxCreditsCheckout',
+            'subscribe' => 'oBxCreditsSubscribe',
+            'withdraw' => 'oBxCreditsWithdraw',
+        ));
+
+        $sPrefix = str_replace('_', '-', $this->_sName);
+        $this->_aHtmlIds = array(
+            'add_bundle_popup' =>  $sPrefix . '-add-bundle-popup',
+            'edit_bundle_popup' =>  $sPrefix . '-edit-bundle-popup',
+
+            'send_popup' =>  $sPrefix . '-send-popup',
+            'grant_popup' =>  $sPrefix . '-grant-popup',
+            'withdraw_request_popup' =>  $sPrefix . '-withdraw-request-popup',
+            'withdraw_confirm_popup' =>  $sPrefix . '-withdraw-confirm-popup',
+            'withdraw_field_rate' => $sPrefix . '-wff-rate',
+            'withdraw_field_amount' => $sPrefix . '-wff-amount',
+            'withdraw_field_result' => $sPrefix . '-wff-result',
+
+            'edit_profile_popup' =>  $sPrefix . '-edit-profile-popup',
+
+            'subscribe_popup' =>  $sPrefix . '-subscribe-popup',
+        );
+
+        $this->_aPrefixes = array(
+            'style' => 'bx-credits',
+        );
+
+        $oPayments = BxDolPayments::getInstance();
+        $this->_iAuthor = (int)$oPayments->getOption('site_admin');
+        $this->_aCurrency = array(
+            'code' => $oPayments->getOption('default_currency_code'),
+            'sign' => $oPayments->getOption('default_currency_sign')
+        );
+        
+        $this->_sCheckoutSessionKey = $this->_sName . '_checkout';
+    }
+
+    public function getHtmlIds($sKey = '')
+    {
+        if(empty($sKey))
+            return $this->_aHtmlIds;
+
+        return isset($this->_aHtmlIds[$sKey]) ? $this->_aHtmlIds[$sKey] : '';
+    }
+
+    public function getAuthor()
+    {
+    	return $this->_iAuthor;
+    }
+
+    public function getCurrency()
+    {
+    	return $this->_aCurrency;
+    }
+
+    public function isWithdraw()
+    {
+        return getParam($this->CNF['PARAM_WITHDRAW']) == 'on';
+    }
+
+    public function getWithdrawEmail()
+    {
+        $sEmail = getParam($this->CNF['PARAM_WITHDRAW_EMAIL']);
+        if(empty($sEmail))
+            $sEmail = getParam('site_email');
+
+        return $sEmail;
+    }
+
+    public function getWithdrawClearing()
+    {
+        return (int)getParam($this->CNF['PARAM_WITHDRAW_CLEARING']);
+    }
+
+    public function getWithdrawMinimum()
+    {
+        return (int)getParam($this->CNF['PARAM_WITHDRAW_MINIMUM']);
+    }
+
+    public function getWithdrawRemaining()
+    {
+        return (int)getParam($this->CNF['PARAM_WITHDRAW_REMAINING']);
+    }
+
+    public function getTransferTypesForClearing()
+    {
+        return [
+            BX_CREDITS_TRANSFER_TYPE_PURCHASE,
+            BX_CREDITS_TRANSFER_TYPE_CHECKOUT,
+            BX_CREDITS_TRANSFER_TYPE_GRANT,
+            BX_CREDITS_TRANSFER_TYPE_SEND,
+            BX_CREDITS_TRANSFER_TYPE_SERVICE
+        ];
+    }
+
+    public function getTransferTypesForSpending()
+    {
+        return [
+            BX_CREDITS_TRANSFER_TYPE_CHECKOUT,
+            BX_CREDITS_TRANSFER_TYPE_SEND,
+            BX_CREDITS_TRANSFER_TYPE_WITHDRAW,
+            BX_CREDITS_TRANSFER_TYPE_SERVICE
+        ];
+    }
+
+    public function getPrecision()
+    {
+        return (int)getParam($this->CNF['PARAM_PRECISION']);
+    }
+
+    public function getConversionRateUse()
+    {
+        return $this->_getConversionRate('use');
+    }
+
+    public function getConversionRateWithdraw()
+    {
+        return $this->_getConversionRate('withdraw');
+    }
+
+    public function getCheckoutUrl()
+    {
+        return bx_absolute_url(BxDolPermalinks::getInstance()->permalink($this->CNF['URL_CHECKOUT']));
+    }
+
+    public function getBundleUrl($aBundle)
+    {
+        return bx_absolute_url(BxDolPermalinks::getInstance()->permalink($this->CNF['URL_HOME']));
+    }
+
+    public function getBundleName($sName)
+    {
+        return uriGenerate($sName, $this->CNF['TABLE_BUNDLES'], $this->CNF['FIELD_NAME'], ['empty' => 'cat', 'divider' => '_', 'lowercase' => false]);
+    }
+
+    public function getBundleDescription($aBundle)
+    {
+        if(empty($aBundle[$this->CNF['FIELD_BONUS']]))
+            return $aBundle[$this->CNF['FIELD_AMOUNT']];
+
+        return _t('_bx_credits_txt_n_plus_m_for_free', $aBundle[$this->CNF['FIELD_AMOUNT']], $aBundle[$this->CNF['FIELD_BONUS']]);
+    }
+
+    public function getCheckoutData()
+    {
+        return BxDolSession::getInstance()->getUnsetValue($this->_sCheckoutSessionKey);
+    }
+
+    public function setCheckoutData($aData)
+    {
+        return BxDolSession::getInstance()->setValue($this->_sCheckoutSessionKey, $aData);
+    }
+
+    public function getOrder($iLength = 16)
+    {
+        return strtoupper(genRndPwd($iLength, false));
+    }
+
+    public function constructCheckoutCustomData()
+    {
+        $aParams = func_get_args();
+        return urlencode(base64_encode(implode('|', $aParams)));
+    }
+
+    public function deconstructCheckoutCustomData($sData)
+    {
+        return explode('|', base64_decode(urldecode($sData)));
+    }
+
+    /**
+     * Convert Credits to Money
+     */
+    public function convertC2M($fCredits, $fRate = false, $iPrecision = false)
+    {
+        if($fRate === false)
+            $fRate = $this->getConversionRateUse();
+        if($iPrecision === false)
+            $iPrecision = $this->getPrecision();
+
+        return round((float)$fCredits * $fRate, $iPrecision);
+    }
+
+    /**
+     * Convert Money to Credits
+     */
+    public function convertM2C($fMoney, $fRate = false, $iPrecision = false)
+    {
+        if($fRate === false)
+            $fRate = $this->getConversionRateUse();
+        if($iPrecision === false)
+            $iPrecision = $this->getPrecision();
+
+        return round((float)$fMoney / $fRate, $iPrecision);
+    }
+
+    /*
+     * Internal methods
+     */
+    protected function _getConversionRate($sType)
+    {
+        $fRate = (float)getParam($this->CNF['PARAM_CR_' . strtoupper($sType)]);
+
+        return $fRate > 0 && $fRate <= 1 ? $fRate : 1;
+    }
+}
+
+/** @} */

+ 157 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/source/classes/BxCreditsFormCredit.php

@@ -0,0 +1,157 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Credit form
+ */
+class BxCreditsFormCredit extends BxTemplFormView
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    protected $_fRate;
+    protected $_iUserId;
+
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        $this->_sModule = 'bx_credits';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
+        parent::__construct($aInfo, $oTemplate);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->_fRate = $this->_oModule->_oConfig->getConversionRateWithdraw();
+
+        if(($iUserId = bx_get('user_id')) !== false)
+                $this->_iUserId = (int)$iUserId;
+
+        if(isset($this->aInputs[$CNF['FIELD_C_AMOUNT']])) {
+            $sInfo = '_bx_credits_form_credit_input_amount_inf_';
+            switch($this->aParams['display']) {
+                case $CNF['OBJECT_FORM_CREDIT_DISPLAY_WITHDRAW_REQUEST']:
+                    $sInfo .= 'wr';
+                    break;
+                
+                default:
+                    $sInfo = '';
+            }
+
+            if($sInfo)
+                $this->aInputs[$CNF['FIELD_C_AMOUNT']]['info'] = _t($sInfo);
+        }
+
+        if(isset($this->aInputs[$CNF['FIELD_C_MESSAGE']])) {
+            $sInfo = '_bx_credits_form_credit_input_message_inf_';
+            switch($this->aParams['display']) {
+                case $CNF['OBJECT_FORM_CREDIT_DISPLAY_GRANT']:
+                    $sInfo .= 'g';
+                    break;
+
+                case $CNF['OBJECT_FORM_CREDIT_DISPLAY_SEND']:
+                    $sInfo .= 's';
+                    break;
+
+                case $CNF['OBJECT_FORM_CREDIT_DISPLAY_WITHDRAW_REQUEST']:
+                    $sInfo .= 'wr';
+                    break;
+            }
+
+            $this->aInputs[$CNF['FIELD_C_MESSAGE']]['info'] = _t($sInfo);
+        }
+    }
+
+    public function initChecker($aValues = [], $aSpecificValues = [])
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $aLimits = $this->_oModule->getProfileLimits($this->_iUserId);
+
+        if(isset($this->aInputs[$CNF['FIELD_C_CLEARED']])) {
+            $this->aInputs[$CNF['FIELD_C_CLEARED']]['info'] = bx_replace_markers($this->aInputs[$CNF['FIELD_C_CLEARED']]['info'], $aLimits);
+        }
+
+        if(isset($this->aInputs[$CNF['FIELD_C_AMOUNT']]))
+            $this->aInputs[$CNF['FIELD_C_AMOUNT']]['info'] = bx_replace_markers($this->aInputs[$CNF['FIELD_C_AMOUNT']]['info'], $aLimits);
+
+        parent::initChecker($aValues, $aSpecificValues);
+    }
+
+    public function setUserId($iUserId)
+    {
+        $this->_iUserId = $iUserId;
+    }
+
+    protected function genCustomRowRate(&$aInput)
+    {
+        if($this->_fRate == 1)
+            return '';
+
+        return $this->genRowStandard($aInput);
+    }
+
+    protected function genCustomRowResult(&$aInput)
+    {
+        if($this->_fRate == 1)
+            return '';
+
+        return $this->genRowStandard($aInput);
+    }
+
+    protected function genCustomInputBalance(&$aInput)
+    {
+        $aInput['value'] = $this->_oModule->getProfileBalance($this->_iUserId);
+
+        return $this->genInputStandard($aInput);
+    }
+
+    protected function genCustomInputCleared(&$aInput)
+    {
+        $aInput['value'] = $this->_oModule->getProfileBalanceCleared($this->_iUserId);
+
+        return $this->genInputStandard($aInput);
+    }
+
+    protected function genCustomInputRate(&$aInput)
+    {
+        $aInput['value'] = $this->_fRate;
+        $aInput['attrs']['id'] = $this->_oModule->_oConfig->getHtmlIds('withdraw_field_rate');
+        return $this->genInputStandard($aInput);
+    }
+
+    protected function genCustomInputAmount(&$aInput)
+    {
+        if($this->_fRate != 1)
+            $aInput['attrs']['onblur'] = $this->_oModule->_oConfig->getJsObject('withdraw') . '.getResult(this)';
+
+        $aInput['attrs']['id'] = $this->_oModule->_oConfig->getHtmlIds('withdraw_field_amount');
+        return $this->genInputStandard($aInput);
+    }
+
+    protected function genCustomInputResult(&$aInput)
+    {
+        $aInput['attrs']['id'] = $this->_oModule->_oConfig->getHtmlIds('withdraw_field_result');
+        return $this->genInputStandard($aInput);
+    }
+
+    protected function genCustomInputProfile(&$aInput)
+    {
+        if(empty($aInput['custom']) || !is_array($aInput['custom']))
+            $aInput['custom'] = array();
+        $aInput['custom']['only_once'] = 1;
+
+        $aInput['ajax_get_suggestions'] = BX_DOL_URL_ROOT . $this->_oModule->_oConfig->getBaseUri() . "get_profiles";
+
+        return $this->genCustomInputUsernamesSuggestions($aInput);
+    }
+}
+
+/** @} */

+ 105 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/source/classes/BxCreditsGridHistoryAdministration.php

@@ -0,0 +1,105 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+require_once('BxCreditsGrid.php');
+
+class BxCreditsGridHistoryAdministration extends BxCreditsGrid
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sDefaultSortingOrder = 'DESC';
+    }
+
+    public function performActionGrant()
+    {
+        $sAction = 'grant';
+
+        $this->_performActionWithProfileAmount($sAction);
+    }
+
+    protected function _getCellDirection($mixedValue, $sKey, $aField, $aRow)
+    {
+        if(in_array($mixedValue, [BX_CREDITS_DIRECTION_IN, BX_CREDITS_DIRECTION_OUT]))
+            $mixedValue = _t('_bx_credits_txt_direction_' . $mixedValue);
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellFirstPid($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_getProfile($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellSecondPid($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_getProfile($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellAmount($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_oModule->convertC2S($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellOrder($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellDate($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(bx_time_js($mixedValue, BX_FORMAT_DATE, true), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellCleared($mixedValue, $sKey, $aField, $aRow)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if($aRow[$CNF['FIELD_H_DIRECTION']] == BX_CREDITS_DIRECTION_IN) {
+            if(empty($mixedValue)) {
+                $iPeriod = (int)$aRow['wdw_clearing'];
+                if($iPeriod == 0) {
+                    $iPeriod = $this->_oModule->_oConfig->getWithdrawClearing();
+                    if($iPeriod == 0)
+                        $iPeriod = 1;
+                }
+
+                $oDate = date_create('@' . $aRow[$CNF['FIELD_H_DATE']]);
+                date_add($oDate, new DateInterval('P' . $iPeriod . 'D'));
+                $mixedValue = date_format($oDate, 'U');
+            }
+
+            $mixedValue = bx_time_js($mixedValue, BX_FORMAT_DATE, true);
+        }
+        else 
+            $mixedValue = '';
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oModule->_oTemplate->addJs([
+            'jquery.form.min.js'
+        ]);
+
+        $this->_oModule->_oTemplate->addCss([
+            'main.css'
+        ]);
+
+        $oForm = new BxTemplFormView([]);
+        $oForm->addCssJs();
+    }
+}
+
+/** @} */

+ 82 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/source/classes/BxCreditsGridWithdrawalsCommon.php

@@ -0,0 +1,82 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+require_once('BxCreditsGridWithdrawalsAdministration.php');
+
+class BxCreditsGridWithdrawalsCommon extends BxCreditsGridWithdrawalsAdministration
+{
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+    }
+
+    public function performActionWithdrawRequest()
+    {
+        if(!$this->_bWithdraw)
+            return echoJson([]);
+
+        $sAction = 'withdraw_request';
+
+        $this->_performActionWithProfileAmount($sAction);
+    }
+
+    public function performActionWithdrawCancel()
+    {
+        if(!$this->_bWithdraw)
+            return echoJson([]);
+
+        $sAction = 'withdraw_cancel';
+
+        $iId = $this->_getId();
+        if(!$iId)
+            return echoJson([]);
+
+        $aResult = $this->_oModule->processWithdrawCancel($iId);
+        return echoJson($this->_onPerformAction($aResult));
+    }
+
+    public function getCode($isDisplayHeader = true)
+    {
+        $sResult = parent::getCode($isDisplayHeader);
+        if(!empty($sResult) && $isDisplayHeader)
+            $sResult = $this->_oModule->_oTemplate->getJsCode('withdraw') . $sResult;
+
+        return $sResult;
+    }
+
+    protected function _getActionWithdrawRequest($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(!$this->_bWithdraw)
+            return $this->_bIsApi ? [] : '';
+
+        return parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getActionWithdrawCancel($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(!$this->_bWithdraw || $aRow['status'] != BX_CREDITS_WITHDRAWAL_STATUS_REQUESTED)
+            return $this->_bIsApi ? [] : '';
+
+        return parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        if(empty($this->_iUserId) || $this->_iUserId != bx_get_logged_profile_id())
+            return [];
+
+        $this->_aOptions['source'] .= $this->_oModule->_oDb->prepareAsString(" AND `profile_id`=?", $this->_iUserId);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+}
+
+/** @} */

+ 77 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/source/install/config.php

@@ -0,0 +1,77 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Credits Credits
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_credits',
+    'title' => 'Credits',
+    'note' => 'Credits module.',
+    'version' => '14.0.4',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/credits/',
+    'home_uri' => 'credits',
+
+    'db_prefix' => 'bx_credits_',
+    'class_prefix' => 'BxCredits',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Credits',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Relations Section
+     */
+    'relations' => array(),
+
+);
+
+/** @} */

+ 343 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/source/install/langs/en.xml

@@ -0,0 +1,343 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string name="_bx_credits"><![CDATA[Credits]]></string>
+
+
+    <!-- Options -->
+    <string name="_bx_credits_option_enable_withdraw"><![CDATA[Allow members to withdraw credits converting them to money]]></string>
+    <string name="_bx_credits_option_withdraw_email"><![CDATA[Email for withdraw requests]]></string>
+    <string name="_bx_credits_option_withdraw_clearing"><![CDATA[Clearing period (in days) before received credits can be withdrawn]]></string>
+    <string name="_bx_credits_option_withdraw_minimum"><![CDATA[Minimum amount of cleared credits which can be withdrawn]]></string>
+    <string name="_bx_credits_option_withdraw_remaining"><![CDATA[Minimum credits balance after withdrawal]]></string>
+    <string name="_bx_credits_option_precision"><![CDATA[Precision (number of digits after the decimal point)]]></string>
+    <string name="_bx_credits_option_conversion_rate_use"><![CDATA[Conversion rate to pay with credits (for site currency)]]></string>
+    <string name="_bx_credits_option_conversion_rate_withdraw"><![CDATA[Conversion rate to withdraw credits (for site currency)]]></string>
+    <string name="_bx_credits_option_code"><![CDATA[Code to prefix amount]]></string>
+    <string name="_bx_credits_option_icon"><![CDATA[Icon to prefix amount]]></string>
+    <string name="_bx_credits_enable_provider"><![CDATA[Auto activate 'Credits' provider for all newly created profiles]]></string>
+
+
+    <!-- Forms -->
+    <string name="_bx_credits_form_bundle"><![CDATA[Bundle]]></string>
+    <string name="_bx_credits_form_bundle_display_add"><![CDATA[Add Bundle]]></string>
+    <string name="_bx_credits_form_bundle_display_edit"><![CDATA[Edit Bundle]]></string>
+    <string name="_bx_credits_form_bundle_input_sys_title"><![CDATA[Title]]></string>
+    <string name="_bx_credits_form_bundle_input_title"><![CDATA[Title]]></string>
+    <string name="_bx_credits_form_bundle_input_title_err"><![CDATA[This field is essential. Please fill it in for all languages.]]></string>
+    <string name="_bx_credits_form_bundle_input_sys_description"><![CDATA[Description]]></string>
+    <string name="_bx_credits_form_bundle_input_description"><![CDATA[Description]]></string>
+    <string name="_bx_credits_form_bundle_input_sys_name"><![CDATA[Name]]></string>
+    <string name="_bx_credits_form_bundle_input_name"><![CDATA[Name]]></string>
+    <string name="_bx_credits_form_bundle_input_name_err"><![CDATA[This field is essential for API calls. Please fill it in.]]></string>
+    <string name="_bx_credits_form_bundle_input_sys_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_credits_form_bundle_input_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_credits_form_bundle_input_amount_err"><![CDATA[This field is essential. Please fill it in.]]></string>
+    <string name="_bx_credits_form_bundle_input_sys_bonus"><![CDATA[Bonus]]></string>
+    <string name="_bx_credits_form_bundle_input_bonus"><![CDATA[Bonus]]></string>
+    <string name="_bx_credits_form_bundle_input_sys_price"><![CDATA[Price]]></string>
+    <string name="_bx_credits_form_bundle_input_price"><![CDATA[Price]]></string>
+    <string name="_bx_credits_form_bundle_input_price_err"><![CDATA[This field is essential. Please fill it in.]]></string>
+    <string name="_bx_credits_form_bundle_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_credits_form_bundle_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_credits_form_bundle_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_credits_form_bundle_input_do_cancel"><![CDATA[Cancel]]></string>
+
+    <string name="_bx_credits_form_credit"><![CDATA[Credit]]></string>
+    <string name="_bx_credits_form_credit_display_grant"><![CDATA[Grant]]></string>
+    <string name="_bx_credits_form_credit_display_send"><![CDATA[Send]]></string>
+    <string name="_bx_credits_form_credit_display_withdraw_request"><![CDATA[Request Withdrawal]]></string>
+    <string name="_bx_credits_form_credit_input_sys_balance"><![CDATA[Balance]]></string>
+    <string name="_bx_credits_form_credit_input_balance"><![CDATA[Balance]]></string>
+    <string name="_bx_credits_form_credit_input_sys_cleared"><![CDATA[Cleared Balance]]></string>
+    <string name="_bx_credits_form_credit_input_cleared"><![CDATA[Cleared Balance]]></string>
+    <string name="_bx_credits_form_credit_input_cleared_inf"><![CDATA[Clearing period is {wdw_clearing} days before received credits can be withdrawn.]]></string>
+    <string name="_bx_credits_form_credit_input_sys_rate"><![CDATA[Conversion rate]]></string>
+    <string name="_bx_credits_form_credit_input_rate"><![CDATA[Conversion rate]]></string>
+    <string name="_bx_credits_form_credit_input_sys_result"><![CDATA[Result]]></string>
+    <string name="_bx_credits_form_credit_input_result"><![CDATA[Result]]></string>
+    <string name="_bx_credits_form_credit_input_sys_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_credits_form_credit_input_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_credits_form_credit_input_amount_inf_wr"><![CDATA[Minimum amount is {wdw_minimum} credits and remaining balance after withdrawal should be {wdw_remaining} credits.]]></string>
+    <string name="_bx_credits_form_credit_input_amount_err"><![CDATA[This field is essential. Please fill it in.]]></string>
+    <string name="_bx_credits_form_credit_input_sys_message"><![CDATA[Message]]></string>
+    <string name="_bx_credits_form_credit_input_message"><![CDATA[Message]]></string>
+    <string name="_bx_credits_form_credit_input_message_inf_g"><![CDATA[]]></string>
+    <string name="_bx_credits_form_credit_input_message_inf_s"><![CDATA[Indicate the purpose of sending to reflect it in history for future references]]></string>
+    <string name="_bx_credits_form_credit_input_message_inf_wr"><![CDATA[Payment instructions and other notes]]></string>
+    <string name="_bx_credits_form_credit_input_profile"><![CDATA[Profile]]></string>
+    <string name="_bx_credits_form_credit_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_credits_form_credit_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_credits_form_credit_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_credits_form_credit_input_do_cancel"><![CDATA[Cancel]]></string>
+
+    <string name="_bx_credits_form_profile"><![CDATA[Profile]]></string>
+    <string name="_bx_credits_form_profile_display_edit"><![CDATA[Edit Profile]]></string>
+    <string name="_bx_credits_form_profile_input_sys_wdw_clearing"><![CDATA[Clearing period]]></string>
+    <string name="_bx_credits_form_profile_input_wdw_clearing"><![CDATA[Clearing period (in days)]]></string>
+    <string name="_bx_credits_form_profile_input_wdw_clearing_inf"><![CDATA[Leave empty (0) to use global value]]></string>
+    <string name="_bx_credits_form_profile_input_sys_wdw_minimum"><![CDATA[Minimum withdrawal amount]]></string>
+    <string name="_bx_credits_form_profile_input_wdw_minimum"><![CDATA[Minimum withdrawal amount]]></string>
+    <string name="_bx_credits_form_profile_input_wdw_minimum_inf"><![CDATA[Leave empty (0) to use global value]]></string>
+    <string name="_bx_credits_form_profile_input_sys_wdw_remaining"><![CDATA[Minimum remaining amount]]></string>
+    <string name="_bx_credits_form_profile_input_wdw_remaining"><![CDATA[Minimum remaining amount]]></string>
+    <string name="_bx_credits_form_profile_input_wdw_remaining_inf"><![CDATA[Leave empty (0) to use global value]]></string>
+    <string name="_bx_credits_form_profile_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_credits_form_profile_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_credits_form_profile_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_credits_form_profile_input_do_cancel"><![CDATA[Cancel]]></string>
+
+
+    <!-- Pages -->
+    <string name="_bx_credits_page_title_sys_home"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_page_title_home"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_page_block_title_sys_bundles"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_page_block_title_bundles"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_page_title_sys_checkout"><![CDATA[Checkout]]></string>
+    <string name="_bx_credits_page_title_checkout"><![CDATA[Checkout]]></string>
+    <string name="_bx_credits_page_block_title_sys_checkout"><![CDATA[Checkout]]></string>
+    <string name="_bx_credits_page_block_title_checkout"><![CDATA[Checkout]]></string>
+    <string name="_bx_credits_page_title_sys_history_common"><![CDATA[My History]]></string>
+    <string name="_bx_credits_page_title_history_common"><![CDATA[History]]></string>
+    <string name="_bx_credits_page_block_title_sys_history_common"><![CDATA[My History]]></string>
+    <string name="_bx_credits_page_block_title_history_common"><![CDATA[History]]></string>    
+    <string name="_bx_credits_page_title_sys_history_administration"><![CDATA[All History]]></string>
+    <string name="_bx_credits_page_title_history_administration"><![CDATA[History]]></string>
+    <string name="_bx_credits_page_block_title_sys_history_administration"><![CDATA[All History]]></string>
+    <string name="_bx_credits_page_block_title_history_administration"><![CDATA[History]]></string>
+    <string name="_bx_credits_page_title_sys_orders_common"><![CDATA[My Orders]]></string>
+    <string name="_bx_credits_page_title_orders_common"><![CDATA[Orders]]></string>
+    <string name="_bx_credits_page_block_title_sys_orders_common_note"><![CDATA[Note]]></string>
+    <string name="_bx_credits_page_block_title_orders_common_note"><![CDATA[Note]]></string>
+    <string name="_bx_credits_page_block_content_orders_common_note"><![CDATA[The orders are for reference only.]]></string>
+    <string name="_bx_credits_page_block_title_sys_orders_common"><![CDATA[My Orders]]></string>
+    <string name="_bx_credits_page_block_title_orders_common"><![CDATA[Orders]]></string>
+    <string name="_bx_credits_page_title_sys_orders_administration"><![CDATA[All Orders]]></string>
+    <string name="_bx_credits_page_title_orders_administration"><![CDATA[Orders]]></string>
+    <string name="_bx_credits_page_block_title_sys_orders_administration"><![CDATA[All Orders]]></string>
+    <string name="_bx_credits_page_block_title_orders_administration"><![CDATA[Orders]]></string>
+    <string name="_bx_credits_page_title_sys_withdrawals_common"><![CDATA[My Withdrawals]]></string>
+    <string name="_bx_credits_page_title_withdrawals_common"><![CDATA[Withdrawals]]></string>
+    <string name="_bx_credits_page_block_title_sys_withdrawals_common"><![CDATA[My Withdrawals]]></string>
+    <string name="_bx_credits_page_block_title_withdrawals_common"><![CDATA[Withdrawals]]></string>    
+    <string name="_bx_credits_page_title_sys_withdrawals_administration"><![CDATA[All Withdrawals]]></string>
+    <string name="_bx_credits_page_title_withdrawals_administration"><![CDATA[Withdrawals]]></string>
+    <string name="_bx_credits_page_block_title_sys_withdrawals_administration"><![CDATA[All Withdrawals]]></string>
+    <string name="_bx_credits_page_block_title_withdrawals_administration"><![CDATA[Withdrawals]]></string>
+    <string name="_bx_credits_page_title_sys_profiles_administration"><![CDATA[All Profiles]]></string>
+    <string name="_bx_credits_page_title_profiles_administration"><![CDATA[Profiles]]></string>
+    <string name="_bx_credits_page_block_title_sys_profiles_administration"><![CDATA[All Profiles]]></string>
+    <string name="_bx_credits_page_block_title_profiles_administration"><![CDATA[Profiles]]></string>
+
+
+    <!-- Menus -->
+    <string name="_bx_credits_menu_item_title_system_credits"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_menu_item_title_credits"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_menu_item_title_system_history"><![CDATA[Balance]]></string>
+    <string name="_bx_credits_menu_item_title_history"><![CDATA[{unit}{value}]]></string>
+    <string name="_bx_credits_menu_item_title_system_notifications"><![CDATA[Balance]]></string>
+    <string name="_bx_credits_menu_item_title_notifications"><![CDATA[{value}]]></string>
+    <string name="_bx_credits_menu_title_submenu"><![CDATA[Credits Submenu]]></string>
+    <string name="_bx_credits_menu_set_title_submenu"><![CDATA[Credits Submenu]]></string>
+    <string name="_bx_credits_menu_item_title_system_home"><![CDATA[Home]]></string>
+    <string name="_bx_credits_menu_item_title_home"><![CDATA[Home]]></string>
+    <string name="_bx_credits_menu_item_title_system_admt_credits"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_menu_item_title_admt_credits"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_menu_item_title_bundles"><![CDATA[Bundles]]></string>
+    <string name="_bx_credits_menu_item_title_system_manage"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_menu_item_title_manage"><![CDATA[Credits]]></string>
+    <string name="_bx_credits_menu_title_manage_submenu"><![CDATA[Orders Submenu]]></string>
+    <string name="_bx_credits_menu_set_title_manage_submenu"><![CDATA[Orders Submenu]]></string>
+    <string name="_bx_credits_menu_item_title_system_orders_administration"><![CDATA[All Orders]]></string>
+    <string name="_bx_credits_menu_item_title_orders_administration"><![CDATA[All Orders]]></string>
+    <string name="_bx_credits_menu_item_title_system_orders_common"><![CDATA[My Orders]]></string>
+    <string name="_bx_credits_menu_item_title_orders_common"><![CDATA[My Orders]]></string>
+    <string name="_bx_credits_menu_item_title_system_history_administration"><![CDATA[All History]]></string>
+    <string name="_bx_credits_menu_item_title_history_administration"><![CDATA[All History]]></string>
+    <string name="_bx_credits_menu_item_title_system_history_common"><![CDATA[My History]]></string>
+    <string name="_bx_credits_menu_item_title_history_common"><![CDATA[My History]]></string>
+    <string name="_bx_credits_menu_item_title_system_withdrawals_administration"><![CDATA[All Withdrawals]]></string>
+    <string name="_bx_credits_menu_item_title_withdrawals_administration"><![CDATA[All Withdrawals]]></string>
+    <string name="_bx_credits_menu_item_title_system_withdrawals_common"><![CDATA[My Withdrawals]]></string>
+    <string name="_bx_credits_menu_item_title_withdrawals_common"><![CDATA[My Withdrawals]]></string>
+    <string name="_bx_credits_menu_item_title_system_profiles_administration"><![CDATA[All Profiles]]></string>
+    <string name="_bx_credits_menu_item_title_profiles_administration"><![CDATA[All Profiles]]></string>
+
+
+    <!-- Grids -->
+    <string name="_bx_credits_grid_column_title_name"><![CDATA[Name]]></string>
+    <string name="_bx_credits_grid_column_title_title"><![CDATA[Title]]></string>
+    <string name="_bx_credits_grid_column_title_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_credits_grid_column_title_bonus"><![CDATA[Bonus]]></string>
+    <string name="_bx_credits_grid_column_title_price"><![CDATA[Price]]></string>
+    <string name="_bx_credits_grid_action_title_add"><![CDATA[Add]]></string>
+    <string name="_bx_credits_grid_action_title_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_credits_grid_action_title_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_credits_grid_popup_title_add_bundle"><![CDATA[Add Bundle]]></string>
+    <string name="_bx_credits_grid_popup_title_edit_bundle"><![CDATA[Edit '{0}' Bundle]]></string>
+
+    <string name="_bx_credits_grid_column_title_htr_pid"><![CDATA[Profile]]></string>
+    <string name="_bx_credits_grid_column_title_htr_first_pid"><![CDATA[Profile 1]]></string>
+    <string name="_bx_credits_grid_column_title_htr_second_pid"><![CDATA[Profile 2]]></string>
+    <string name="_bx_credits_grid_column_title_htr_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_credits_grid_column_title_htr_order"><![CDATA[Order]]></string>
+    <string name="_bx_credits_grid_column_title_htr_direction"><![CDATA[Direction]]></string>
+    <string name="_bx_credits_grid_column_title_htr_info"><![CDATA[Notice]]></string>
+    <string name="_bx_credits_grid_column_title_htr_date"><![CDATA[Date]]></string>
+    <string name="_bx_credits_grid_column_title_htr_cleared"><![CDATA[Clearing]]></string>
+    <string name="_bx_credits_grid_action_title_htr_grant"><![CDATA[Grant]]></string>
+    <string name="_bx_credits_grid_action_title_htr_send"><![CDATA[Send]]></string>
+    <string name="_bx_credits_grid_popup_title_htr_grant"><![CDATA[Grant]]></string>
+    <string name="_bx_credits_grid_popup_title_htr_send"><![CDATA[Send]]></string>
+
+    <string name="_bx_credits_grid_column_title_ord_profile_id"><![CDATA[Profile]]></string>
+    <string name="_bx_credits_grid_column_title_ord_bundle"><![CDATA[Bundle]]></string>
+    <string name="_bx_credits_grid_column_title_ord_transaction"><![CDATA[Transaction]]></string>
+    <string name="_bx_credits_grid_column_title_ord_type"><![CDATA[Type]]></string>
+    <string name="_bx_credits_grid_column_value_ord_type_single"><![CDATA[Single]]></string>
+    <string name="_bx_credits_grid_column_value_ord_type_recurring"><![CDATA[Recurring]]></string>
+    <string name="_bx_credits_grid_column_title_ord_added"><![CDATA[Added]]></string>
+    <string name="_bx_credits_grid_column_title_ord_expired"><![CDATA[Expires]]></string>
+
+    <string name="_bx_credits_grid_column_title_wdw_profile_id"><![CDATA[Profile]]></string>
+    <string name="_bx_credits_grid_column_title_wdw_performer_id"><![CDATA[Performer]]></string>
+    <string name="_bx_credits_grid_column_title_wdw_amount"><![CDATA[Amount]]></string>
+    <string name="_bx_credits_grid_column_title_wdw_rate"><![CDATA[Rate]]></string>
+    <string name="_bx_credits_grid_column_title_wdw_message"><![CDATA[Message]]></string>
+    <string name="_bx_credits_grid_column_title_wdw_order"><![CDATA[Order]]></string>
+    <string name="_bx_credits_grid_column_title_wdw_added"><![CDATA[Added]]></string>
+    <string name="_bx_credits_grid_column_title_wdw_confirmed"><![CDATA[Confirmed]]></string>
+    <string name="_bx_credits_grid_column_title_wdw_status"><![CDATA[Status]]></string>
+    <string name="_bx_credits_grid_action_title_wdw_withdraw_confirm"><![CDATA[Confirm withdrawal]]></string>
+    <string name="_bx_credits_grid_action_title_wdw_withdraw_cancel"><![CDATA[Cancel withdrawal request]]></string>
+    <string name="_bx_credits_grid_action_title_wdw_withdraw_request"><![CDATA[Request withdrawal]]></string>
+    <string name="_bx_credits_grid_popup_title_wdw_withdraw_request"><![CDATA[Request withdrawal]]></string>
+
+    <string name="_bx_credits_grid_column_title_pfl_id"><![CDATA[Profile]]></string>
+    <string name="_bx_credits_grid_column_title_pfl_wdw_clearing"><![CDATA[Clearing period (in days)]]></string>
+    <string name="_bx_credits_grid_column_title_pfl_wdw_minimum"><![CDATA[Min withdrawal amount]]></string>
+    <string name="_bx_credits_grid_column_title_pfl_wdw_remaining"><![CDATA[Min remaining amount]]></string>
+    <string name="_bx_credits_grid_column_title_pfl_balance"><![CDATA[Balance]]></string>
+    <string name="_bx_credits_grid_column_title_pfl_balance_cleared"><![CDATA[Cleared balance]]></string>
+    <string name="_bx_credits_grid_action_title_pfl_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_credits_grid_popup_title_pfl_edit"><![CDATA[Edit '{0}' Profile's Limitations]]></string>
+
+    <!-- Email templates -->
+    <string name="_bx_credits_et_txt_name_granted"><![CDATA[Credits granted.]]></string>
+    <string name="_bx_credits_et_txt_subject_granted"><![CDATA[You're granted {amount} credits.]]></string>
+    <string name="_bx_credits_et_txt_body_granted"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p>You're granted <b>{amount}</b> credits.</p>
+<p>{message}</p>
+{email_footer}]]></string>
+    <string name="_bx_credits_et_txt_name_received"><![CDATA[Credits received.]]></string>
+    <string name="_bx_credits_et_txt_subject_received"><![CDATA[You've received {amount} credits.]]></string>
+    <string name="_bx_credits_et_txt_body_received"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p>You've received <b>{amount}</b> credits from <a href="{performer_link}">{performer_name}</a>.<br />The order is <b>{order}</b>.</p>
+<p>{message}</p>
+{email_footer}]]></string>
+    <string name="_bx_credits_et_txt_name_purchased"><![CDATA[Credits purchased.]]></string>
+    <string name="_bx_credits_et_txt_subject_purchased"><![CDATA[You've purchased {bundle_title} credits bundle.]]></string>
+    <string name="_bx_credits_et_txt_body_purchased"><![CDATA[{email_header}
+<p>Hello, {client_name}.</p>
+<p>You've purchased <a href="{bundle_url}">{bundle_title}</a> credits bundle.<br />Your order is <b>{order}</b>.</p>
+<p>{notes}</p>
+{email_footer}]]></string>
+    <string name="_bx_credits_et_txt_name_in"><![CDATA[Credits in.]]></string>
+    <string name="_bx_credits_et_txt_subject_in"><![CDATA[Credits in.]]></string>
+    <string name="_bx_credits_et_txt_body_in"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p>You've received <b>{amount}</b> credits from {second_profile_name}.</p>
+{email_footer}]]></string>
+    <string name="_bx_credits_et_txt_name_out"><![CDATA[Credits out.]]></string>
+    <string name="_bx_credits_et_txt_subject_out"><![CDATA[Credits out.]]></string>
+    <string name="_bx_credits_et_txt_body_out"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p>You've sent <b>{amount}</b> credits to {second_profile_name}.</p>
+{email_footer}]]></string>
+    <string name="_bx_credits_et_txt_name_withdraw_requested"><![CDATA[Withdrawal request.]]></string>
+    <string name="_bx_credits_et_txt_subject_withdraw_requested"><![CDATA[Withdrawal request.]]></string>
+    <string name="_bx_credits_et_txt_body_withdraw_requested"><![CDATA[{email_header}
+<p><a href="{profile_link}">{profile_name}</a> requested to withdraw <b>{amount}</b> credits from his/her balance.</p>
+<p>Current withdrawal conversion rate is <b>{rate}</b></p>
+<p>Withdrawal can be confirmed <a href="{confirm_url}">here</a>.</p>
+<p>Notes from <b>{profile_name}</b>:<br />{message}</p>
+{email_footer}]]></string>
+    <string name="_bx_credits_et_txt_name_withdraw_canceled"><![CDATA[Withdrawal request canceled.]]></string>
+    <string name="_bx_credits_et_txt_subject_withdraw_canceled"><![CDATA[Withdrawal request canceled.]]></string>
+    <string name="_bx_credits_et_txt_body_withdraw_canceled"><![CDATA[{email_header}
+<p><a href="{profile_link}">{profile_name}</a> canceled his/her withdrawal request for <b>{amount}</b> credits.</p>
+<p>The canceled withdrawal request can be seen <a href="{manage_url}">here</a>.</p>
+{email_footer}]]></string>
+    <string name="_bx_credits_et_txt_name_withdraw_sent"><![CDATA[Withdraw was sent.]]></string>
+    <string name="_bx_credits_et_txt_subject_withdraw_sent"><![CDATA[Withdraw was sent.]]></string>
+    <string name="_bx_credits_et_txt_body_withdraw_sent"><![CDATA[{email_header}
+<p>Hello, {profile_name}.</p>
+<p>The requested amount of credits (<b>{amount}</b>) was withdrawn to you.</p>
+{email_footer}]]></string>
+
+
+    <!-- Other -->
+    <string name="_bx_credits_txt_sample_single"><![CDATA[credit]]></string>
+    <string name="_bx_credits_txt_sample_single_with_article"><![CDATA[a credit]]></string>
+    <string name="_bx_credits_txt_for_free"><![CDATA[for Free]]></string>
+    <string name="_bx_credits_txt_n_plus_m_for_free"><![CDATA[{0} (+{1} for free)]]></string>
+    <string name="_bx_credits_txt_purchase"><![CDATA[Purchase]]></string>
+    <string name="_bx_credits_txt_never"><![CDATA[never]]></string>
+    <string name="_bx_credits_txt_direction_out"><![CDATA[sent to]]></string>
+    <string name="_bx_credits_txt_direction_in"><![CDATA[got from]]></string>
+    <string name="_bx_credits_txt_purchased_note"><![CDATA[Contact <a href="{0}">{1}</a> if you have any questions or need support.]]></string>
+    <string name="_bx_credits_txt_history_info_grant"><![CDATA[Grant]]></string>
+    <string name="_bx_credits_txt_history_info_send"><![CDATA[Transfer]]></string>
+    <string name="_bx_credits_txt_history_info_purchase"><![CDATA[Bundle purchase]]></string>
+    <string name="_bx_credits_txt_history_info_checkout"><![CDATA[Profile to profile checkout]]></string>
+    <string name="_bx_credits_txt_history_info_withdraw"><![CDATA[Withdrawal by request]]></string>
+    <string name="_bx_credits_txt_history_info_service"><![CDATA[Artificially initiated transfer]]></string>
+    <string name="_bx_credits_txt_history_info_refund"><![CDATA[Refund]]></string>
+    <string name="_bx_credits_txt_history_info_cancellation"><![CDATA[Cancellation]]></string>
+    <string name="_bx_credits_txt_withdrawal_status_requested"><![CDATA[Requested]]></string>
+    <string name="_bx_credits_txt_withdrawal_status_canceled"><![CDATA[Canceled]]></string>
+    <string name="_bx_credits_txt_withdrawal_status_confirmed"><![CDATA[Confirmed]]></string>
+
+
+    <string name="_bx_credits_msg_empty_author"><![CDATA['Main seller' is empty. You need to mark some profile as 'Main seller' in Settings of currently active Payment app.]]></string>
+    <string name="_bx_credits_msg_action_performed"><![CDATA[The action was performed successfully.]]></string>
+    <string name="_bx_credits_msg_action_withdrawals_disabled"><![CDATA[Withdrawals are disabled.]]></string>
+    <string name="_bx_credits_err_profile_not_found"><![CDATA[Profile wasn't found.]]></string>
+    <string name="_bx_credits_err_login_required"><![CDATA[Login required.]]></string>
+    <string name="_bx_credits_err_low_balance"><![CDATA[Low balance.]]></string>
+    <string name="_bx_credits_err_withdrawal_not_found"><![CDATA[Withdrawal request wasn't found.]]></string>
+    <string name="_bx_credits_err_withdraw_minimum"><![CDATA[Withdrawal less then {0} credits isn't available.]]></string>
+    <string name="_bx_credits_err_withdraw_remaining"><![CDATA[Minimum remaining balance after withdrawal should be more then {0} credits.]]></string>
+    <string name="_bx_credits_err_cannot_update_balance"><![CDATA[Cannot update balance.]]></string>
+    <string name="_bx_credits_err_cannot_send"><![CDATA[Cannot send message. Please report.]]></string>
+    <string name="_bx_credits_err_cannot_cancel_withdrawal"><![CDATA[Cannot cancel withdrawal request. Please report.]]></string>
+    <string name="_bx_credits_err_cannot_perform_action"><![CDATA[Cannot perform the action. Please report.]]></string>
+
+
+    <!-- Checkout process -->
+    <string name="_bx_credits_txt_checkout"><![CDATA[Checkout]]></string>
+    <string name="_bx_credits_txt_checkout_to"><![CDATA[Pay to {0} for:]]></string>
+    <string name="_bx_credits_txt_checkout_price_money"><![CDATA[Price:]]></string>
+    <string name="_bx_credits_txt_checkout_price_credits"><![CDATA[In Credits:]]></string>
+    <string name="_bx_credits_txt_checkout_rate"><![CDATA[Rate:]]></string>
+    <string name="_bx_credits_txt_checkout_qt"><![CDATA[qt.]]></string>
+
+    <!-- Subscribe process -->
+    <string name="_bx_credits_txt_subscribe"><![CDATA[Subscribe]]></string>
+    <string name="_bx_credits_txt_subscribe_for"><![CDATA[Subscribe for:]]></string>
+    <string name="_bx_credits_txt_subscribe_lifetime"><![CDATA[lifetime]]></string>    
+    <string name="_bx_credits_txt_subscribe_period_mask"><![CDATA[{0} {1}]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_year"><![CDATA[yr.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_month"><![CDATA[mo.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_week"><![CDATA[wk.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_day"><![CDATA[d.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_hour"><![CDATA[hr.]]></string>
+    <string name="_bx_credits_txt_subscribe_period_unit_minute"><![CDATA[min.]]></string>
+    <string name="_bx_credits_txt_subscribe_trial"><![CDATA[Trial:]]></string>
+    <string name="_bx_credits_txt_subscribe_trial_mask"><![CDATA[{0} {1}]]></string>
+
+    <string name="_bx_credits_err_unknown_buyer"><![CDATA[Login required.]]></string>
+    <string name="_bx_credits_err_unknown_seller"><![CDATA[Unknown seller.]]></string>
+    <string name="_bx_credits_err_incorrect_data"><![CDATA[Incorrect input data. Please report.]]></string>
+</resources>

+ 213 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/source/install/sql/install.sql

@@ -0,0 +1,213 @@
+-- TABLE: bundles
+CREATE TABLE IF NOT EXISTS `bx_credits_bundles` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `added` int(11) NOT NULL,
+  `name` varchar(255) NOT NULL,
+  `title` varchar(255) NOT NULL,
+  `description` varchar(255) NOT NULL,
+  `amount` int(11) NOT NULL DEFAULT '0',
+  `bonus` int(11) NOT NULL DEFAULT '0',
+  `price` float NOT NULL DEFAULT '0',
+  `active` tinyint(4) NOT NULL DEFAULT '0',
+  `order` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`)
+);
+
+-- TABLE: orders
+CREATE TABLE IF NOT EXISTS `bx_credits_orders` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `bundle_id` int(11) unsigned NOT NULL default '0',
+  `count` int(11) unsigned NOT NULL default '0',
+  `order` varchar(32) NOT NULL default '',
+  `license` varchar(32) NOT NULL default '',
+  `type` varchar(16) NOT NULL default '',
+  `added` int(11) unsigned NOT NULL default '0',
+  `expired` int(11) unsigned NOT NULL default '0',
+  `new` tinyint(1) NOT NULL default '1',
+  PRIMARY KEY (`id`),
+  KEY `order_id` (`bundle_id`, `profile_id`),
+  KEY `license` (`license`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_credits_orders_deleted` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `bundle_id` int(11) unsigned NOT NULL default '0',
+  `count` int(11) unsigned NOT NULL default '0',
+  `order` varchar(32) NOT NULL default '',
+  `license` varchar(32) NOT NULL default '',
+  `type` varchar(16) NOT NULL default '',
+  `added` int(11) unsigned NOT NULL default '0',
+  `expired` int(11) unsigned NOT NULL default '0',
+  `new` tinyint(1) NOT NULL default '1',
+  `reason` varchar(16) NOT NULL default '',
+  `deleted` int(11) unsigned NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `order_id` (`bundle_id`, `profile_id`),
+  KEY `license` (`license`)
+);
+
+-- TABLE: profiles
+CREATE TABLE IF NOT EXISTS `bx_credits_profiles` (
+  `id` int(11) NOT NULL DEFAULT '0',
+  `wdw_clearing` int(11) unsigned NOT NULL default '0',
+  `wdw_minimum` int(11) unsigned NOT NULL default '0',
+  `wdw_remaining` int(11) unsigned NOT NULL default '0',
+  `balance` float NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`)
+);
+
+-- TABLE: history
+CREATE TABLE IF NOT EXISTS `bx_credits_history` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `first_pid` int(11) NOT NULL DEFAULT '0',
+  `second_pid` int(11) NOT NULL DEFAULT '0',
+  `amount` float NOT NULL DEFAULT '0',
+  `type` varchar(16) NOT NULL default '',
+  `direction` enum('in', 'out') NOT NULL DEFAULT 'in',
+  `order` varchar(32) NOT NULL default '',
+  `data` text NOT NULL default '',
+  `info` varchar(255) NOT NULL DEFAULT '',
+  `date` int(11) NOT NULL DEFAULT '0',
+  `cleared` int(11) NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`)
+);
+
+-- TABLE: withdrawals
+CREATE TABLE IF NOT EXISTS `bx_credits_withdrawals` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `performer_id` int(11) unsigned NOT NULL default '0',
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `amount` float NOT NULL DEFAULT '0',
+  `rate` float NOT NULL DEFAULT '0',
+  `message` text NOT NULL default '',
+  `order` varchar(32) NOT NULL default '',
+  `added` int(11) unsigned NOT NULL default '0',
+  `confirmed` int(11) unsigned NOT NULL default '0',
+  `status` enum('requested', 'canceled', 'confirmed') NOT NULL DEFAULT 'requested',
+  PRIMARY KEY (`id`)
+);
+
+
+-- FORMS: credit
+INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_credits_credit', 'bx_credits', '_bx_credits_form_credit', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', '', '', '', '', 'a:1:{i:0;s:9:"do_submit";}', '', 0, 1, 'BxCreditsFormCredit', 'modules/boonex/credits/classes/BxCreditsFormCredit.php');
+
+INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
+('bx_credits_credit', 'bx_credits_credit_send', 'bx_credits', 0, '_bx_credits_form_credit_display_send'),
+('bx_credits_credit', 'bx_credits_credit_grant', 'bx_credits', 0, '_bx_credits_form_credit_display_grant'),
+('bx_credits_credit', 'bx_credits_credit_withdraw_request', 'bx_credits', 0, '_bx_credits_form_credit_display_withdraw_request');
+
+INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
+('bx_credits_credit', 'bx_credits', 'balance', '', '', 0, 'text', '_bx_credits_form_credit_input_sys_balance', '_bx_credits_form_credit_input_balance', '', 0, 0, 0, 'a:1:{s:8:"disabled";s:8:"disabled";}', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_credits_credit', 'bx_credits', 'cleared', '', '', 0, 'text', '_bx_credits_form_credit_input_sys_cleared', '_bx_credits_form_credit_input_cleared', '_bx_credits_form_credit_input_cleared_inf', 0, 0, 0, 'a:1:{s:8:"disabled";s:8:"disabled";}', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_credits_credit', 'bx_credits', 'rate', '', '', 0, 'text', '_bx_credits_form_credit_input_sys_rate', '_bx_credits_form_credit_input_rate', '', 0, 0, 0, 'a:1:{s:8:"disabled";s:8:"disabled";}', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_credits_credit', 'bx_credits', 'result', '', '', 0, 'text', '_bx_credits_form_credit_input_sys_result', '_bx_credits_form_credit_input_result', '', 0, 0, 0, 'a:1:{s:8:"disabled";s:8:"disabled";}', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_credits_credit', 'bx_credits', 'amount', '', '', 0, 'text', '_bx_credits_form_credit_input_sys_amount', '_bx_credits_form_credit_input_amount', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_credits_form_credit_input_amount_err', 'Xss', '', 1, 0),
+('bx_credits_credit', 'bx_credits', 'message', '', '', 0, 'textarea', '_bx_credits_form_credit_input_sys_message', '_bx_credits_form_credit_input_message', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_credits_credit', 'bx_credits', 'profile', '', '', 0, 'custom', '_bx_credits_form_credit_input_sys_profile', '_bx_credits_form_credit_input_profile', '', 1, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_credits_credit', 'bx_credits', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_credits_credit', 'bx_credits', 'do_submit', '_bx_credits_form_credit_input_do_submit', '', 0, 'submit', '_bx_credits_form_credit_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_credits_credit', 'bx_credits', 'do_cancel', '_bx_credits_form_credit_input_do_cancel', '', 0, 'button', '_bx_credits_form_credit_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES 
+('bx_credits_credit_send', 'profile', 2147483647, 1, 1),
+('bx_credits_credit_send', 'amount', 2147483647, 1, 2),
+('bx_credits_credit_send', 'message', 2147483647, 1, 3),
+('bx_credits_credit_send', 'controls', 2147483647, 1, 4),
+('bx_credits_credit_send', 'do_submit', 2147483647, 1, 5),
+('bx_credits_credit_send', 'do_cancel', 2147483647, 1, 6),
+
+('bx_credits_credit_grant', 'profile', 2147483647, 1, 1),
+('bx_credits_credit_grant', 'amount', 2147483647, 1, 2),
+('bx_credits_credit_grant', 'message', 2147483647, 1, 3),
+('bx_credits_credit_grant', 'controls', 2147483647, 1, 4),
+('bx_credits_credit_grant', 'do_submit', 2147483647, 1, 5),
+('bx_credits_credit_grant', 'do_cancel', 2147483647, 1, 6),
+
+('bx_credits_credit_withdraw_request', 'balance', 2147483647, 1, 1),
+('bx_credits_credit_withdraw_request', 'cleared', 2147483647, 1, 2),
+('bx_credits_credit_withdraw_request', 'rate', 2147483647, 1, 3),
+('bx_credits_credit_withdraw_request', 'amount', 2147483647, 1, 4),
+('bx_credits_credit_withdraw_request', 'result', 2147483647, 1, 5),
+('bx_credits_credit_withdraw_request', 'message', 2147483647, 1, 6),
+('bx_credits_credit_withdraw_request', 'controls', 2147483647, 1, 7),
+('bx_credits_credit_withdraw_request', 'do_submit', 2147483647, 1, 8),
+('bx_credits_credit_withdraw_request', 'do_cancel', 2147483647, 1, 9);
+
+-- FORMS: bundle
+INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_credits_bundle', 'bx_credits', '_bx_credits_form_bundle', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'bx_credits_bundles', 'id', '', '', 'a:1:{i:0;s:9:"do_submit";}', '', 0, 1, 'BxCreditsFormBundle', 'modules/boonex/credits/classes/BxCreditsFormBundle.php');
+
+INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
+('bx_credits_bundle', 'bx_credits_bundle_add', 'bx_credits', 0, '_bx_credits_form_bundle_display_add'),
+('bx_credits_bundle', 'bx_credits_bundle_edit', 'bx_credits', 0, '_bx_credits_form_bundle_display_edit');
+
+INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
+('bx_credits_bundle', 'bx_credits', 'title', '', '', 0, 'text_translatable', '_bx_credits_form_bundle_input_sys_title', '_bx_credits_form_bundle_input_title', '', 1, 0, 0, '', '', '', 'AvailTranslatable', 'a:1:{i:0;s:5:"title";}', '_bx_credits_form_bundle_input_title_err', 'Xss', '', 1, 0),
+('bx_credits_bundle', 'bx_credits', 'description', '', '', 0, 'textarea_translatable', '_bx_credits_form_bundle_input_sys_description', '_bx_credits_form_bundle_input_description', '', 0, 0, 3, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+('bx_credits_bundle', 'bx_credits', 'name', '', '', 0, 'text', '_bx_credits_form_bundle_input_sys_name', '_bx_credits_form_bundle_input_name', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_credits_form_bundle_input_name_err', 'Xss', '', 1, 0),
+('bx_credits_bundle', 'bx_credits', 'amount', '', '', 0, 'text', '_bx_credits_form_bundle_input_sys_amount', '_bx_credits_form_bundle_input_amount', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_credits_form_bundle_input_amount_err', 'Xss', '', 1, 0),
+('bx_credits_bundle', 'bx_credits', 'bonus', '', '', 0, 'text', '_bx_credits_form_bundle_input_sys_bonus', '_bx_credits_form_bundle_input_bonus', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_credits_bundle', 'bx_credits', 'price', '', '', 0, 'price', '_bx_credits_form_bundle_input_sys_price', '_bx_credits_form_bundle_input_price', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_credits_form_bundle_input_price_err', 'Xss', '', 1, 0),
+('bx_credits_bundle', 'bx_credits', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_credits_bundle', 'bx_credits', 'do_submit', '_bx_credits_form_bundle_input_do_submit', '', 0, 'submit', '_bx_credits_form_bundle_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_credits_bundle', 'bx_credits', 'do_cancel', '_bx_credits_form_bundle_input_do_cancel', '', 0, 'button', '_bx_credits_form_bundle_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES 
+('bx_credits_bundle_add', 'name', 2147483647, 1, 1),
+('bx_credits_bundle_add', 'title', 2147483647, 1, 2),
+('bx_credits_bundle_add', 'description', 2147483647, 1, 3),
+('bx_credits_bundle_add', 'amount', 2147483647, 1, 4),
+('bx_credits_bundle_add', 'bonus', 2147483647, 1, 5),
+('bx_credits_bundle_add', 'price', 2147483647, 1, 6),
+('bx_credits_bundle_add', 'controls', 2147483647, 1, 7),
+('bx_credits_bundle_add', 'do_submit', 2147483647, 1, 8),
+('bx_credits_bundle_add', 'do_cancel', 2147483647, 1, 9),
+
+('bx_credits_bundle_edit', 'name', 2147483647, 1, 1),
+('bx_credits_bundle_edit', 'title', 2147483647, 1, 2),
+('bx_credits_bundle_edit', 'description', 2147483647, 1, 3),
+('bx_credits_bundle_edit', 'amount', 2147483647, 1, 4),
+('bx_credits_bundle_edit', 'bonus', 2147483647, 1, 5),
+('bx_credits_bundle_edit', 'price', 2147483647, 1, 6),
+('bx_credits_bundle_edit', 'controls', 2147483647, 1, 7),
+('bx_credits_bundle_edit', 'do_submit', 2147483647, 1, 8),
+('bx_credits_bundle_edit', 'do_cancel', 2147483647, 1, 9);
+
+-- FORMS: profile
+INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_credits_profile', 'bx_credits', '_bx_credits_form_profile', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'bx_credits_profiles', 'id', '', '', 'a:1:{i:0;s:9:"do_submit";}', '', 0, 1, 'BxCreditsFormProfile', 'modules/boonex/credits/classes/BxCreditsFormProfile.php');
+
+INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
+('bx_credits_profile', 'bx_credits_profile_edit', 'bx_credits', 0, '_bx_credits_form_profile_display_edit');
+
+INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
+('bx_credits_profile', 'bx_credits', 'wdw_clearing', '', '', 0, 'text', '_bx_credits_form_profile_input_sys_wdw_clearing', '_bx_credits_form_profile_input_wdw_clearing', '_bx_credits_form_profile_input_wdw_clearing_inf', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_credits_profile', 'bx_credits', 'wdw_minimum', '', '', 0, 'text', '_bx_credits_form_profile_input_sys_wdw_minimum', '_bx_credits_form_profile_input_wdw_minimum', '_bx_credits_form_profile_input_wdw_minimum_inf', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_credits_profile', 'bx_credits', 'wdw_remaining', '', '', 0, 'text', '_bx_credits_form_profile_input_sys_wdw_remaining', '_bx_credits_form_profile_input_wdw_remaining', '_bx_credits_form_profile_input_wdw_remaining_inf', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_credits_profile', 'bx_credits', 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_credits_profile', 'bx_credits', 'do_submit', '_bx_credits_form_profile_input_do_submit', '', 0, 'submit', '_bx_credits_form_profile_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_credits_profile', 'bx_credits', 'do_cancel', '_bx_credits_form_profile_input_do_cancel', '', 0, 'button', '_bx_credits_form_bundle_input_sys_do_cancel', '', '', 0, 0, 0, 'a:2:{s:7:"onclick";s:45:"$(''.bx-popup-applied:visible'').dolPopupHide()";s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 1, 0);
+
+INSERT INTO `sys_form_display_inputs`(`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES 
+('bx_credits_profile_edit', 'wdw_clearing', 2147483647, 1, 1),
+('bx_credits_profile_edit', 'wdw_minimum', 2147483647, 1, 2),
+('bx_credits_profile_edit', 'wdw_remaining', 2147483647, 1, 3),
+('bx_credits_profile_edit', 'controls', 2147483647, 1, 4),
+('bx_credits_profile_edit', 'do_submit', 2147483647, 1, 5),
+('bx_credits_profile_edit', 'do_cancel', 2147483647, 1, 6);
+
+
+-- STUDIO: page & widget
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, 'bx_credits', '_bx_credits', '_bx_credits', 'bx_credits@modules/boonex/credits/|std-icon.svg');
+SET @iPageId = LAST_INSERT_ID();
+
+SET @iParentPageId = (SELECT `id` FROM `sys_std_pages` WHERE `name` = 'home');
+SET @iParentPageOrder = (SELECT MAX(`order`) FROM `sys_std_pages_widgets` WHERE `page_id` = @iParentPageId);
+INSERT INTO `sys_std_widgets` (`page_id`, `module`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, 'bx_credits', 'extensions', '{url_studio}module.php?name=bx_credits', '', 'bx_credits@modules/boonex/credits/|std-icon.svg', '_bx_credits', '', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_actions";s:6:"params";a:0:{}s:5:"class";s:18:"TemplStudioModules";}');
+INSERT INTO `sys_std_pages_widgets` (`page_id`, `widget_id`, `order`) VALUES
+(@iParentPageId, LAST_INSERT_ID(), IF(ISNULL(@iParentPageOrder), 1, @iParentPageOrder + 1));

+ 35 - 0
modules/boonex/credits/updates/14.0.3_14.0.4/source/template/css/main.css

@@ -0,0 +1,35 @@
+.sys-icon.bx-credits-prefix-icon {
+    color: #334455;
+}
+
+/*--- 'Auto suggestion' popups ---*/
+.ui-autocomplete.ui-front {
+    z-index: 10001;
+}
+
+/*--- Bundles ---*/
+.bx-credits-bundle-cnt > div {
+    -webkit-flex: 0 1 auto; 
+    flex: 0 1 auto;
+
+    width: 100%;
+}
+
+.bx-credits-bp-currency {
+    margin-top: 0.5rem;
+
+    line-height: normal;
+    font-size: 1rem;
+    font-weight: 300;
+}
+
+.bx-credits-bp-value {
+    line-height: normal;
+    font-size: 2.4rem;
+    font-weight: 700;
+}
+
+.bx-credits-b-buy .bx-btn {
+    float: none;
+    display: inline-block;
+}

+ 51 - 0
modules/boonex/events/updates/14.0.5_14.0.6/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Events',
+    'version_from' => '14.0.5',
+    'version_to' => '14.0.6',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/events/updates/update_14.0.5_14.0.6/',
+    'home_uri' => 'events_update_1405_1406',
+
+    'module_dir' => 'boonex/events/',
+    'module_uri' => 'events',
+
+    'db_prefix' => 'bx_events_',
+    'class_prefix' => 'BxEvents',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Events',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/events/updates/14.0.5_14.0.6/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxEventsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 4 - 0
modules/boonex/events/updates/14.0.5_14.0.6/install/langs/en.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_add name="_bx_events_option_rm_from_timeline_after"><![CDATA[Delete past events from Timeline after N hours (0 - don't delete)]]></string_add>
+</resources>

+ 9 - 0
modules/boonex/events/updates/14.0.5_14.0.6/install/sql/enable.sql

@@ -0,0 +1,9 @@
+SET @sName = 'bx_events';
+
+
+-- SETTINGS
+SET @iCategId = (SELECT `id` FROM `sys_options_categories` WHERE `name`=@sName LIMIT 1);
+
+DELETE FROM `sys_options` WHERE `name`='bx_events_rm_from_timeline_after';
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `extra`, `check`, `check_error`, `order`) VALUES
+('bx_events_rm_from_timeline_after', '0', @iCategId, '_bx_events_option_rm_from_timeline_after', 'digit', '', '', '', 80);

+ 339 - 0
modules/boonex/events/updates/14.0.5_14.0.6/source/classes/BxEventsConfig.php

@@ -0,0 +1,339 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Events Events
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxEventsConfig extends BxBaseModGroupsConfig
+{
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_aMenuItems2MethodsActions = array_merge($this->_aMenuItems2MethodsActions, array(
+            'view-event-profile' => 'checkAllowedView',
+            'edit-event-profile' => 'checkAllowedEdit',
+            'event-manage' => 'checkAllowedEdit',
+            'event-questionnaire' => 'checkAllowedEdit',
+            'event-sessions' => 'checkAllowedEdit',
+            'event-cover' => 'checkAllowedChangeCover',
+            'invite-to-event' => 'checkAllowedInvite',
+            'delete-event-profile' => 'checkAllowedDelete',
+            'approve-event-profile' => 'checkAllowedApprove',
+            'profile-check-in' =>  'checkAllowedCheckIn',
+            'ical-export' => 'checkAllowedIcalExport',
+        ));
+
+        $this->CNF = array (
+
+            // module icon
+            'ICON' => 'calendar col-red2',
+
+            // database tables
+            'TABLE_ENTRIES' => $aModule['db_prefix'] . 'data',
+            'TABLE_ENTRIES_FULLTEXT' => 'search_fields',
+            'TABLE_ADMINS' => $aModule['db_prefix'] . 'admins',
+            'TABLE_CHECK_IN' => $aModule['db_prefix'] . 'check_in',
+            'TABLE_INVITES' => $aModule['db_prefix'] . 'invites',
+            'TABLE_PRICES' => $aModule['db_prefix'] . 'prices',
+            'TABLE_QUESTIONS' => $aModule['db_prefix'] . 'qnr_questions',
+            'TABLE_ANSWERS' => $aModule['db_prefix'] . 'qnr_answers',
+            'TABLE_SESSIONS' => $aModule['db_prefix'] . 'sessions',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_AUTHOR' => 'author',
+            'FIELD_ADDED' => 'added',
+            'FIELD_CHANGED' => 'changed',
+            'FIELD_PUBLISHED' => 'published',
+            'FIELD_NAME' => 'event_name',
+            'FIELD_TITLE' => 'event_name',
+            'FIELD_TEXT' => 'event_desc',
+            'FIELD_CATEGORY' => 'event_cat',
+            'FIELD_PICTURE' => 'picture',
+            'FIELD_COVER' => 'cover',
+            'FIELD_COVER_POSITION' => 'cover_data',
+            'FIELD_DATE_START' => 'date_start',
+            'FIELD_DATE_END' => 'date_end',
+            'FIELD_TIMEZONE' => 'timezone',
+            'FIELD_JOIN_CONFIRMATION' => 'join_confirmation',
+            'FIELD_REMINDER' => 'reminder',
+            'FIELD_ALLOW_VIEW_TO' => 'allow_view_to',
+            'FIELD_ALLOW_POST_TO' => 'allow_post_to',
+            'FIELD_CF' => 'cf',
+            'FIELD_VIEWS' => 'views',
+            'FIELD_VOTES' => 'votes',
+            'FIELD_COMMENTS' => 'comments',
+            'FIELD_STATUS' => 'status',
+            'FIELD_STATUS_ADMIN' => 'status_admin',
+            'FIELDS_QUICK_SEARCH' => array('event_name'),
+            'FIELD_LOCATION' => 'location',
+            'FIELD_LOCATION_PREFIX' => 'location',
+            'FIELDS_WITH_KEYWORDS' => 'auto', // can be 'auto', array of fields or comma separated string of field names, works only when OBJECT_METATAGS is specified
+            'FIELD_LABELS' => 'labels',
+
+            'FIELD_PRICE_ROLE_ID' => 'role_id',
+            'FIELD_PRICE_NAME' => 'name',
+
+            // page URIs
+            'URI_VIEW_ENTRY' => 'view-event-profile',
+            'URI_VIEW_MEMBERS' => 'event-fans',
+            'URI_EDIT_ENTRY' => 'edit-event-profile',
+            'URI_ENTRIES_BY_CONTEXT' => 'events-context',
+            'URI_JOIN_ENTRY' => 'join-event-profile',
+            'URI_JOINED_ENTRIES' => 'joined-events',
+            'URI_FOLLOWED_ENTRIES' => 'followed-events',
+            'URI_MANAGE_COMMON' => 'events-manage',
+            'URI_FAVORITES_LIST' => 'events-favorites',
+
+            'URL_HOME' => 'page.php?i=events-home',
+            'URL_ENTRY_FANS' => 'page.php?i=event-fans',
+            'URL_ENTRY_MANAGE' => 'page.php?i=event-manage',
+            'URL_MANAGE_COMMON' => 'page.php?i=events-manage',
+            'URL_MANAGE_ADMINISTRATION' => 'page.php?i=events-administration',
+
+            'PARAM_AUTO_APPROVE' => 'bx_events_enable_auto_approve',
+            'PARAM_CHARS_SUMMARY' => 'bx_events_summary_chars',
+            'PARAM_NUM_RSS' => 'bx_events_num_rss',
+            'PARAM_NUM_CONNECTIONS_QUICK' => 'bx_events_num_connections_quick',
+            'PARAM_SEARCHABLE_FIELDS' => 'bx_events_searchable_fields',
+            'PARAM_PUBLIC_SBSD' => 'bx_events_public_subscribed_me',
+            'PARAM_PER_PAGE_BROWSE_SHOWCASE' => 'bx_events_per_page_browse_showcase',
+            'PARAM_PER_PAGE_BROWSE_RECOMMENDED' => 'bx_events_per_page_browse_recommended',
+            'PARAM_FORMAT_TIME' => 'bx_events_time_format',
+            'PARAM_FORMAT_DATE' => 'bx_events_short_date_format',
+            'PARAM_FORMAT_DATETIME' => 'bx_events_datetime_format',
+            'PARAM_MMODE' => 'bx_events_members_mode',
+            'PARAM_PAID_JOIN_ENABLED' => true,
+            'PARAM_RECURRING_RESERVE' => 3, // 3 days for recurring payment to be registered
+            'PARAM_SBS_WO_JOIN' => 'bx_events_enable_subscribe_wo_join',
+            'PARAM_PER_PAGE_FOR_FAVORITES_LISTS' => 'bx_events_per_page_for_favorites_lists',
+            'PARAM_USE_IN' => 'bx_events_internal_notifications',
+            'PARAM_DELETE_FROM_TIMELINE_AFTER' => 'bx_events_rm_from_timeline_after',
+
+            // objects
+            'OBJECT_STORAGE' => 'bx_events_pics',
+            'OBJECT_STORAGE_COVER' => 'bx_events_pics',
+            'OBJECT_IMAGES_TRANSCODER_THUMB' => 'bx_events_thumb',
+            'OBJECT_IMAGES_TRANSCODER_ICON' => 'bx_events_icon',
+            'OBJECT_IMAGES_TRANSCODER_AVATAR' => 'bx_events_avatar',
+            'OBJECT_IMAGES_TRANSCODER_AVATAR_BIG' => 'bx_events_avatar_big',
+            'OBJECT_IMAGES_TRANSCODER_PICTURE' => 'bx_events_picture',
+            'OBJECT_IMAGES_TRANSCODER_COVER' => 'bx_events_cover',
+            'OBJECT_IMAGES_TRANSCODER_COVER_THUMB' => 'bx_events_cover_thumb',
+            'OBJECT_IMAGES_TRANSCODER_GALLERY' => 'bx_events_gallery',
+            'OBJECT_VIEWS' => 'bx_events',
+            'OBJECT_VOTES' => 'bx_events',
+            'OBJECT_SCORES' => 'bx_events',
+            'OBJECT_FAVORITES' => 'bx_events',
+            'OBJECT_FEATURED' => 'bx_events',
+            'OBJECT_COMMENTS' => 'bx_events',
+            'OBJECT_NOTES' => 'bx_events_notes',
+            'OBJECT_REPORTS' => 'bx_events',
+            'OBJECT_METATAGS' => 'bx_events',
+            'OBJECT_CATEGORY' => 'bx_events_cats',
+            'OBJECT_FORM_ENTRY' => 'bx_event',
+            'OBJECT_FORM_ENTRY_DISPLAY_VIEW' => 'bx_event_view',
+            'OBJECT_FORM_ENTRY_DISPLAY_VIEW_FULL' => 'bx_event_view_full', // for "info" tab on view group page
+            'OBJECT_FORM_ENTRY_DISPLAY_ADD' => 'bx_event_add',
+            'OBJECT_FORM_ENTRY_DISPLAY_EDIT' => 'bx_event_edit',
+            'OBJECT_FORM_ENTRY_DISPLAY_EDIT_COVER' => 'bx_event_edit_cover',
+            'OBJECT_FORM_ENTRY_DISPLAY_DELETE' => 'bx_event_delete',
+            'OBJECT_FORM_ENTRY_DISPLAY_INVITE' => 'bx_event_invite',
+            'OBJECT_FORM_QUESTION' => 'bx_events_question',
+            'OBJECT_FORM_QUESTION_DISPLAY_ADD' => 'bx_events_question_add',
+            'OBJECT_FORM_QUESTION_DISPLAY_EDIT' => 'bx_events_question_edit',
+            'OBJECT_FORM_SESSION' => 'bx_events_session',
+            'OBJECT_FORM_SESSION_DISPLAY_ADD' => 'bx_events_session_add',
+            'OBJECT_FORM_SESSION_DISPLAY_EDIT' => 'bx_events_session_edit',
+            'OBJECT_FORM_PRICE' => 'bx_events_price',
+            'OBJECT_FORM_PRICE_DISPLAY_ADD' => 'bx_events_price_add',
+            'OBJECT_FORM_PRICE_DISPLAY_EDIT' => 'bx_events_price_edit',
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY' => 'bx_events_view_actions', // actions menu on view entry page
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY_MORE' => 'bx_events_view_actions_more', // actions menu on view entry page for "more" popup
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY_ALL' => 'bx_events_view_actions_all', // all actions menu on view entry page
+            'OBJECT_MENU_ACTIONS_MY_ENTRIES' => 'bx_events_my', // actions menu on profile entries page
+            'OBJECT_MENU_SUBMENU' => 'bx_events_submenu', // main module submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY' => 'bx_events_view_submenu',  // view entry submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY_MAIN_SELECTION' => 'events-home', // first item in view entry submenu from main module submenu
+            'OBJECT_MENU_MANAGE_VIEW_ENTRY' => '',
+            'OBJECT_MENU_VIEW_ENTRY_META' => 'bx_events_view_meta', // meta menu on view entry page
+            'OBJECT_MENU_SNIPPET_META' => 'bx_events_snippet_meta', // menu for snippet meta info
+            'OBJECT_MENU_MANAGE_TOOLS' => 'bx_events_menu_manage_tools', //manage menu in content administration tools
+            'OBJECT_PAGE_VIEW_ENTRY' => 'bx_events_view_profile',
+            'OBJECT_PAGE_VIEW_ENTRY_CLOSED' => 'bx_events_view_profile_closed',
+            'OBJECT_PAGE_JOINED_ENTRY' => 'bx_events_join_profile',
+            'OBJECT_PRIVACY_VIEW' => 'bx_events_allow_view_to',
+            'OBJECT_PRIVACY_LIST_VIEW' => 'bx_events_allow_view_favorite_list',
+            'OBJECT_PRIVACY_VIEW_NOTIFICATION_EVENT' => 'bx_events_allow_view_notification_to',
+            'OBJECT_PRIVACY_POST' => 'bx_events_allow_post_to',
+            'OBJECT_GRID_ADMINISTRATION' => 'bx_events_administration',
+            'OBJECT_GRID_COMMON' => 'bx_events_common',
+            'OBJECT_GRID_CONNECTIONS' => 'bx_events_fans',
+            'OBJECT_GRID_INVITES' => 'bx_events_invites',
+            'OBJECT_GRID_QUESTIONS_MANAGE' => 'bx_events_questions_manage',
+            'OBJECT_GRID_SESSIONS_MANAGE' => 'bx_events_sessions_manage',
+            'OBJECT_GRID_PRICES_MANAGE' => 'bx_events_prices_manage',
+            'OBJECT_GRID_PRICES_VIEW' => 'bx_events_prices_view',
+            'OBJECT_CONNECTIONS' => 'bx_events_fans',
+            'OBJECT_UPLOADERS_COVER' => array('bx_events_cover_crop'),
+            'OBJECT_UPLOADERS_PICTURE' => array('bx_events_picture_crop'),
+            'OBJECT_PRE_LIST_ROLES' => 'bx_events_roles',
+            'OBJECT_PRE_LIST_PERIOD_UNITS' => 'bx_events_period_units',
+            
+            'BADGES_AVALIABLE' => true,
+            'INVITES_KEYS_LIFETIME' => 86400,
+            'ENABLE_FOR_CONTEXT_IN_MODULES' => array('bx_groups', 'bx_spaces'),
+
+            'EMAIL_INVITATION' => 'bx_events_invitation',
+            'EMAIL_JOIN_REQUEST' => 'bx_events_join_request',
+            'EMAIL_JOIN_CONFIRM' => 'bx_events_join_confirm',
+            'EMAIL_FAN_BECOME_ADMIN' => 'bx_events_fan_become_admin',
+            'EMAIL_ADMIN_BECOME_FAN' => 'bx_events_admin_become_fan',
+            'EMAIL_FAN_REMOVE' => 'bx_events_fan_remove',
+            'EMAIL_FAN_SET_ROLE' => 'bx_events_set_role',
+            'EMAIL_JOIN_REJECT' => 'bx_events_join_reject',
+            'EMAIL_REMINDER' => 'bx_events_reminder',
+
+            'TRIGGER_MENU_PROFILE_VIEW_SUBMENU' => 'trigger_group_view_submenu',
+            'TRIGGER_MENU_PROFILE_SNIPPET_META' => 'trigger_group_snippet_meta',
+            'TRIGGER_MENU_PROFILE_VIEW_ACTIONS' => 'trigger_group_view_actions',
+        	'TRIGGER_PAGE_VIEW_ENTRY' => 'trigger_page_group_view_entry',
+
+            // menu items which visibility depends on custom visibility checking
+            'MENU_ITEM_TO_METHOD' => array (
+                'bx_events_view_actions' => $this->_aMenuItems2MethodsActions,
+                'bx_events_view_actions_more' => $this->_aMenuItems2MethodsActions,
+                'bx_events_view_actions_all' => $this->_aMenuItems2MethodsActions,
+            ),
+
+            // informer messages
+            'INFORMERS' => array (
+                'status' => array (
+                    'name' => 'bx-events-status-not-active',
+                    'map' => array (
+                        BX_PROFILE_STATUS_PENDING => '_bx_events_txt_account_pending',
+                        BX_PROFILE_STATUS_SUSPENDED => '_bx_events_txt_account_suspended',
+                    ),
+                ),
+                'approving' => array (
+                    'name' => 'bx-groups-approving',
+                    'map' => array (
+                        'pending' => array('msg' => '_bx_events_txt_account_pending', 'type' => BX_INFORMER_ALERT),
+                        'hidden' => array('msg' => '_bx_events_txt_status_hidden', 'type' => BX_INFORMER_ERROR),
+                    ),
+                ),
+                'scheduled' => array (
+                    'name' => 'bx-events-scheduled',
+                    'map' => array (
+                        'awaiting' => array('msg' => '_bx_events_txt_scheduled_awaiting', 'type' => BX_INFORMER_ALERT),
+                    ),
+                ),
+            ),
+
+            // some language keys
+            'T' => array (
+                'txt_sample_single' => '_bx_events_txt_sample_single',
+            	'txt_sample_single_with_article' => '_bx_events_txt_sample_single_with_article',
+            	'txt_sample_comment_single' => '_bx_events_txt_sample_comment_single',
+                'txt_sample_vote_single' => '_bx_events_txt_sample_vote_single',
+                'txt_sample_score_up_single' => '_bx_events_txt_sample_score_up_single',
+                'txt_sample_score_down_single' => '_bx_events_txt_sample_score_down_single',
+                'txt_private_group' => '_bx_events_txt_private_group',
+                'txt_N_fans' => '_bx_events_txt_N_fans',
+                'txt_ntfs_join_invitation' => '_bx_events_txt_ntfs_join_invitation',
+                'txt_ntfs_join_request' => '_bx_events_txt_ntfs_join_request',
+                'txt_ntfs_fan_added' => '_bx_events_txt_ntfs_fan_added',
+            	'txt_ntfs_timeline_post_common' => '_bx_events_txt_ntfs_timeline_post_common',
+                'option_members_mode_multi_roles' => '_bx_events_option_members_mode_multi_roles',
+                'txt_all_entries_in' => '_bx_events_txt_all_entries_in',
+            	'form_field_author' => '_bx_events_form_profile_input_author',
+                'menu_item_title_befriend_sent' => '_bx_events_menu_item_title_befriend_sent',
+                'menu_item_title_unfriend_cancel_request' => '_bx_events_menu_item_title_unfriend_cancel_request',
+                'menu_item_title_befriend_confirm' => '_bx_events_menu_item_title_befriend_confirm',
+                'menu_item_title_unfriend_reject_request' => '_bx_events_menu_item_title_unfriend_reject_request',
+                'menu_item_title_befriend' => '_bx_events_menu_item_title_befriend',
+                'menu_item_title_unfriend' => '_bx_events_menu_item_title_unfriend',
+                'menu_item_title_subscribe' => '_bx_events_menu_item_title_subscribe',
+                'menu_item_title_unsubscribe' => '_bx_events_menu_item_title_unsubscribe',
+            	'grid_action_err_delete' => '_bx_events_grid_action_err_delete',
+            	'grid_txt_account_manager' => '_bx_events_grid_txt_account_manager',
+                'filter_item_active' => '_bx_events_grid_filter_item_title_adm_active',
+                'filter_item_hidden' => '_bx_events_grid_filter_item_title_adm_hidden',
+            	'filter_item_pending' => '_bx_events_grid_filter_item_title_adm_pending',
+            	'filter_item_suspended' => '_bx_events_grid_filter_item_title_adm_suspended',
+            	'filter_item_select_one_filter1' => '_bx_events_grid_filter_item_title_adm_select_one_filter1',
+                'filter_item_select_one_filter2' => '_bx_events_grid_filter_item_title_adm_select_one_filter2',
+            	'menu_item_manage_my' => '_bx_events_menu_item_title_manage_my',
+                'menu_item_manage_all' => '_bx_events_menu_item_title_manage_all',
+                'menu_item_title_sm_join' => '_bx_events_menu_item_title_become_fan',
+                'menu_item_title_sm_join_requested' => '_bx_events_menu_item_title_become_fan_sent',
+                'menu_item_title_sm_leave' => '_bx_events_menu_item_title_leave_group',
+                'menu_item_title_sm_leave_cancel' => '_bx_events_menu_item_title_leave_group_cancel_request',
+                'menu_item_title_sm_members' => '_bx_events_menu_item_title_sm_members',
+                'menu_item_title_sm_subscribers' => '_bx_events_menu_item_title_sm_subscribers',
+            	'txt_all_entries_by_author' => '_bx_events_page_title_browse_by_author',
+                'txt_all_entries_by_context' => '_bx_events_page_title_browse_by_context',
+                'txt_invitation_popup_title' => '_bx_events_txt_invite_popup_title',
+                'txt_invitation_popup_text' => '_bx_events_txt_invite_popup_text',
+                'txt_invitation_popup_accept_button' => '_bx_events_txt_invite_popup_button_accept',
+                'txt_invitation_popup_decline_button' => '_bx_events_txt_invite_popup_button_decline',
+                'txt_invitation_popup_error_invitation_absent' => '_bx_events_txt_invite_popup_error_invitation_absent',
+                'txt_invitation_popup_error_wrong_user' => '_bx_events_txt_invite_popup_error_invitation_wrong_user',
+                'txt_n_unit' => '_bx_events_txt_n_unit',
+                'txt_buy_title' => '_bx_events_grid_action_title_buy_title',
+                'txt_cart_item_title' => '_bx_events_txt_cart_item_title',
+                'txt_cart_item_title_lifetime' => '_bx_events_txt_cart_item_title_lifetime',
+                'txt_subscribe_title' => '_bx_events_grid_action_title_subscribe_title',
+                'form_qnr_field_qn_err' => '_bx_events_form_questionnaire_input_question_err',
+                'popup_title_questionnaire' => '_bx_events_popup_title_questionnaire',
+                'popup_title_question_add' => '_bx_events_popup_title_qn_add',
+                'popup_title_question_edit' => '_bx_events_popup_title_qn_edit',
+                'popup_title_price_add' => '_bx_events_popup_title_price_add',
+                'popup_title_price_edit' => '_bx_events_popup_title_price_edit',
+                'msg_performed' => '_bx_events_msg_performed',
+                'err_period_unit' => '_bx_events_form_price_input_err_period_unit',
+                'err_price_duplicate' => '_bx_events_err_price_duplicate',
+                'err_cannot_perform' => '_bx_events_err_cannot_perform',
+            ),
+
+        );
+
+        $this->_aJsClasses = array(
+            'main' => 'BxEventsMain',
+            'entry' => 'BxEventsEntry',
+            'manage_tools' => 'BxEventsManageTools',
+            'invite_popup' => 'BxEventsInvitePopup',
+            'prices' => 'BxEventsPrices'
+        );
+
+        $this->_aJsObjects = array(
+            'main' => 'oBxEventsMain',
+            'entry' => 'oBxEventsEntry',
+            'manage_tools' => 'oBxEventsManageTools',
+            'invite_popup' => 'oBxEventsInvitePopup',
+            'prices' => 'oBxEventsPrices'
+        );
+
+        $this->_aGridObjects = array(
+            'common' => $this->CNF['OBJECT_GRID_COMMON'],
+            'administration' => $this->CNF['OBJECT_GRID_ADMINISTRATION']
+        );
+
+        $sHtmlPrefix = str_replace('_', '-', $this->_sName);
+        $this->_aHtmlIds = array_merge($this->_aHtmlIds, [
+            'popup_bfilters_recent' => $sHtmlPrefix . '-popup-bfilters-recent',
+            'popup_question' => $sHtmlPrefix . '-popup-question',
+            'popup_questionnaire' => $sHtmlPrefix . '-popup-questionnaire',
+            'popup_session' => $sHtmlPrefix . '-popup-session'
+        ]);
+    }
+
+}
+
+/** @} */

+ 44 - 0
modules/boonex/events/updates/14.0.5_14.0.6/source/classes/BxEventsCronPruning.php

@@ -0,0 +1,44 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Events Events
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxEventsCronPruning extends BxBaseModGroupsCronPruning
+{
+    public function __construct()
+    {
+        $this->_sModule = 'bx_events';
+
+        parent::__construct();
+    }
+    
+    function processing()
+    {
+        parent::processing();
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sModule = 'bx_timeline';
+        $sMethod = 'get_events_by_descriptor';
+        if(($iInterval = (int)getParam($CNF['PARAM_DELETE_FROM_TIMELINE_AFTER'])) != 0 && bx_is_srv($sModule, $sMethod)) {
+            $iDateTo = time() - 3600 * $iInterval;
+            $aEntries = $this->_oModule->_oDb->getEntriesBy(['type' => 'past', 'date_from' => $iDateTo - 86400, 'date_to' => $iDateTo]);
+            foreach($aEntries as $aEntry) {
+                $aEvent = bx_srv($sModule, $sMethod, [$this->_sModule, 'added', $aEntry[$CNF['FIELD_ID']]]);
+
+                if(empty($aEvent) || !is_array($aEvent))
+                    continue;
+
+                bx_srv($sModule, 'delete_entity', [$aEvent['id']]);
+            }
+        }
+    }
+}
+
+/** @} */

+ 315 - 0
modules/boonex/events/updates/14.0.5_14.0.6/source/classes/BxEventsDb.php

@@ -0,0 +1,315 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Events Events
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Groups module database queries
+ */
+class BxEventsDb extends BxBaseModGroupsDb
+{
+    public function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+    }
+
+    public function getContentInfoByIntervalId ($iIntervalId)
+    {
+        $iContentId = $this->getOne("SELECT `event_id` FROM `bx_events_intervals` WHERE `interval_id` = :interval", array(
+            'interval' => $iIntervalId,
+        ));
+        if (!$iContentId)
+            return 0;
+        return $this->getContentInfoById ($iContentId);
+    }
+
+    public function getContentInfoById ($iContentId)
+    {
+        $sQuery = $this->prepare ("SELECT `c`.*, `p`.`account_id`, `p`.`id` AS `profile_id`, `p`.`status` AS `profile_status`, MAX(`i`.`repeat_stop`) AS `repeat_stop` FROM `" . $this->_oConfig->CNF['TABLE_ENTRIES'] . "` AS `c` 
+            INNER JOIN `sys_profiles` AS `p` ON (`p`.`content_id` = `c`.`id` AND `p`.`type` = ?) 
+            LEFT JOIN `bx_events_intervals` as `i` ON (`c`.`id` = `i`.`event_id`)
+            WHERE `c`.`id` = ?
+            GROUP BY `c`.`id`", $this->_oConfig->getName(), $iContentId);
+        return $this->getRow($sQuery);
+    }
+
+    public function deleteIntervalById ($iIntervalId)
+    {
+        return $this->query("DELETE FROM `bx_events_intervals` WHERE `interval_id` = :interval", array(
+            'interval' => $iIntervalId,
+        ));
+    }
+    
+    public function getIntervals ($iContentId) 
+    {
+        return $this->getAllWithKey("SELECT * FROM `bx_events_intervals` WHERE `event_id` = :event_id", 'interval_id', array(
+            'event_id' => $iContentId,
+        ));
+    }
+
+    public function getEntriesBy($aParams = [])
+    {
+        if(isset($aParams['type']) && !in_array($aParams['type'], ['past']))
+            return parent::getEntriesBy($aParams); 
+            
+        $CNF = &$this->_oConfig->CNF;
+
+    	$aMethod = ['name' => 'getAll', 'params' => [0 => 'query', 1 => []]];
+        $sSelectClause = $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        $sSelectClause = "`" . $CNF['TABLE_ENTRIES'] . "`.*";
+
+        switch($aParams['type']) {
+            case 'past':
+                if(!empty($aParams['date_from'])) {
+                    $aMethod['params'][1]['date_from'] = (int)$aParams['date_from'];
+
+                    $sWhereClause .= " AND `" . $CNF['TABLE_ENTRIES'] . "`.`" . $CNF['FIELD_DATE_END'] . "` >= :date_from";
+                }
+
+                $aMethod['params'][1]['date_to'] = !empty($aParams['date_to']) ? (int)$aParams['date_to'] : time();
+
+                $sWhereClause .= " AND `" . $CNF['TABLE_ENTRIES'] . "`.`" . $CNF['FIELD_DATE_END'] . "` < :date_to";
+                break;
+        }
+
+        if(!empty($sOrderClause))
+            $sOrderClause = 'ORDER BY ' . $sOrderClause;
+
+        if(!empty($sLimitClause))
+            $sLimitClause = 'LIMIT ' . $sLimitClause;
+
+        $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 getEntriesByDate($sDateFrom, $sDateTo, $iEventId = 0, $aSQLPart = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        // validate input data
+        if (false === ($oDateFrom = date_create($sDateFrom, new DateTimeZone('UTC'))))
+            return array();
+        if (false === ($oDateTo = date_create($sDateTo, new DateTimeZone('UTC'))))
+            return array();
+        if ($oDateFrom > $oDateTo)
+            return array();
+
+        $sModule = $this->_oConfig->getName();
+        $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
+
+        // increase start and end date to cover timezones
+        $oDateFrom = $oDateFrom->sub(new DateInterval("P1D"));
+        $oDateTo = $oDateTo->add(new DateInterval("P1D"));
+
+        // look throught all days in the interval
+        $oDateIter = clone($oDateFrom);
+        $aEntries = array();
+        while ($oDateIter->format('Y-m-d') != $oDateTo->format('Y-m-d')) {
+
+            $oDateMin = date_create($oDateIter->format('Y-m-d') . '00:00:00', new DateTimeZone('UTC'));
+            $oDateMax = date_create($oDateIter->format('Y-m-d') . '23:59:59', new DateTimeZone('UTC'));
+                
+            // get all events for the specific day            
+            $oDateMonthBegin = date_create($oDateIter->format('Y-m-01'), new DateTimeZone('UTC'));
+            $iWeekOfMonth = $oDateIter->format('W') - $oDateMonthBegin->format('W') + 1;
+            $aBindings = array(
+                'timestamp_min' => $oDateMin->getTimestamp(),
+                'timestamp_max' => $oDateMax->getTimestamp(),
+            );
+            $aBindingsRepeating = array(
+                'timestamp_min' => $oDateMin->getTimestamp(),
+                'timestamp_max' => $oDateMax->getTimestamp(),
+                'year_start' => $oDateFrom->format('Y'),
+                'year' => $oDateIter->format('Y'),
+                'month' => $oDateIter->format('n'),
+                'week_of_month' => $iWeekOfMonth > 0 ? $iWeekOfMonth : $oDateIter->format('W') + 1,
+                'day_of_month' => $oDateIter->format('j'),
+                'day_of_week' => $oDateIter->format('N'),
+            );
+            $sJoin = isset($aSQLPart['join']) ? $aSQLPart['join'] : '';
+            $sWhere = isset($aSQLPart['where']) ? $aSQLPart['where'] : '';
+            if ((int)$iEventId) {
+                $aBindings['event'] = (int)$iEventId;
+                $aBindingsRepeating['event'] = (int)$iEventId;
+                $sWhere .= " AND `bx_events_data`.`id` = :event ";
+            }
+
+            // search for regular events
+            $a = $this->getAllWithKey("SELECT DISTINCT `bx_events_data`.`id`, `bx_events_data`.`event_name` AS `title`, `bx_events_data`.`event_desc` AS `description`, 
+            `bx_events_data`.`threshold` AS `threshold`, `bx_events_data`.`date_start`, `bx_events_data`.`date_end`, `bx_events_data`.`timezone`, `bx_events_data`.`reminder`, 0 AS `repeating`
+                FROM `bx_events_data` $sJoin 
+                WHERE `bx_events_data`.`status`='active' AND `bx_events_data`.`status_admin`='active' AND `bx_events_data`.`date_start` >= :timestamp_min AND `bx_events_data`.`date_start` <= :timestamp_max $sWhere
+            ", 'id', $aBindings);
+
+            if ($a)
+                $sWhere .= " AND `bx_events_data`.`id` NOT IN(" . $this->implode_escape(array_keys($a)) . ") ";
+
+            $aRepeating = $this->getAllWithKey("SELECT DISTINCT `bx_events_data`.`id`, `bx_events_data`.`event_name` AS `title`, `bx_events_data`.`event_desc` AS `description`, `bx_events_data`.`threshold` AS `threshold`, `bx_events_data`.`date_start`, `bx_events_data`.`date_end`, `bx_events_data`.`timezone`, `bx_events_data`.`reminder`, 1 AS `repeating`
+                FROM `bx_events_data`
+                LEFT JOIN `bx_events_intervals` AS `i` ON (
+                    `bx_events_data`.`id` = `i`.`event_id`
+                    AND
+                    `bx_events_data`.`date_start` < :timestamp_max
+                    AND
+                    (0 = `i`.`repeat_stop` OR `i`.`repeat_stop` >= :timestamp_min)
+                    AND (
+                        (0 = `i`.`repeat_year` OR 0 = (:year - :year_start) % `i`.`repeat_year`)
+                        AND 
+                        (0 = `i`.`repeat_month` OR :month = `i`.`repeat_month`)
+                        AND 
+                        (0 = `i`.`repeat_week_of_month` OR :week_of_month = `i`.`repeat_week_of_month`)
+                        AND 
+                        (0 = `i`.`repeat_day_of_month` OR :day_of_month = `i`.`repeat_day_of_month`)
+                        AND 
+                        (0 = `i`.`repeat_day_of_week` OR :day_of_week = `i`.`repeat_day_of_week`)
+                    )
+                ) $sJoin 
+                WHERE `bx_events_data`.`status`='active' AND `bx_events_data`.`status_admin`='active' AND `i`.`interval_id` IS NOT NULL $sWhere
+            ", 'id', $aBindingsRepeating);
+
+            $a = array_merge($aRepeating ? $aRepeating : array(), $a ? $a : array());
+
+            // prepare variables for each event            
+            foreach ($a as $k => $r) {
+                $oDateStart = new DateTime('@' . $r['date_start']);
+                $oDateEnd = new DateTime('@' . $r['date_end']);
+                $oDuration = $oDateStart->diff($oDateEnd);
+
+                if ($r['repeating']) {
+                    // since repeating interval values are stored in specific timezones then we need to convert it
+                    $oTz = new DateTimeZone($r['timezone'] ? $r['timezone'] : 'UTC');
+                    $sHoursStart = $oDateStart->format('H:i:s');
+                    $iSecondsStart = (clone($oDateStart))->setTimezone($oTz)->format('H') * 3600 + $oDateStart->format('i') * 60 +  $oDateStart->format('s');
+                    $iSecondsOffset = $oTz->getOffset($oDateIter);
+
+                    $sCurrentDay = $oDateIter->format('Y-m-d');
+                    if ($iSecondsOffset > 0 && $iSecondsOffset > $iSecondsStart)
+                        $sCurrentDay = (clone($oDateIter))->sub(new DateInterval("P1D"))->format('Y-m-d');
+                    if ($iSecondsOffset < 0 && -$iSecondsOffset > (86400 - $iSecondsStart))
+                        $sCurrentDay = (clone($oDateIter))->add(new DateInterval("P1D"))->format('Y-m-d');
+
+                    $oStart = date_create($sCurrentDay . ' ' . $sHoursStart);
+                    $oEnd = $oStart ? clone($oStart) : null;
+                    $oEnd = $oEnd ? $oEnd->add($oDuration) : null;
+                } 
+                else {
+                    $oStart = $oDateStart;
+                    $oEnd = $oDateEnd;
+                }
+
+                $a[$k]['start'] = $oStart ? $oStart->format('c') : 0;
+                $a[$k]['end'] = $oEnd ? $oEnd->format('c') : 0;
+                $a[$k]['start_utc'] = $oDateStart ? $oDateStart->getTimestamp() : 0;
+                $a[$k]['end_utc'] = $oDateEnd ? $oDateEnd->getTimestamp() : 0;
+                $a[$k]['url'] = bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $this->_oConfig->CNF['URI_VIEW_ENTRY'] . '&id=' . $r['id']));
+                if(bx_is_api()) {
+                    $a[$k]['url'] = bx_api_get_relative_url($a[$k]['url']);
+                    $a[$k]['location_data'] = $oMetatags->locationGet($r['id']);
+                    $a[$k]['location'] = $oMetatags->locationsString($r['id'], false);
+                }
+                else {
+                    if(($oEvent = BxDolProfile::getInstanceByContentAndType($r['id'], $sModule)) !== false)
+                        $a[$k]['extendedProps'] = [
+                            'class' => 'bx-events-calendar-unit',
+                            'card' => $oEvent->getUnit()
+                        ];
+                }
+            }
+
+            // merge with all other events
+            $aEntries = array_merge($aEntries, $a);
+
+            // go to the next day
+            $oDateIter = $oDateIter->add(new DateInterval("P1D"));
+        }
+
+        return $aEntries;
+    }
+
+    public function checkIn($iProfileId, $iId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return $this->query("INSERT INTO `" . $CNF['TABLE_CHECK_IN'] . "` SET `profile_id` = :profile_id, `event_id` = :event_id ON DUPLICATE KEY UPDATE `event_id` = :event_id", [
+            'profile_id' => $iProfileId,
+            'event_id' => $iId
+        ]) !== false;
+    }
+    
+    public function isCheckedIn($iProfileId, $iId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return (int)$this->getOne("SELECT `id` FROM `" . $CNF['TABLE_CHECK_IN'] . "` WHERE `profile_id` = :profile_id AND `event_id` = :event_id LIMIT 1", [
+            'profile_id' => $iProfileId,
+            'event_id' => $iId
+        ]) > 0;
+    }
+
+    public function getSessions($aParams = [])
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+
+        $sSelectClause = "`ts`.*";
+        $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+        switch($aParams['sample']) {
+            case 'id':
+                $aMethod['name'] = 'getRow';
+                $aMethod['params'][1] = [
+                    'id' => $aParams['id']
+                ];
+
+                $sWhereClause = " AND `ts`.`id`=:id";
+                break;
+            
+             case 'event_id':
+                $aMethod['params'][1] = [
+                    'event_id' => $aParams['event_id']
+                ];
+
+                $sWhereClause = " AND `ts`.`event_id`=:event_id";
+                $sOrderClause = "`ts`.`" . !empty($aParams['order_by']) ? $aParams['order_by'] : "order" . "` ASC";
+                break;
+        }
+
+        if(!empty($sOrderClause))
+            $sOrderClause = 'ORDER BY ' . $sOrderClause;
+
+        if(!empty($sLimitClause))
+            $sLimitClause = 'LIMIT ' . $sLimitClause;
+
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " FROM `" . $CNF['TABLE_SESSIONS'] . "` AS `ts` " . $sJoinClause . " WHERE 1" . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+
+        return call_user_func_array([$this, $aMethod['name']], $aMethod['params']);
+    }
+
+    public function getSessionOrderMax($iEventId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return (int)$this->getOne("SELECT MAX(`order`) FROM `" . $CNF['TABLE_SESSIONS'] . "` WHERE `event_id`=:event_id", [
+            'event_id' => $iEventId
+        ]);
+    }
+    
+    public function deleteSessions($aWhere)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aWhere) || !is_array($aWhere))
+            return false;
+
+        return (int)$this->query("DELETE FROM `" . $CNF['TABLE_SESSIONS'] . "` WHERE " . $this->arrayToSQL($aWhere, ' AND ')) > 0;
+    }
+}
+
+/** @} */

+ 152 - 0
modules/boonex/events/updates/14.0.5_14.0.6/source/install/config.php

@@ -0,0 +1,152 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Events Events
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_events',
+    'title' => 'Events',
+    'note' => 'Events functionality.',
+    'version' => '14.0.6',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/events/',
+    'home_uri' => 'events',
+
+    'db_prefix' => 'bx_events_',
+    'class_prefix' => 'BxEvents',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Events',
+
+    /**
+     * Connections.
+     */
+    'connections' => array(
+    	'sys_profiles_friends' => array ('type' => 'profiles'),
+		'sys_profiles_subscriptions' => array ('type' => 'profiles'),
+    ),
+
+    /**
+     * Menu triggers.
+     */
+    'menu_triggers' => array(
+        'trigger_profile_view_submenu',
+        'trigger_group_view_submenu',
+        'trigger_group_snippet_meta',
+        'trigger_group_view_actions',
+    ),
+
+    /**
+     * Page triggers.
+     */
+    'page_triggers' => array (
+    	'trigger_page_group_view_entry', 
+    ),
+
+	/**
+     * Storage objects to automatically delete files from upon module uninstallation.
+     * Note. Don't add storage objects used in transcoder objects.
+     */
+    'storages' => array(
+    	'bx_events_pics'
+    ),
+
+	/**
+     * Transcoders.
+     */
+    'transcoders' => array(
+    	'bx_events_icon', 
+    	'bx_events_thumb', 
+    	'bx_events_avatar', 
+        'bx_events_avatar_big', 
+    	'bx_events_picture', 
+    	'bx_events_cover', 
+    	'bx_events_cover_thumb',
+    	'bx_events_gallery'
+    ),
+
+    /**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_events',
+    	'bx_events_cmts',
+    ),
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+    	'process_esearches' => 1,
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    	'process_connections' => 1,
+        'process_deleted_profiles' => 1,
+    	'update_relations' => 1,        
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable_success' => array(
+    	'process_menu_triggers' => 1,
+    	'process_page_triggers' => 1,
+    	'process_esearches' => 1,
+    	'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'unregister_transcoders' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+    	'register_transcoders' => 1,
+    	'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Connections Section
+     */
+    'relations' => array(
+        'bx_timeline',
+        'bx_notifications',
+    ),
+);
+
+/** @} */

+ 693 - 0
modules/boonex/events/updates/14.0.5_14.0.6/source/install/langs/en.xml

@@ -0,0 +1,693 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string name="_bx_events"><![CDATA[Events]]></string>
+    <string name="_bx_events_cmts"><![CDATA[Events' Comments]]></string>
+
+    <string name="_bx_events_option_enable_auto_approve"><![CDATA[Auto approve an event after creation]]></string>
+    <string name="_bx_events_option_num_connections_quick"><![CDATA[Number of items in friends block]]></string>
+    <string name="_bx_events_option_per_page_browse"><![CDATA[Number of items per page]]></string>
+    <string name="_bx_events_option_num_rss"><![CDATA[Number of items in RSS feed]]></string>
+    <string name="_bx_events_option_time_format"><![CDATA[Time format]]></string>
+    <string name="_bx_events_option_short_date_format"><![CDATA[Short date format]]></string>
+    <string name="_bx_events_option_datetime_format"><![CDATA[Date and time format]]></string>
+    <string name="_bx_events_option_summary_chars"><![CDATA[Number of characters in auto-cropped event summary]]></string>
+    <string name="_bx_events_option_searchable_fields"><![CDATA[Searchable fields]]></string>
+    <string name="_bx_events_option_members_mode"><![CDATA[Special mode for event members]]></string>
+    <string name="_bx_events_option_members_mode_multi_roles"><![CDATA[Multi Roles]]></string>
+    <string name="_bx_events_option_reminder_interval"><![CDATA[Reminder Interval]]></string>
+    <string name="_bx_events_option_public_subscribed_me"><![CDATA[Public list of interested members]]></string>
+    <string name="_bx_events_option_per_page_for_favorites_lists"><![CDATA[Number of favorite lists per page]]></string>
+    <string name="_bx_events_option_enable_subscribe_wo_join"><![CDATA[Allow to mark as Interested without joining (marking as Going)]]></string>
+    <string name="_bx_events_option_internal_notifications"><![CDATA[Use internal notifications]]></string>
+    <string name="_bx_events_option_enable_subscribe_for_past_events"><![CDATA[Enable join for past events]]></string>
+    <string name="_bx_events_option_rm_from_timeline_after"><![CDATA[Delete past events from Timeline after N hours (0 - don't delete)]]></string>
+
+    <string name="_bx_events_form_profile"><![CDATA[Events]]></string>
+    <string name="_bx_events_form_profile_display_add"><![CDATA[Add Event]]></string>
+    <string name="_bx_events_form_profile_display_edit"><![CDATA[Edit Event]]></string>
+    <string name="_bx_events_form_profile_display_edit_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_events_form_profile_display_delete"><![CDATA[Delete Event]]></string>
+    <string name="_bx_events_form_profile_display_view"><![CDATA[View Event]]></string>
+    <string name="_bx_events_form_profile_display_view_full"><![CDATA[Full View Event]]></string>
+    <string name="_bx_events_form_profile_display_invite"><![CDATA[Invite to Event]]></string>
+    <string name="_bx_events_form_profile_input_author"><![CDATA[Author]]></string>
+    <string name="_bx_events_form_profile_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_events_form_profile_input_sys_join_confirm"><![CDATA[Join confirmation]]></string>
+    <string name="_bx_events_form_profile_input_join_confirm"><![CDATA[Approval required to join]]></string>
+    <string name="_bx_events_form_profile_input_sys_allow_view_to"><![CDATA[Visible to]]></string>
+    <string name="_bx_events_form_profile_input_allow_view_to"><![CDATA[Visible to]]></string>
+    <string name="_bx_events_form_profile_input_allow_view_favorite_list"><![CDATA[Visibility]]></string>
+    <string name="_bx_events_form_profile_input_allow_view_to_desc"><![CDATA[Closed - anyone can find the event and see who's in it, only members can see posts. Secret - only members can find the event and see posts.]]></string>
+    <string name="_bx_events_form_profile_input_sys_allow_post_to"><![CDATA[Who can post]]></string>
+    <string name="_bx_events_form_profile_input_allow_post_to"><![CDATA[Who can post]]></string>
+    <string name="_bx_events_form_profile_input_sys_initial_members"><![CDATA[Invite Members]]></string>
+    <string name="_bx_events_form_profile_input_initial_members"><![CDATA[Invite Members]]></string>
+    <string name="_bx_events_form_profile_input_sys_event_name"><![CDATA[Event Name]]></string>
+    <string name="_bx_events_form_profile_input_event_name"><![CDATA[Event Name]]></string>
+    <string name="_bx_events_form_profile_input_event_name_err"><![CDATA[Field must contain 1 - 80 characters.]]></string>
+    <string name="_bx_events_form_profile_input_sys_event_desc"><![CDATA[Description]]></string>
+    <string name="_bx_events_form_profile_input_event_desc"><![CDATA[Description]]></string>
+    <string name="_bx_events_form_profile_input_sys_event_cat"><![CDATA[Category]]></string>
+    <string name="_bx_events_form_profile_input_event_cat"><![CDATA[Category]]></string>
+    <string name="_bx_events_form_profile_input_event_cat_err"><![CDATA[Category field is mandatory]]></string>
+    <string name="_bx_events_form_profile_input_sys_hashtag"><![CDATA[Main hashtag]]></string>
+    <string name="_bx_events_form_profile_input_hashtag"><![CDATA[Main hashtag]]></string>
+    <string name="_bx_events_form_profile_input_sys_picture"><![CDATA[Event Picture]]></string>
+    <string name="_bx_events_form_profile_input_picture"><![CDATA[Upload New Event Picture]]></string>
+    <string name="_bx_events_form_profile_input_picture_err"><![CDATA[Event picture is mandatory.]]></string>
+    <string name="_bx_events_form_profile_input_sys_cover"><![CDATA[Cover Picture]]></string>
+    <string name="_bx_events_form_profile_input_cover"><![CDATA[Upload New Cover Picture]]></string>
+    <string name="_bx_events_form_profile_input_sys_delete_confirm"><![CDATA[Delete confirmation]]></string>
+    <string name="_bx_events_form_profile_input_delete_confirm"><![CDATA[I am sure I want to delete this event]]></string>
+    <string name="_bx_events_form_profile_input_delete_confirm_info"><![CDATA[This action can not be undone.]]></string>
+    <string name="_bx_events_form_profile_input_delete_confirm_error"><![CDATA[Please check this checkbox if you really want to delete event.]]></string>
+    <string name="_bx_events_form_profile_input_sys_reoccurring"><![CDATA[Reoccurring]]></string>
+    <string name="_bx_events_form_profile_input_reoccurring"><![CDATA[Reoccurring]]></string>
+    <string name="_bx_events_form_profile_input_reoccurring_add_interval"><![CDATA[Add repeating interval]]></string>
+    <string name="_bx_events_form_profile_input_sys_date_added"><![CDATA[Created]]></string>
+    <string name="_bx_events_form_profile_input_date_added"><![CDATA[Created]]></string>
+    <string name="_bx_events_form_profile_input_sys_date_changed"><![CDATA[Updated]]></string>
+    <string name="_bx_events_form_profile_input_date_changed"><![CDATA[Updated]]></string>
+    <string name="_bx_events_form_profile_input_sys_date_published"><![CDATA[Publishing time]]></string>
+    <string name="_bx_events_form_profile_input_date_published"><![CDATA[Publishing time]]></string>
+    <string name="_bx_events_form_profile_input_date_published_info"><![CDATA[Leave empty to publish immediately.]]></string>
+    <string name="_bx_events_form_profile_input_sys_date_start"><![CDATA[Date start]]></string>
+    <string name="_bx_events_form_profile_input_date_start"><![CDATA[Date start]]></string>
+    <string name="_bx_events_form_profile_input_date_start_err"><![CDATA[Start date is mandatory]]></string>
+    <string name="_bx_events_form_profile_input_sys_date_end"><![CDATA[Date end]]></string>
+    <string name="_bx_events_form_profile_input_date_end"><![CDATA[Date end]]></string>    
+    <string name="_bx_events_form_profile_input_date_end_err"><![CDATA[End date is mandatory]]></string>
+    <string name="_bx_events_form_profile_input_date_end_invalid_err"><![CDATA[End date must be greater than start date]]></string>
+    <string name="_bx_events_form_profile_input_sys_timezone"><![CDATA[Timezone]]></string>
+    <string name="_bx_events_form_profile_input_timezone"><![CDATA[Timezone]]></string>
+    <string name="_bx_events_form_profile_input_sys_time"><![CDATA[Time]]></string>
+    <string name="_bx_events_form_profile_input_time"><![CDATA[Time]]></string>
+    <string name="_bx_events_form_profile_input_sys_reminder"><![CDATA[Reminder (part of Internal Notifications)]]></string>
+    <string name="_bx_events_form_profile_input_reminder"><![CDATA[Reminder]]></string>
+    <string name="_bx_events_form_profile_input_sys_threshold"><![CDATA[Threshold radius]]></string>
+    <string name="_bx_events_form_profile_input_threshold"><![CDATA[Threshold radius (in meters)]]></string>
+    <string name="_bx_events_form_input_repeat_year"><![CDATA[Repeat year]]></string>
+    <string name="_bx_events_form_input_repeat_month"><![CDATA[Repeat month]]></string>
+    <string name="_bx_events_form_input_repeat_week_of_month"><![CDATA[Repeat week of month]]></string>
+    <string name="_bx_events_form_input_repeat_day_of_month"><![CDATA[Repeat day of month]]></string>
+    <string name="_bx_events_form_input_repeat_day_of_week"><![CDATA[Repeat day of week]]></string>
+    <string name="_bx_events_form_input_stop_repeating"><![CDATA[Stop repeating]]></string>
+
+    <string name="_bx_events_form_question"><![CDATA[Question]]></string>
+    <string name="_bx_events_form_question_display_add"><![CDATA[Add Question]]></string>
+    <string name="_bx_events_form_question_display_edit"><![CDATA[Edit Question]]></string>
+    <string name="_bx_events_form_question_input_sys_action"><![CDATA[Action]]></string>
+    <string name="_bx_events_form_question_input_sys_question"><![CDATA[Question]]></string>
+    <string name="_bx_events_form_question_input_question"><![CDATA[Question]]></string>
+    <string name="_bx_events_form_question_input_question_err"><![CDATA[Question is mandatory.]]></string>
+    <string name="_bx_events_form_question_input_sys_answer"><![CDATA[Answer]]></string>
+    <string name="_bx_events_form_question_input_sys_controls"><![CDATA[Controls]]></string>
+    <string name="_bx_events_form_question_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_events_form_question_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_events_form_question_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_events_form_question_input_do_cancel"><![CDATA[Cancel]]></string>
+
+    <string name="_bx_events_form_questionnaire_input_question_err"><![CDATA[Please, answer the question.]]></string>
+
+    <string name="_bx_events_form_session"><![CDATA[Session]]></string>
+    <string name="_bx_events_form_session_display_add"><![CDATA[Add Session]]></string>
+    <string name="_bx_events_form_session_display_edit"><![CDATA[Edit Session]]></string>
+    <string name="_bx_events_form_session_input_sys_title"><![CDATA[Title]]></string>
+    <string name="_bx_events_form_session_input_title"><![CDATA[Title]]></string>
+    <string name="_bx_events_form_session_input_title_err"><![CDATA[Session title is mandatory.]]></string>
+    <string name="_bx_events_form_session_input_sys_description"><![CDATA[Description]]></string>
+    <string name="_bx_events_form_session_input_description"><![CDATA[Description]]></string>
+    <string name="_bx_events_form_session_input_sys_date_start"><![CDATA[Date start]]></string>
+    <string name="_bx_events_form_session_input_date_start"><![CDATA[Date start]]></string>
+    <string name="_bx_events_form_session_input_date_start_err"><![CDATA[Start date is mandatory]]></string>
+    <string name="_bx_events_form_session_input_sys_date_end"><![CDATA[Date end]]></string>
+    <string name="_bx_events_form_session_input_date_end"><![CDATA[Date end]]></string>
+    <string name="_bx_events_form_session_input_date_end_err"><![CDATA[End date is mandatory]]></string>
+    <string name="_bx_events_form_session_input_sys_controls"><![CDATA[Controls]]></string>
+    <string name="_bx_events_form_session_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_events_form_session_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_events_form_session_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_events_form_session_input_do_cancel"><![CDATA[Cancel]]></string>
+
+    <string name="_bx_events_form_price"><![CDATA[Price]]></string>
+    <string name="_bx_events_form_price_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_events_form_price_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_events_form_price_input_sys_id"><![CDATA[ID]]></string>
+    <string name="_bx_events_form_price_input_sys_role_id"><![CDATA[Role ID]]></string>
+    <string name="_bx_events_form_price_input_sys_name"><![CDATA[Name]]></string>
+    <string name="_bx_events_form_price_input_name"><![CDATA[Name]]></string>
+    <string name="_bx_events_form_price_input_inf_name"><![CDATA[Unique value which will be used in back end and API calls]]></string>
+    <string name="_bx_events_form_price_input_err_name"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_events_form_price_input_sys_period"><![CDATA[Period]]></string>
+    <string name="_bx_events_form_price_input_period"><![CDATA[Period]]></string>
+    <string name="_bx_events_form_price_input_inf_period"><![CDATA[Use 0 for lifetime]]></string>
+    <string name="_bx_events_form_price_input_sys_period_unit"><![CDATA[Period unit]]></string>
+    <string name="_bx_events_form_price_input_period_unit"><![CDATA[Period unit]]></string>
+    <string name="_bx_events_form_price_input_inf_period_unit"><![CDATA[Leave empty for lifetime level.]]></string>
+    <string name="_bx_events_form_price_input_err_period_unit"><![CDATA[You need to select a period unit here.]]></string>
+    <string name="_bx_events_form_price_input_sys_price"><![CDATA[Price]]></string>
+    <string name="_bx_events_form_price_input_price"><![CDATA[Price]]></string>
+    <string name="_bx_events_form_price_input_inf_price"><![CDATA[Use 0 or leave empty to create free level.]]></string>
+    <string name="_bx_events_form_price_input_sys_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_events_form_price_input_do_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_events_form_price_input_sys_do_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_events_form_price_input_do_cancel"><![CDATA[Cancel]]></string>
+
+    <string name="_bx_events_form_filters_input_by_location"><![CDATA[By location]]></string>
+    <string name="_bx_events_form_filters_input_by_date"><![CDATA[By date]]></string>
+    <string name="_bx_events_form_filters_input_by_date_all"><![CDATA[Any date]]></string>
+    <string name="_bx_events_form_filters_input_by_date_today"><![CDATA[Today]]></string>
+    <string name="_bx_events_form_filters_input_by_date_tomorrow"><![CDATA[Tomorrow]]></string>
+    <string name="_bx_events_form_filters_input_by_date_weekend"><![CDATA[This weekend]]></string>
+    <string name="_bx_events_form_filters_input_by_date_this_week"><![CDATA[This week]]></string>
+    <string name="_bx_events_form_filters_input_by_date_next_week"><![CDATA[Next week]]></string>
+    <string name="_bx_events_form_filters_input_by_date_this_month"><![CDATA[This month]]></string>
+    <string name="_bx_events_form_filters_input_by_date_date_range"><![CDATA[Custom date range]]></string>
+    <string name="_bx_events_form_filters_input_do_apply"><![CDATA[Apply]]></string>
+
+
+    <string name="_bx_events_acl_action_create_profile"><![CDATA[Create Event]]></string>
+    <string name="_bx_events_acl_action_use_paid_join"><![CDATA[Use paid join]]></string>
+    <string name="_bx_events_acl_action_delete_profile"><![CDATA[Delete Event]]></string>
+    <string name="_bx_events_acl_action_view_profile"><![CDATA[View Event]]></string>
+    <string name="_bx_events_acl_action_edit_any_profile"><![CDATA[Edit Any Event]]></string>
+    <string name="_bx_events_acl_action_delete_any_profile"><![CDATA[Delete Any Event]]></string>
+    <string name="_bx_events_acl_action_delete_invites"><![CDATA[Delete Invites]]></string>
+
+    <string name="_bx_events_page_title_sys_joined"><![CDATA[Profile Events]]></string>
+    <string name="_bx_events_page_title_joined"><![CDATA[Profile Events]]></string>
+    <string name="_bx_events_page_title_sys_joined_entries"><![CDATA[Profile Events]]></string>
+    <string name="_bx_events_page_title_followed"><![CDATA[Events I follow]]></string>
+    <string name="_bx_events_page_title_sys_followed"><![CDATA[Events I follow]]></string>
+    <string name="_bx_events_page_title_joined_entries"><![CDATA[Profile Events]]></string>
+    <string name="_bx_events_page_title_sys_entries_in_context"><![CDATA[Events in context]]></string>
+    <string name="_bx_events_page_title_entries_in_context"><![CDATA[Events in {display_name}]]></string>
+    <string name="_bx_events_page_title_sys_create_profile"><![CDATA[Create Event]]></string>
+    <string name="_bx_events_page_title_create_profile"><![CDATA[Create Event]]></string>
+    <string name="_bx_events_page_title_sys_edit_profile"><![CDATA[Edit Event]]></string>
+    <string name="_bx_events_page_title_edit_profile"><![CDATA[Edit Event]]></string>
+    <string name="_bx_events_page_title_sys_edit_profile_cover"><![CDATA[Edit Event Cover]]></string>
+    <string name="_bx_events_page_title_edit_profile_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_events_page_title_sys_questionnaire"><![CDATA[Questionnaire]]></string>
+    <string name="_bx_events_page_title_questionnaire"><![CDATA[Questionnaire]]></string>
+    <string name="_bx_events_page_title_manage_profile"><![CDATA[Manage]]></string>
+    <string name="_bx_events_page_title_sys_manage_profile"><![CDATA[Manage Event]]></string>
+    <string name="_bx_events_page_title_sys_delete_profile"><![CDATA[Delete Event]]></string>
+    <string name="_bx_events_page_title_delete_profile"><![CDATA[Delete Event]]></string>
+    <string name="_bx_events_page_title_sys_join_profile"><![CDATA[Join Event]]></string>
+    <string name="_bx_events_page_title_join_profile"><![CDATA[Join Event]]></string>
+    <string name="_bx_events_page_title_sys_view_profile_closed"><![CDATA[View Closed Event]]></string>
+    <string name="_bx_events_page_title_sys_view_profile"><![CDATA[View Event]]></string>
+    <string name="_bx_events_page_title_view_profile"><![CDATA[{display_name}]]></string>
+    <string name="_bx_events_page_title_browse"><![CDATA[Events]]></string>
+    <string name="_bx_events_page_title_browse_by_author"><![CDATA[Events of {display_name}]]></string>
+    <string name="_bx_events_page_title_browse_by_context"><![CDATA[Events in {display_name}]]></string>
+    <string name="_bx_events_page_title_browse_featured"><![CDATA[Featured Events]]></string>
+    <string name="_bx_events_page_title_browse_top"><![CDATA[Top Events]]></string>
+    <string name="_bx_events_page_title_browse_upcoming"><![CDATA[Upcoming Events]]></string>
+    <string name="_bx_events_page_title_browse_past"><![CDATA[Past Events]]></string>
+    <string name="_bx_events_page_title_browse_recent"><![CDATA[New Events]]></string>
+    <string name="_bx_events_page_title_browse_connections"><![CDATA[{0} connections]]></string>
+    <string name="_bx_events_page_title_browse_connections_mutual"><![CDATA[{0} and {1} mutual connections]]></string>
+    <string name="_bx_events_page_title_sys_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_events_page_title_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_events_page_title_sys_group_fans"><![CDATA[Event Guests]]></string>
+    <string name="_bx_events_page_title_group_fans"><![CDATA[Going]]></string>
+    <string name="_bx_events_page_title_sys_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_events_page_title_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_events_page_title_sys_profile_info"><![CDATA[Event Info]]></string>
+    <string name="_bx_events_page_title_profile_info"><![CDATA[Info about {display_name}]]></string>
+    <string name="_bx_events_page_title_sys_profile_sessions"><![CDATA[Sessions]]></string>
+    <string name="_bx_events_page_title_profile_sessions"><![CDATA[Sessions]]></string>
+    <string name="_bx_events_page_title_sys_profile_pricing"><![CDATA[Pricing]]></string>
+    <string name="_bx_events_page_title_profile_pricing"><![CDATA[Pricing]]></string>
+    <string name="_bx_events_page_title_sys_recent"><![CDATA[New Events]]></string>
+    <string name="_bx_events_page_title_recent"><![CDATA[New Events]]></string>
+    <string name="_bx_events_page_title_sys_manage"><![CDATA[Manage Own Events]]></string>
+    <string name="_bx_events_page_title_sys_manage_administration"><![CDATA[Manage All Events]]></string>
+    <string name="_bx_events_page_title_manage"><![CDATA[Manage Events]]></string>
+    <string name="_bx_events_page_title_sys_top"><![CDATA[Top Events]]></string>
+    <string name="_bx_events_page_title_top"><![CDATA[Top Events]]></string>
+    <string name="_bx_events_page_title_sys_upcoming"><![CDATA[Upcoming Events]]></string>
+    <string name="_bx_events_page_title_upcoming"><![CDATA[Upcoming Events]]></string>
+    <string name="_bx_events_page_title_sys_past"><![CDATA[Past Events]]></string>
+    <string name="_bx_events_page_title_past"><![CDATA[Past Events]]></string>
+    <string name="_bx_events_page_title_sys_calendar"><![CDATA[Events Calendar]]></string>
+    <string name="_bx_events_page_title_calendar"><![CDATA[Events Calendar]]></string>
+    <string name="_bx_events_page_title_sys_invite_to_group"><![CDATA[Invite Members to the Event]]></string>
+    <string name="_bx_events_page_title_invite_to_group"><![CDATA[Invite Members to the Event]]></string>
+    <string name="_bx_events_page_title_sys_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_events_page_title_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_events_page_title_sys_entries_favorites"><![CDATA[Favorites Events]]></string>
+    <string name="_bx_events_page_title_entries_favorites"><![CDATA[Favorites Events]]></string>
+
+    <string name="_bx_events_page_block_title_sys_entries_actions"><![CDATA[Events actions]]></string>
+    <string name="_bx_events_page_block_title_entries_actions"><![CDATA[Events actions]]></string>
+    <string name="_bx_events_page_block_title_entries_in_context"><![CDATA[Events in <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_events_page_block_title_sys_entries_in_context"><![CDATA[Events in context]]></string>
+    <string name="_bx_events_page_block_title_calendar_in_context"><![CDATA[Events Calendar in <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_events_page_block_title_sys_calendar_in_context"><![CDATA[Events Calendar]]></string>
+    <string name="_bx_events_page_block_title_calendar_for_context"><![CDATA[Events Calendar]]></string>
+    <string name="_bx_events_page_block_title_browse_for_context"><![CDATA[Events in context]]></string>
+    <string name="_bx_events_page_block_title_sys_favorites_entries"><![CDATA[Favorite List]]></string>
+    <string name="_bx_events_page_block_title_favorites_entries"><![CDATA["{title}" List]]></string>
+    <string name="_bx_events_page_block_title_favorites_entries_info"><![CDATA[Info]]></string>
+    <string name="_bx_events_page_block_title_favorites_entries_actions"><![CDATA[Favorites List Actions]]></string>
+    <string name="_bx_events_page_block_title_sys_entries_of_author"><![CDATA[Events of author]]></string>
+    <string name="_bx_events_page_block_title_entries_of_author"><![CDATA[Events by {display_name}]]></string>
+    <string name="_bx_events_page_block_title_favorites_of_author"><![CDATA[Favorite Events]]></string>
+    <string name="_bx_events_page_block_title_sys_favorites_of_author"><![CDATA[Favorite Events]]></string>
+    <string name="_bx_events_page_block_title_sys_joined_entries"><![CDATA[Joined Events]]></string>
+    <string name="_bx_events_page_block_title_joined_entries"><![CDATA[Joined Events]]></string>
+    <string name="_bx_events_page_block_title_sys_joined_calendar"><![CDATA[Joined Events Calendar]]></string>
+    <string name="_bx_events_page_block_title_joined_calendar"><![CDATA[Joined Events Calendar]]></string>
+    <string name="_bx_events_page_block_title_sys_followed_entries"><![CDATA[Following]]></string>
+    <string name="_bx_events_page_block_title_followed_entries"><![CDATA[Following]]></string>
+    <string name="_bx_events_page_block_title_create_profile"><![CDATA[Create Event]]></string>
+    <string name="_bx_events_page_block_title_edit_profile"><![CDATA[Edit Event]]></string>
+    <string name="_bx_events_page_block_title_edit_profile_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_events_page_block_title_edit_questionnaire"><![CDATA[Questionnaire]]></string>
+    <string name="_bx_events_page_block_title_delete_profile"><![CDATA[Delete Event]]></string>
+    <string name="_bx_events_page_block_title_join_profile"><![CDATA[Join Event]]></string>
+    <string name="_bx_events_page_block_title_profile_info_link"><![CDATA[About <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_events_page_block_title_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_events_page_block_title_system_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_events_page_block_title_profile_sessions"><![CDATA[Sessions]]></string>
+    <string name="_bx_events_page_block_title_profile_pricing"><![CDATA[Pricing]]></string>
+    <string name="_bx_events_page_block_title_profile_description"><![CDATA[Description]]></string>
+    <string name="_bx_events_page_block_title_profile_friends_link"><![CDATA[Friends of <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_events_page_block_title_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_events_page_block_title_system_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_events_page_block_title_admins"><![CDATA[Administrators]]></string>
+    <string name="_bx_events_page_block_title_fans_link"><![CDATA[Going to <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_events_page_block_title_fans"><![CDATA[Going]]></string>
+    <string name="_bx_events_page_block_title_system_fans"><![CDATA[Going]]></string>
+    <string name="_bx_events_page_block_title_system_fans_manage"><![CDATA[Manage fans]]></string>
+    <string name="_bx_events_page_block_title_invites"><![CDATA[Invites]]></string>
+    <string name="_bx_events_page_block_title_system_invites_manage"><![CDATA[Manage invites]]></string>
+    <string name="_bx_events_page_block_title_subscribers_link"><![CDATA[Interested in <a href="{profile_link}">{display_name}</a>]]></string>
+    <string name="_bx_events_page_block_title_subscribers"><![CDATA[Interested]]></string>
+    <string name="_bx_events_page_block_title_system_subscribers_manage"><![CDATA[Manage interested]]></string>
+    <string name="_bx_events_page_block_title_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_events_page_block_title_entry_reports"><![CDATA[Reports]]></string>
+    <string name="_bx_events_page_block_title_profile_comments_link"><![CDATA[Comments to <a href="{entry_link}">{title}</a>]]></string>
+    <string name="_bx_events_page_block_title_profile_actions"><![CDATA[Actions]]></string>
+    <string name="_bx_events_page_block_title_featured_profiles"><![CDATA[Featured Events]]></string>
+    <string name="_bx_events_page_block_title_latest_profiles"><![CDATA[New Events]]></string>
+    <string name="_bx_events_page_block_title_system_manage"><![CDATA[Manage Own Events]]></string>
+    <string name="_bx_events_page_block_title_system_manage_administration"><![CDATA[Manage All Events]]></string>
+    <string name="_bx_events_page_block_title_manage"><![CDATA[Manage Events]]></string>
+    <string name="_bx_events_page_block_title_top_profiles"><![CDATA[Top Events]]></string>
+    <string name="_bx_events_page_block_title_joined_profiles"><![CDATA[Joined Events]]></string>
+    <string name="_bx_events_page_block_title_followed_profiles"><![CDATA[Events I follow]]></string>
+    <string name="_bx_events_page_block_title_upcoming_profiles"><![CDATA[Upcoming Events]]></string>
+    <string name="_bx_events_page_block_title_past_profiles"><![CDATA[Past Events]]></string>
+    <string name="_bx_events_page_block_title_entry_all_actions"><![CDATA[All Actions]]></string>
+    <string name="_bx_events_page_block_title_entry_social_sharing"><![CDATA[Share]]></string>
+    <string name="_bx_events_page_block_title_categories"><![CDATA[Event categories]]></string>
+    <string name="_bx_events_page_block_title_calendar"><![CDATA[Events calendar]]></string>
+    <string name="_bx_events_page_block_title_profile_calendar"><![CDATA[Calendar]]></string>
+    <string name="_bx_events_page_block_title_profile_sessions"><![CDATA[Sessions]]></string>
+    <string name="_bx_events_page_block_title_sys_entry_context"><![CDATA[Posted in]]></string>
+    <string name="_bx_events_page_block_title_entry_context"><![CDATA[Posted in]]></string>
+    <string name="_bx_events_page_block_title_entry_location"><![CDATA[Location]]></string>
+    <string name="_bx_events_page_block_title_invite_to_group"><![CDATA[Invite Members to the Event]]></string>
+    <string name="_bx_events_page_block_title_search_form"><![CDATA[Search Form]]></string>
+    <string name="_bx_events_page_block_title_search_results"><![CDATA[Search Results]]></string>
+    <string name="_bx_events_page_block_title_search_form_cmts"><![CDATA[Comments Search Form]]></string>
+    <string name="_bx_events_page_block_title_search_results_cmts"><![CDATA[Comments Search Results]]></string>
+    <string name="_bx_events_page_block_title_sys_featured_entries_view_showcase"><![CDATA[Featured Events (Showcase View)]]></string>
+    <string name="_bx_events_page_block_title_featured_entries_view_showcase"><![CDATA[Featured Events]]></string>
+    <string name="_bx_events_page_block_title_sys_recommended_entries_view_showcase"><![CDATA[Recommended Events (Showcase View)]]></string>
+    <string name="_bx_events_page_block_title_recommended_entries_view_showcase"><![CDATA[Recommended Events]]></string>
+    <string name="_bx_events_page_block_title_sys_cover_block"><![CDATA[Cover]]></string>
+    <string name="_bx_events_page_block_title_cover_block"><![CDATA[Cover]]></string>
+    
+    <string name="_bx_events_menu_title_submenu"><![CDATA[Events Submenu]]></string>
+    <string name="_bx_events_menu_set_title_submenu"><![CDATA[Events Submenu]]></string>
+    <string name="_bx_events_menu_set_title_view_profile_actions"><![CDATA[Event Actions]]></string>
+    <string name="_bx_events_menu_title_view_profile_actions"><![CDATA[Event Actions]]></string>
+    <string name="_bx_events_menu_set_title_view_profile_actions_more"><![CDATA[Event More Actions]]></string>
+    <string name="_bx_events_menu_title_view_profile_actions_more"><![CDATA[Event More Actions]]></string>
+    <string name="_bx_events_menu_title_view_profile_meta"><![CDATA[Event Meta]]></string>
+    <string name="_bx_events_menu_set_title_view_profile_meta"><![CDATA[Event Meta]]></string>
+    <string name="_bx_events_menu_set_title_view_profile_submenu"><![CDATA[View Event Submenu]]></string>
+    <string name="_bx_events_menu_title_view_profile_submenu"><![CDATA[View Event Submenu]]></string>
+    <string name="_bx_events_menu_title_entries_my"><![CDATA[Profile Events Action Menu]]></string>
+    <string name="_bx_events_menu_set_title_entries_my"><![CDATA[Profile Events Menu]]></string>
+    <string name="_bx_events_menu_set_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+    <string name="_bx_events_menu_title_manage_tools"><![CDATA[Manage Tools Submenu]]></string>
+    <string name="_bx_events_menu_item_title_system_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_events_menu_item_title_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_events_menu_item_title_system_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_events_menu_item_title_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_events_menu_item_title_system_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_events_menu_item_title_clear_reports"><![CDATA[Clear reports]]></string>
+    <string name="_bx_events_menu_item_title_system_edit_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_events_menu_item_title_edit_cover"><![CDATA[Edit Cover]]></string>
+    <string name="_bx_events_menu_item_title_system_edit_questionnaire"><![CDATA[Questionnaire]]></string>
+    <string name="_bx_events_menu_item_title_edit_questionnaire"><![CDATA[Questionnaire]]></string>
+    <string name="_bx_events_menu_item_title_system_edit_sessions"><![CDATA[Sessions]]></string>
+    <string name="_bx_events_menu_item_title_edit_sessions"><![CDATA[Sessions]]></string>
+    <string name="_bx_events_menu_item_title_system_edit_pricing"><![CDATA[Pricing]]></string>
+    <string name="_bx_events_menu_item_title_edit_pricing"><![CDATA[Pricing]]></string>
+    <string name="_bx_events_menu_item_title_system_edit_profile"><![CDATA[Edit Event]]></string>
+    <string name="_bx_events_menu_item_title_edit_profile"><![CDATA[Edit Event]]></string>
+    <string name="_bx_events_menu_item_title_system_delete_profile"><![CDATA[Delete Event]]></string>
+    <string name="_bx_events_menu_item_title_delete_profile"><![CDATA[Delete Event]]></string>
+    <string name="_bx_events_menu_item_title_system_delete_account"><![CDATA[Delete Account]]></string>
+    <string name="_bx_events_menu_item_title_delete_account"><![CDATA[Delete Account]]></string>
+    <string name="_bx_events_menu_item_title_system_profile_type"><![CDATA[Event]]></string>
+    <string name="_bx_events_menu_item_title_profile_type"><![CDATA[Event]]></string>
+    <string name="_bx_events_menu_item_title_system_create_profile"><![CDATA[Event]]></string>
+    <string name="_bx_events_menu_item_title_create_profile"><![CDATA[Event]]></string>
+    <string name="_bx_events_menu_item_title_system_view_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_events_menu_item_title_view_profile_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_events_menu_item_title_system_view_joined_groups"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_view_joined_groups"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_system_view_fans"><![CDATA[Guests]]></string>
+    <string name="_bx_events_menu_item_title_view_fans"><![CDATA[Going]]></string>
+    <string name="_bx_events_menu_item_title_system_view_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_events_menu_item_title_view_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_events_menu_item_title_system_view_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_events_menu_item_title_view_profile_comments"><![CDATA[Comments]]></string>
+    <string name="_bx_events_menu_item_title_system_view_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_events_menu_item_title_view_profile_info"><![CDATA[Info]]></string>
+    <string name="_bx_events_menu_item_title_system_view_profile_view"><![CDATA[Event]]></string>
+    <string name="_bx_events_menu_item_title_view_profile_view"><![CDATA[Event]]></string>
+    <string name="_bx_events_menu_item_title_system_view_profile_more_auto"><![CDATA[More]]></string>
+    <string name="_bx_events_menu_item_title_view_profile_more_auto"><![CDATA[More]]></string>
+    <string name="_bx_events_menu_item_title_system_invite"><![CDATA[Invite]]></string>
+    <string name="_bx_events_menu_item_title_invite"><![CDATA[Invite]]></string>
+
+    <string name="_bx_events_menu_item_title_system_befriend"><![CDATA[Add friend]]></string>
+    <string name="_bx_events_menu_item_title_befriend"><![CDATA[Add friend]]></string>
+    <string name="_bx_events_menu_item_title_befriend_sent"><![CDATA[Add friend (requested)]]></string>
+    <string name="_bx_events_menu_item_title_befriend_confirm"><![CDATA[Accept friend request]]></string>
+    <string name="_bx_events_menu_item_title_system_unfriend"><![CDATA[Unfriend]]></string>
+    <string name="_bx_events_menu_item_title_unfriend"><![CDATA[Unfriend]]></string>
+    <string name="_bx_events_menu_item_title_unfriend_cancel_request"><![CDATA[Cancel friend request]]></string>
+    <string name="_bx_events_menu_item_title_unfriend_reject_request"><![CDATA[Reject friend request]]></string>
+
+    <string name="_bx_events_menu_item_title_system_become_fan"><![CDATA[Join Event]]></string>
+    <string name="_bx_events_menu_item_title_become_fan"><![CDATA[Going]]></string>
+    <string name="_bx_events_menu_item_title_become_fan_sent"><![CDATA[Requested]]></string>
+    <string name="_bx_events_menu_item_title_become_fan_confirm"><![CDATA[Confirm]]></string>
+    <string name="_bx_events_menu_item_title_system_leave_group"><![CDATA[Leave Event]]></string>
+    <string name="_bx_events_menu_item_title_leave_group"><![CDATA[Not going]]></string>
+    <string name="_bx_events_menu_item_title_leave_group_cancel_request"><![CDATA[Cancel]]></string>
+    <string name="_bx_events_menu_item_title_leave_group_reject_request"><![CDATA[Decline]]></string>
+    <string name="_bx_events_menu_item_title_sm_members"><![CDATA[{0} going]]></string>
+    <string name="_bx_events_menu_item_title_sm_subscribers"><![CDATA[{0} interested]]></string>
+
+    <string name="_bx_events_menu_item_title_system_pay_and_join"><![CDATA[Pay & Join]]></string>
+    <string name="_bx_events_menu_item_title_pay_and_join"><![CDATA[Pay & Join]]></string>
+    <string name="_bx_events_menu_item_title_system_subscribe"><![CDATA[Follow]]></string>
+    <string name="_bx_events_menu_item_title_subscribe"><![CDATA[Interested]]></string>
+    <string name="_bx_events_menu_item_title_system_check_in"><![CDATA[Check In]]></string>
+    <string name="_bx_events_menu_item_title_check_in"><![CDATA[Check In]]></string>
+    <string name="_bx_events_menu_item_title_system_view_entries_in_context"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_view_entries_in_context"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_system_unsubscribe"><![CDATA[Unfollow]]></string>
+    <string name="_bx_events_menu_item_title_unsubscribe"><![CDATA[Not interested]]></string>
+    <string name="_bx_events_menu_item_title_system_more_actions"><![CDATA[More Actions]]></string>
+    <string name="_bx_events_menu_item_title_more_actions"><![CDATA[]]></string>
+    <string name="_bx_events_menu_item_title_system_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_events_menu_item_title_friends"><![CDATA[Friends]]></string>
+    <string name="_bx_events_menu_item_title_system_manage_my_groups"><![CDATA[My events]]></string>
+    <string name="_bx_events_menu_item_title_manage_my_groups"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_system_followings"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_followings"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_system_entries_recent"><![CDATA[New]]></string>
+    <string name="_bx_events_menu_item_title_entries_recent"><![CDATA[New]]></string>
+    <string name="_bx_events_menu_item_title_system_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_events_menu_item_title_entries_search"><![CDATA[Search]]></string>
+    <string name="_bx_events_menu_item_title_system_entries_joined"><![CDATA[Joined]]></string>
+    <string name="_bx_events_menu_item_title_entries_followed"><![CDATA[Following]]></string>
+    <string name="_bx_events_menu_item_title_system_entries_followed"><![CDATA[Following]]></string>
+    <string name="_bx_events_menu_item_title_entries_joined"><![CDATA[Joined]]></string>
+    <string name="_bx_events_menu_item_title_system_entries_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_events_menu_item_title_entries_manage"><![CDATA[Manage]]></string>
+    <string name="_bx_events_menu_item_title_system_entries_home"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_entries_home"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_system_admt_groups"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_admt_groups"><![CDATA[Events]]></string>
+    <string name="_bx_events_menu_item_title_manage_my"><![CDATA[My Events]]></string>
+    <string name="_bx_events_menu_item_title_manage_all"><![CDATA[All Events]]></string>
+    <string name="_bx_events_menu_item_title_system_entries_top"><![CDATA[Top]]></string>
+    <string name="_bx_events_menu_item_title_entries_top"><![CDATA[Top]]></string>
+    <string name="_bx_events_menu_item_title_system_entries_upcoming"><![CDATA[Upcoming]]></string>
+    <string name="_bx_events_menu_item_title_entries_upcoming"><![CDATA[Upcoming]]></string>        
+    <string name="_bx_events_menu_item_title_system_entries_past"><![CDATA[Past]]></string>
+    <string name="_bx_events_menu_item_title_entries_past"><![CDATA[Past]]></string>
+    <string name="_bx_events_menu_item_title_system_calendar"><![CDATA[Calendar]]></string>
+    <string name="_bx_events_menu_item_title_calendar"><![CDATA[Calendar]]></string>
+
+    <string name="_bx_events_menu_item_title_system_sm_date_start"><![CDATA[Date start]]></string>
+    <string name="_bx_events_menu_item_title_sm_date_start"><![CDATA[Date start]]></string>
+    <string name="_bx_events_menu_item_title_system_sm_date_end"><![CDATA[Date end]]></string>
+    <string name="_bx_events_menu_item_title_sm_date_end"><![CDATA[Date end]]></string>
+    <string name="_bx_events_menu_item_title_ical_export"><![CDATA[Sync to Calendar]]></string>
+
+    <string name="_bx_events_grid_filter_item_title_adm_select_one_filter1"><![CDATA[All Statuses]]></string>
+    <string name="_bx_events_grid_filter_item_title_adm_select_one_filter2"><![CDATA[Any Audience]]></string>
+    <string name="_bx_events_grid_filter_item_title_adm_active"><![CDATA[Active]]></string>
+    <string name="_bx_events_grid_filter_item_title_adm_hidden"><![CDATA[Hidden]]></string>
+    <string name="_bx_events_grid_filter_item_title_adm_pending"><![CDATA[Pending]]></string>
+    <string name="_bx_events_grid_filter_item_title_adm_suspended"><![CDATA[Suspended]]></string>
+    <string name="_bx_events_grid_column_title_adm_active"><![CDATA[Active]]></string>
+    <string name="_bx_events_grid_column_title_adm_name"><![CDATA[Name]]></string>
+    <string name="_bx_events_grid_column_title_adm_added"><![CDATA[Created]]></string>
+    <string name="_bx_events_grid_column_title_adm_account"><![CDATA[Account]]></string>
+    <string name="_bx_events_grid_action_title_adm_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_events_grid_action_title_adm_delete_with_content"><![CDATA[Delete With Content]]></string>
+    <string name="_bx_events_grid_action_title_adm_more_actions"><![CDATA[More Actions]]></string>
+    <string name="_bx_events_grid_action_title_adm_audit_content"><![CDATA[Audit actions with content]]></string>
+    <string name="_bx_events_grid_action_title_adm_audit_context"><![CDATA[Audit actions in context]]></string>
+    <string name="_bx_events_grid_action_title_adm_clear_reports"><![CDATA[Сlear reports]]></string>
+    <string name="_bx_events_grid_action_err_delete"><![CDATA[Cannot delete the selected event(s).]]></string>
+    <string name="_bx_events_grid_txt_account_manager"><![CDATA[Redirect to manage account.]]></string>
+
+    <string name="_bx_events_grid_column_title_qn_question"><![CDATA[Question]]></string>
+    <string name="_bx_events_grid_action_title_qn_add"><![CDATA[Add]]></string>
+    <string name="_bx_events_grid_action_title_qn_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_events_grid_action_title_qn_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_events_popup_title_qn_add"><![CDATA[Add Question]]></string>
+    <string name="_bx_events_popup_title_qn_edit"><![CDATA[Edit Question]]></string>
+    <string name="_bx_events_popup_title_questionnaire"><![CDATA[Questionnaire]]></string>
+
+    <string name="_bx_events_grid_column_title_sn_title"><![CDATA[Title]]></string>
+    <string name="_bx_events_grid_column_title_sn_description"><![CDATA[Description]]></string>
+    <string name="_bx_events_grid_column_title_sn_date_start"><![CDATA[Date start]]></string>
+    <string name="_bx_events_grid_column_title_sn_date_end"><![CDATA[Date end]]></string>
+    <string name="_bx_events_grid_action_title_sn_add"><![CDATA[Add]]></string>
+    <string name="_bx_events_grid_action_title_sn_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_events_grid_action_title_sn_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_events_popup_title_sn_add"><![CDATA[Add Session]]></string>
+    <string name="_bx_events_popup_title_sn_edit"><![CDATA[Edit Session]]></string>
+
+    <string name="_bx_events_grid_column_title_name"><![CDATA[Name]]></string>
+    <string name="_bx_events_grid_column_title_role_id"><![CDATA[Role]]></string>
+    <string name="_bx_events_grid_column_title_period"><![CDATA[Period]]></string>
+    <string name="_bx_events_grid_column_title_price"><![CDATA[Price]]></string>
+    <string name="_bx_events_grid_action_title_add"><![CDATA[Add]]></string>
+    <string name="_bx_events_grid_action_title_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_events_grid_action_title_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_events_grid_action_title_buy"><![CDATA[Buy]]></string>
+    <string name="_bx_events_grid_action_title_buy_title"><![CDATA[One-time payment]]></string>
+    <string name="_bx_events_grid_action_title_subscribe"><![CDATA[Subscribe]]></string>
+    <string name="_bx_events_grid_action_title_subscribe_title"><![CDATA[Create a subscription with recurring billing]]></string>
+    <string name="_bx_events_grid_action_title_choose"><![CDATA[Choose]]></string>
+    <string name="_bx_events_grid_action_title_choose_title"><![CDATA[Switch to the level for free]]></string>
+    <string name="_bx_events_popup_title_price_add"><![CDATA[Add Pricing Plan]]></string>
+    <string name="_bx_events_popup_title_price_edit"><![CDATA[Edit Pricing Plan]]></string>
+
+    <string name="_bx_events_chart_growth"><![CDATA[Events: Growth]]></string>
+    <string name="_bx_events_chart_growth_speed"><![CDATA[Events: Growth Speed]]></string>
+
+    <string name="_bx_events_search_extended"><![CDATA[Events Search]]></string>
+    <string name="_bx_events_search_extended_cmts"><![CDATA[Events Comments Search]]></string>
+
+    <string name="_bx_events_txt_all_entries_in"><![CDATA[<a href="{0}">All Events in {1} ({2})</a>]]></string>
+    <string name="_bx_events_txt_account_pending"><![CDATA[This event is pending approval, please wait until it is activated.]]></string>
+    <string name="_bx_events_txt_account_suspended"><![CDATA[This event is suspended.]]></string>
+    <string name="_bx_events_txt_scheduled_awaiting"><![CDATA[Event publishing date is {date_publish_uf}]]></string>
+    <string name="_bx_events_txt_status_hidden"><![CDATA[This event was hidden.]]></string>
+    <string name="_bx_events_txt_N_fans"><![CDATA[{0} going]]></string>
+    <string name="_bx_events_txt_role"><![CDATA[Role]]></string>
+    <string name="_bx_events_txt_role_added"><![CDATA[Added]]></string>
+    <string name="_bx_events_txt_role_expired"><![CDATA[Expires]]></string>
+    <string name="_bx_events_txt_sample_single"><![CDATA[event]]></string>
+    <string name="_bx_events_txt_sample_single_with_article"><![CDATA[an event]]></string>
+    <string name="_bx_events_txt_sample_comment_single"><![CDATA[comment to event]]></string>
+    <string name="_bx_events_txt_sample_vote_single"><![CDATA[vote to event]]></string>
+    <string name="_bx_events_txt_sample_score_up_single"><![CDATA[score up vote to event]]></string>
+    <string name="_bx_events_txt_sample_score_down_single"><![CDATA[score down vote to event]]></string>
+    <string name="_bx_events_txt_set_role"><![CDATA[Set Role]]></string>
+    <string name="_bx_events_txt_view_answers"><![CDATA[View questionnaire answers]]></string>
+    <string name="_bx_events_txt_private_group"><![CDATA[Private event]]></string>
+    <string name="_bx_events_txt_from_time_to_time"><![CDATA[{0} - {1}]]></string>
+    <string name="_bx_events_txt_invite_popup_text"><![CDATA[You have been invited to a event. Click the button below to accept the invitation.]]></string>
+    <string name="_bx_events_txt_invite_popup_title"><![CDATA[Invitation to a event]]></string>
+    <string name="_bx_events_txt_invite_popup_button_accept"><![CDATA[Accept]]></string>
+    <string name="_bx_events_txt_invite_popup_button_decline"><![CDATA[Decline]]></string>
+    <string name="_bx_events_txt_invite_popup_error_invitation_absent"><![CDATA[Sorry, an error has occurred. You can not join this event. The invitation is expired or deleted.]]></string>
+    <string name="_bx_events_txt_invite_popup_error_invitation_wrong_user"><![CDATA[Sorry, an error has occurred. You can not join this event. This invitation cannot be used by you.]]></string>
+    <string name="_bx_events_txt_n_unit"><![CDATA[{0} ({1})]]></string>
+    <string name="_bx_events_txt_cart_item_title"><![CDATA[{0}: {1} ({2} {3})]]></string>
+    <string name="_bx_events_txt_cart_item_title_lifetime"><![CDATA[{0}: {1} (lifetime)]]></string>    
+
+    <string name="_bx_events_msg_performed"><![CDATA[The action was successfully performed.]]></string>
+    <string name="_bx_events_err_price_duplicate"><![CDATA[Similar pricing plan is already exists.]]></string>
+    <string name="_bx_events_err_cannot_perform"><![CDATA[An error appeared. Please report.]]></string>
+
+    <string name="_bx_events_alert_action_fan_added"><![CDATA[member joined]]></string>
+    <string name="_bx_events_txt_ntfs_fan_added"><![CDATA[Event <b class="bx-ntfs-content-profile">{owner_name}</b> has got a member <b class="bx-ntfs-content-entry">{subentry_sample}</b>]]></string>
+    <string name="_bx_events_alert_action_join_invitation_notif"><![CDATA[join invitation]]></string>
+    <string name="_bx_events_alert_action_join_request"><![CDATA[join request]]></string>
+    <string name="_bx_events_alert_action_reminder"><![CDATA[reminder]]></string>
+    <string name="_bx_events_alert_action_reminder_follow_context"><![CDATA[New reminders]]></string>
+    <string name="_bx_events_txt_ntfs_join_invitation"><![CDATA[<b class="bx-ntfs-content-profile">{owner_name}</b> event's representative invites <b class="bx-ntfs-content-entry">{subentry_sample}</b> to join event]]></string>
+    <string name="_bx_events_txt_ntfs_join_request"><![CDATA[Event <b class="bx-ntfs-content-profile">{owner_name}</b> has a join request from <b class="bx-ntfs-content-entry">{subentry_sample}</b>]]></string>
+    <string name="_bx_events_txt_ntfs_reminder_1"><![CDATA[Event <b class="bx-ntfs-content-profile">{owner_name}</b> starts in an hour]]></string>
+    <string name="_bx_events_txt_ntfs_reminder_24"><![CDATA[Event <b class="bx-ntfs-content-profile">{owner_name}</b> starts in 24 hours]]></string>
+    <string name="_bx_events_txt_ntfs_timeline_post_common"><![CDATA[New post <b class="bx-ntfs-content-entry">{subentry_sample}</b> in <b class="bx-ntfs-content-profile">{owner_name}</b> event timeline]]></string>
+
+    <string name="_bx_events_privacy_group_closed"><![CDATA[Closed]]></string>
+    <string name="_bx_events_privacy_group_secret"><![CDATA[Secret]]></string>
+
+    <string name="_bx_events_pre_lists_reminder"><![CDATA[Event reminder]]></string>
+    <string name="_bx_events_reminder_none"><![CDATA[Reminder is off]]></string>
+    <string name="_bx_events_reminder_1h"><![CDATA[1 hour]]></string>
+    <string name="_bx_events_reminder_2h"><![CDATA[2 hours]]></string>
+    <string name="_bx_events_reminder_3h"><![CDATA[3 hours]]></string>
+    <string name="_bx_events_reminder_6h"><![CDATA[6 hours]]></string>
+    <string name="_bx_events_reminder_12h"><![CDATA[12 hours]]></string>
+    <string name="_bx_events_reminder_24h"><![CDATA[24 hours]]></string>
+
+    <string name="_bx_events_pre_lists_cats"><![CDATA[Event categories]]></string>
+    <string name="_bx_events_cat_Conference"><![CDATA[Conference]]></string>
+    <string name="_bx_events_cat_Festival"><![CDATA[Festival]]></string>
+    <string name="_bx_events_cat_Fundraiser"><![CDATA[Fundraiser]]></string>
+    <string name="_bx_events_cat_Lecture"><![CDATA[Lecture]]></string>
+    <string name="_bx_events_cat_Market"><![CDATA[Market]]></string>
+    <string name="_bx_events_cat_Meal"><![CDATA[Meal]]></string>
+    <string name="_bx_events_cat_Social_Mixer"><![CDATA[Social Mixer]]></string>
+    <string name="_bx_events_cat_Tour"><![CDATA[Tour]]></string>
+    <string name="_bx_events_cat_Volunteering"><![CDATA[Volunteering]]></string>
+    <string name="_bx_events_cat_Workshop"><![CDATA[Workshop]]></string>
+    <string name="_bx_events_cat_Other"><![CDATA[Other]]></string>
+
+    <string name="_bx_events_pre_lists_roles"><![CDATA[Event roles]]></string>
+    <string name="_bx_events_role_regular"><![CDATA[Regular]]></string>
+    <string name="_bx_events_role_administrator"><![CDATA[Administrator]]></string>
+    <string name="_bx_events_role_moderator"><![CDATA[Moderator]]></string>
+
+    <string name="_bx_events_pre_lists_period_units"><![CDATA[Billing Interval]]></string>
+    <string name="_bx_events_period_unit_day"><![CDATA[day]]></string>
+    <string name="_bx_events_period_unit_week"><![CDATA[week]]></string>
+    <string name="_bx_events_period_unit_month"><![CDATA[month]]></string>
+    <string name="_bx_events_period_unit_year"><![CDATA[year]]></string>
+
+    <string name="_bx_events_pre_lists_repeat_year"><![CDATA[Repeat year]]></string>
+    <string name="_bx_events_cat_repeat_year_every_year"><![CDATA[Every year]]></string>
+    <string name="_bx_events_cat_repeat_year_every_2_years"><![CDATA[Every 2 years]]></string>
+    <string name="_bx_events_cat_repeat_year_every_3_years"><![CDATA[Every 3 years]]></string>
+    <string name="_bx_events_cat_repeat_year_every_4_years"><![CDATA[Every 4 years]]></string>
+
+    <string name="_bx_events_pre_lists_repeat_month"><![CDATA[Repeat month]]></string>
+    <string name="_bx_events_cat_repeat_month_every_month"><![CDATA[Every month]]></string>
+    <string name="_bx_events_cat_repeat_month_jan"><![CDATA[Every January]]></string>
+    <string name="_bx_events_cat_repeat_month_feb"><![CDATA[Every February]]></string>
+    <string name="_bx_events_cat_repeat_month_mar"><![CDATA[Every March]]></string>
+    <string name="_bx_events_cat_repeat_month_apr"><![CDATA[Every April]]></string>
+    <string name="_bx_events_cat_repeat_month_may"><![CDATA[Every May]]></string>
+    <string name="_bx_events_cat_repeat_month_jun"><![CDATA[Every June]]></string>
+    <string name="_bx_events_cat_repeat_month_jul"><![CDATA[Every July]]></string>
+    <string name="_bx_events_cat_repeat_month_aug"><![CDATA[Every August]]></string>
+    <string name="_bx_events_cat_repeat_month_sep"><![CDATA[Every September]]></string>
+    <string name="_bx_events_cat_repeat_month_oct"><![CDATA[Every October]]></string>
+    <string name="_bx_events_cat_repeat_month_nov"><![CDATA[Every November]]></string>
+    <string name="_bx_events_cat_repeat_month_dec"><![CDATA[Every December]]></string>
+
+    <string name="_bx_events_pre_lists_repeat_week_of_month"><![CDATA[Repeat week of the month]]></string>
+    <string name="_bx_events_cat_repeat_week_of_month_every_week"><![CDATA[Every week]]></string>
+    <string name="_bx_events_cat_repeat_week_of_month_first_week_of_month"><![CDATA[Every 1st week of the month]]></string>
+    <string name="_bx_events_cat_repeat_week_of_month_second_week_of_month"><![CDATA[Every 2nd week of the month]]></string>
+    <string name="_bx_events_cat_repeat_week_of_month_third_week_of_month"><![CDATA[Every 3rd week of the month]]></string>
+    <string name="_bx_events_cat_repeat_week_of_month_fourth_week_of_month"><![CDATA[Every 4th week of the month]]></string>
+    <string name="_bx_events_cat_repeat_week_of_month_fifth_week_of_month"><![CDATA[Every 5th week of the month]]></string>
+    <string name="_bx_events_cat_repeat_week_of_month_sixth_week_of_month"><![CDATA[Every 6th week of the month]]></string>
+
+    <string name="_bx_events_pre_lists_repeat_day_of_month"><![CDATA[Repeat day of the month]]></string>
+    <string name="_bx_events_cat_repeat_day_of_month_every_day"><![CDATA[Every day]]></string>
+
+    <string name="_bx_events_pre_lists_repeat_day_of_week"><![CDATA[Repeat day of the week]]></string>
+    <string name="_bx_events_cat_repeat_day_of_week_every_day"><![CDATA[Every day of the week]]></string>
+    <string name="_bx_events_cat_repeat_day_of_week_mon"><![CDATA[Every Monday]]></string>
+    <string name="_bx_events_cat_repeat_day_of_week_tue"><![CDATA[Every Tuesday]]></string>
+    <string name="_bx_events_cat_repeat_day_of_week_wed"><![CDATA[Every Wednesday]]></string>
+    <string name="_bx_events_cat_repeat_day_of_week_thu"><![CDATA[Every Thursday]]></string>
+    <string name="_bx_events_cat_repeat_day_of_week_fri"><![CDATA[Every Friday]]></string>
+    <string name="_bx_events_cat_repeat_day_of_week_sat"><![CDATA[Every Saturday]]></string>
+    <string name="_bx_events_cat_repeat_day_of_week_sun"><![CDATA[Every Sunday]]></string>
+
+    <string name="_bx_events_email_join_request"><![CDATA[Event join request]]></string>
+    <string name="_bx_events_email_join_request_subject"><![CDATA[Request to join your event]]></string>
+    <string name="_bx_events_email_join_request_body"><![CDATA[{email_header}
+<p>New request to join <a href="{EntryUrl}">{EntryTitle}</a> event from <a href="{NewMemberUrl}">{NewMemberDisplayName}</a></p>
+{email_footer}]]></string>
+
+    <string name="_bx_events_email_join_reject"><![CDATA[Event join request was rejected]]></string>
+    <string name="_bx_events_email_join_reject_subject"><![CDATA[Request to join an event was rejected]]></string>
+    <string name="_bx_events_email_join_reject_body"><![CDATA[{email_header}
+<p>Your request to join <a href="{EntryUrl}">{EntryTitle}</a> event was rejected by event admin.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_events_email_join_confirm"><![CDATA[Event join request was confirmed]]></string>
+    <string name="_bx_events_email_join_confirm_subject"><![CDATA[Your request to join an event was confirmed]]></string>
+    <string name="_bx_events_email_join_confirm_body"><![CDATA[{email_header}
+<p>Your request to join <a href="{EntryUrl}">{EntryTitle}</a> event was confirmed by the event admin.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_events_email_fan_remove"><![CDATA[Profile was removed from event participants]]></string>
+    <string name="_bx_events_email_fan_remove_subject"><![CDATA[Your profile was removed from the event]]></string>
+    <string name="_bx_events_email_fan_remove_body"><![CDATA[{email_header}
+<p>Your profile was removed from <a href="{EntryUrl}">{EntryTitle}</a> event by the event admin.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_events_email_fan_become_admin"><![CDATA[Event admin status was granted]]></string>
+    <string name="_bx_events_email_fan_become_admin_subject"><![CDATA[You are an event admin now]]></string>
+    <string name="_bx_events_email_fan_become_admin_body"><![CDATA[{email_header}
+<p>You are an admin of <a href="{EntryUrl}">{EntryTitle}</a> event now.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_events_email_admin_become_fan"><![CDATA[Event admin status was revoked]]></string>
+    <string name="_bx_events_email_admin_become_fan_subject"><![CDATA[Your event admin status was revoked]]></string>
+    <string name="_bx_events_email_admin_become_fan_body"><![CDATA[{email_header}
+<p>Your admin status was revoked from <a href="{EntryUrl}">{EntryTitle}</a> event.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_events_email_invitation"><![CDATA[Invitation to event]]></string>
+    <string name="_bx_events_email_invitation_subject"><![CDATA[Invitation to "{EntryTitle}" event]]></string>
+    <string name="_bx_events_email_invitation_body"><![CDATA[{email_header}
+<p><a href="{InviterUrl}">{InviterDisplayName}</a> invited you to <a href="{EntryUrl}">{EntryTitle} event</a>.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_events_email_set_role"><![CDATA[Event member status was changed]]></string>
+    <string name="_bx_events_email_set_role_subject"><![CDATA[Your event status was changed]]></string>
+    <string name="_bx_events_email_set_role_body"><![CDATA[{email_header}
+<p>Your status in <a href="{EntryUrl}">{EntryTitle}</a> event was changed to <b>{Role}</b>.</p>
+{email_footer}]]></string>
+
+    <string name="_bx_events_email_reminder"><![CDATA[Reminder for the event]]></string>
+    <string name="_bx_events_email_reminder_subject"><![CDATA["{EntryTitle}" event - {EntryBeginShort}]]></string>
+    <string name="_bx_events_email_reminder_body"><![CDATA[{email_header}
+<p class="vevent">
+    <a href="{EntryUrl}" class="summary">{EntryTitle}</a> event reminder:</a><br />
+    Date: <abbr class="dtstart" title="{EntryBeginUTC}">{EntryBegin}</abbr><br />
+    {EntryLocation}
+</p>
+{email_footer}]]></string>
+    <string name="_bx_events_email_reminder_location"><![CDATA[Location: <span class="location">{0}</span>]]></string>
+</resources>

+ 797 - 0
modules/boonex/events/updates/14.0.5_14.0.6/source/install/sql/enable.sql

@@ -0,0 +1,797 @@
+
+-- SETTINGS
+
+SET @iTypeOrder = (SELECT MAX(`order`) FROM `sys_options_types` WHERE `group` = 'modules');
+INSERT INTO `sys_options_types`(`group`, `name`, `caption`, `icon`, `order`) VALUES 
+('modules', 'bx_events', '_bx_events', 'bx_events@modules/boonex/events/|std-icon.svg', IF(ISNULL(@iTypeOrder), 1, @iTypeOrder + 1));
+SET @iTypeId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order`)
+VALUES (@iTypeId, 'bx_events', '_bx_events', 1);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `extra`, `check`, `check_error`, `order`) VALUES
+('bx_events_enable_auto_approve', 'on', @iCategId, '_bx_events_option_enable_auto_approve', 'checkbox', '', '', '', 0),
+('bx_events_num_connections_quick', '6', @iCategId, '_bx_events_option_num_connections_quick', 'digit', '', '', '', 10),
+('bx_events_per_page_browse', '24', @iCategId, '_bx_events_option_per_page_browse', 'digit', '', '', '', 11),
+('bx_events_num_rss', '10', @iCategId, '_bx_events_option_num_rss', 'digit', '', '', '', 12),
+('bx_events_per_page_browse_showcase', '32', @iCategId, '_sys_option_per_page_browse_showcase', 'digit', '', '', '', 15),
+('bx_events_per_page_browse_recommended', '10', @iCategId, '_sys_option_per_page_browse_recommended', 'digit', '', '', '', 16),
+('bx_events_per_page_for_favorites_lists', '5', @iCategId, '_bx_events_option_per_page_for_favorites_lists', 'digit', '', '', '', 17),
+('bx_events_time_format', 'H:i', @iCategId, '_bx_events_option_time_format', 'digit', '', '', '', 20),
+('bx_events_short_date_format', 'j/n/Y', @iCategId, '_bx_events_option_short_date_format', 'digit', '', '', '', 22),
+('bx_events_datetime_format', 'j/n/Y H:i', @iCategId, '_bx_events_option_datetime_format', 'digit', '', '', '', 23),
+('bx_events_summary_chars', '700', @iCategId, '_bx_events_option_summary_chars', 'digit', '', '', '', 25),
+('bx_events_searchable_fields', 'event_name,event_desc', @iCategId, '_bx_events_option_searchable_fields', 'list', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:21:"get_searchable_fields";}', '', '', 30),
+('bx_events_members_mode', '', @iCategId, '_bx_events_option_members_mode', 'select', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:24:"get_options_members_mode";}', '', '', 40),
+('bx_events_public_subscribed_me', 'on', @iCategId, '_bx_events_option_public_subscribed_me', 'checkbox', '', '', '', 50),
+('bx_events_enable_subscribe_wo_join', '', @iCategId, '_bx_events_option_enable_subscribe_wo_join', 'checkbox', '', '', '', 55),
+('bx_events_enable_subscribe_for_past_events', 'on', @iCategId, '_bx_events_option_enable_subscribe_for_past_events', 'checkbox', '', '', '', 57),
+('bx_events_internal_notifications', '', @iCategId, '_bx_events_option_internal_notifications', 'checkbox', '', '', '', 60),
+('bx_events_reminder_interval', '24', @iCategId, '_bx_events_option_reminder_interval', 'select', '24,48', '', '', 70),
+('bx_events_rm_from_timeline_after', '0', @iCategId, '_bx_events_option_rm_from_timeline_after', 'digit', '', '', '', 80);
+
+-- PAGES
+
+-- PAGE: create profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_create_profile', 'create-event-profile', '_bx_events_page_title_sys_create_profile', '_bx_events_page_title_create_profile', 'bx_events', 5, 2147483647, 1, 'page.php?i=create-event-profile', '', '', '', 0, 1, 0, '', '');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_create_profile', 1, 'bx_events', '_bx_events_page_block_title_create_profile', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:13:\"entity_create\";}', 0, 1, 1);
+
+-- PAGE: view profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_view_profile', 'view-event-profile', '_bx_events_page_title_sys_view_profile', '_bx_events_page_title_view_profile', 'bx_events', 10, 2147483647, 1, 'page.php?i=view-event-profile', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `hidden_on`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES
+('bx_events_view_profile', 4, 'bx_events', '', '_bx_events_page_block_title_profile_comments', 11, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:15:\"entity_comments\";}', 0, 0, 0, 0),
+('bx_events_view_profile', 4, 'bx_events', '', '_bx_events_page_block_title_profile_description', 13, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:17:\"entity_text_block\";}', 0, 0, 1, 0),
+('bx_events_view_profile', 2, 'bx_events', '', '_bx_events_page_block_title_profile_info', 11, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:11:\"entity_info\";}', 0, 0, 1, 0),
+('bx_events_view_profile', 1, 'bx_events', '', '_bx_events_page_block_title_entry_social_sharing', 13, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:21:\"entity_social_sharing\";}', 0, 0, 0, 0),
+('bx_events_view_profile', 1, 'bx_events', '', '_bx_events_page_block_title_entry_all_actions', 13, '', 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:18:"entity_all_actions";}', 0, 0, 0, 0),
+('bx_events_view_profile', 3, 'bx_events', '', '_bx_events_page_block_title_profile_calendar', 11, '', 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:8:\"calendar\";s:6:\"params\";a:2:{i:0;a:1:{s:5:\"event\";s:12:\"{content_id}\";}i:1;s:21:\"calendar_compact.html\";}}', 0, 0, 1, 0),
+('bx_events_view_profile', 3, 'bx_events', '', '_bx_events_page_block_title_profile_sessions', 11, '', 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:8:"sessions";s:6:"params";a:1:{i:0;s:12:"{content_id}";}}', 0, 0, 1, 1),
+('bx_events_view_profile', 3, 'bx_events', '_bx_events_page_block_title_sys_entry_context', '_bx_events_page_block_title_entry_context', 13, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:14:\"entity_context\";}', 0, 0, 1, 2),
+('bx_events_view_profile', 4, 'bx_events', '', '_bx_events_page_block_title_fans', 11, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:4:\"fans\";}', 0, 0, 1, 1),
+('bx_events_view_profile', 4, 'bx_events', '', '_bx_events_page_block_title_admins', 11, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:6:\"admins\";}', 0, 0, 1, 2),
+('bx_events_view_profile', 2, 'bx_events', '', '_bx_events_page_block_title_entry_location', 11, '', 2147483647, 'service', 'a:4:{s:6:\"module\";s:6:\"system\";s:6:\"method\";s:13:\"locations_map\";s:6:\"params\";a:2:{i:0;s:9:\"bx_events\";i:1;s:12:\"{content_id}\";}s:5:\"class\";s:20:\"TemplServiceMetatags\";}', 0, 0, 1, 1),
+('bx_events_view_profile', 2, 'bx_events', '', '_bx_events_page_block_title_entry_reports', 11, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"events\";s:6:\"method\";s:14:\"entity_reports\";}', 0, 0, 1, 6);
+-- PAGE: view closed profile 
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_view_profile_closed', 'view-event-profile-closed', '_bx_events_page_title_sys_view_profile_closed', '_bx_events_page_title_view_profile', 'bx_events', 10, 2147483647, 1, 'page.php?i=view-event-profile', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+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_events_view_profile_closed', 2, 'bx_events', '', '_bx_events_page_block_title_profile_info', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:11:\"entity_info\";}', 0, 0, 1, 0),
+('bx_events_view_profile_closed', 3, 'bx_events', '', '_bx_events_page_block_title_fans', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:4:\"fans\";}', 0, 0, 1, 0);
+
+-- PAGE: edit profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_edit_profile', 'edit-event-profile', '_bx_events_page_title_sys_edit_profile', '_bx_events_page_title_edit_profile', 'bx_events', 5, 2147483647, 1, 'page.php?i=edit-event-profile', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_edit_profile', 1, 'bx_events', '_bx_events_page_block_title_edit_profile', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:11:\"entity_edit\";}', 0, 0, 0);
+
+-- PAGE: questionnaire (join)
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_questionnaire', 'event-questionnaire', '_bx_events_page_title_sys_questionnaire', '_bx_events_page_title_questionnaire', 'bx_events', 5, 2147483647, 1, 'page.php?i=event-questionnaire', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_questionnaire', 1, 'bx_events', '_bx_events_page_block_title_edit_questionnaire', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:25:"entity_edit_questionnaire";}', 0, 0, 0);
+
+
+-- PAGE: invite members
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_invite', 'invite-to-event', '_bx_events_page_title_sys_invite_to_group', '_bx_events_page_title_invite_to_group', 'bx_events', 5, 2147483647, 1, 'page.php?i=invite-to-event', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_invite', 1, 'bx_events', '_bx_events_page_block_title_invite_to_group', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:13:\"entity_invite\";}', 0, 0, 0);
+
+-- PAGE: delete profile
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_delete_profile', 'delete-event-profile', '_bx_events_page_title_sys_delete_profile', '_bx_events_page_title_delete_profile', 'bx_events', 5, 2147483647, 1, 'page.php?i=delete-event-profile', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_delete_profile', 1, 'bx_events', '_bx_events_page_block_title_delete_profile', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:13:\"entity_delete\";}', 0, 0, 0);
+
+-- PAGE: join profile
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_join_profile', 'join-event-profile', '_bx_events_page_title_sys_join_profile', '_bx_events_page_title_join_profile', 'bx_events', 5, 2147483647, 1, 'page.php?i=join-event-profile', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_join_profile', 1, 'bx_events', '_bx_events_page_block_title_join_profile', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:11:"entity_join";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 0);
+
+-- PAGE: profile info
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_profile_info', 'event-profile-info', '_bx_events_page_title_sys_profile_info', '_bx_events_page_title_profile_info', 'bx_events', 5, 2147483647, 1, 'page.php?i=event-profile-info', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `hidden_on`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_events_profile_info', 1, 'bx_events', '', '_bx_events_page_block_title_profile_description', 13, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:17:\"entity_text_block\";}', 0, 0, 1, 0),
+('bx_events_profile_info', 2, 'bx_events', '_bx_events_page_block_title_system_profile_info', '_bx_events_page_block_title_profile_info_link', 11, '', 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:16:\"entity_info_full\";}', 0, 0, 1, 0),
+('bx_events_profile_info', 3, 'bx_events', '', '_bx_events_page_block_title_profile_calendar', 11, '', 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:8:\"calendar\";s:6:\"params\";a:2:{i:0;a:1:{s:5:\"event\";s:12:\"{content_id}\";}i:1;s:21:\"calendar_compact.html\";}}', 0, 0, 1, 0),
+('bx_events_profile_info', 2, 'bx_events', '', '_bx_events_page_block_title_entry_location', 11, '', 2147483647, 'service', 'a:4:{s:6:\"module\";s:6:\"system\";s:6:\"method\";s:13:\"locations_map\";s:6:\"params\";a:2:{i:0;s:9:\"bx_events\";i:1;s:12:\"{content_id}\";}s:5:\"class\";s:20:\"TemplServiceMetatags\";}', 0, 0, 1, 1);
+
+-- PAGE: manage profile sessions
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_profile_sessions', 'event-sessions', '_bx_events_page_title_sys_profile_sessions', '_bx_events_page_title_profile_sessions', 'bx_events', 5, 2147483647, 1, 'page.php?i=event-sessions', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+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_events_profile_sessions', 1, 'bx_events', '', '_bx_events_page_block_title_profile_sessions', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:15:"entity_sessions";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 1);
+
+-- PAGE: manage profile pricing
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_profile_pricing', 'event-pricing', '_bx_events_page_title_sys_profile_pricing', '_bx_events_page_title_profile_pricing', 'bx_events', 5, 2147483647, 1, 'page.php?i=event-pricing', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+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_events_profile_pricing', 1, 'bx_events', '', '_bx_events_page_block_title_profile_pricing', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:14:"entity_pricing";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 0, 0, 1, 1);
+
+-- PAGE: event fans
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_fans', 'event-fans', '_bx_events_page_title_sys_group_fans', '_bx_events_page_title_group_fans', 'bx_events', 5, 2147483647, 1, 'page.php?i=event-fans', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+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_events_fans', 1, 'bx_events', '_bx_events_page_block_title_system_fans', '_bx_events_page_block_title_fans_link', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:14:"browse_members";s:6:"params";a:2:{i:0;s:12:"{profile_id}";i:1;a:2:{s:13:"empty_message";b:1;s:13:"ajax_paginate";b:0;}}}', 0, 0, 1, 1);
+
+
+-- PAGE: event manage
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_manage_item', 'event-manage', '_bx_events_page_title_sys_manage_profile', '_bx_events_page_title_manage_profile', 'bx_events', 5, 2147483647, 1, 'page.php?i=event-manage', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+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_events_manage_item', 1, 'bx_events', '_bx_events_page_block_title_system_fans_manage', '_bx_events_page_block_title_fans_link', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:10:"fans_table";}', 0, 0, 1, 1),
+('bx_events_manage_item', 1, 'bx_events', '_bx_events_page_block_title_system_subscribers_manage', '_bx_events_page_block_title_subscribers_link', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:6:"system";s:6:"method";s:19:"subscribed_me_table";s:5:"class";s:23:"TemplServiceConnections";}', 0, 0, 1, 2),
+('bx_events_manage_item', 1, 'bx_events', '_bx_events_page_block_title_system_invites_manage', '_bx_events_page_block_title_invites', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:13:"invites_table";}', 0, 0, 1, 3);
+
+-- PAGE: view entry comments
+
+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 
+('bx_events_profile_comments', '_bx_events_page_title_sys_profile_comments', '_bx_events_page_title_profile_comments', 'bx_events', 5, 2147483647, 1, 'event-profile-comments', '', '', '', '', 0, 1, 0, 'BxEventsPageEntry', 'modules/boonex/events/classes/BxEventsPageEntry.php');
+
+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_events_profile_comments', 1, 'bx_events', '_bx_events_page_block_title_profile_comments', '_bx_events_page_block_title_profile_comments_link', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:15:\"entity_comments\";}', 0, 0, 0, 1);
+
+-- PAGE: module home
+
+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 
+('bx_events_home', '_bx_events_page_title_sys_recent', '_bx_events_page_title_recent', 'bx_events', 5, 2147483647, 1, 'events-home', 'page.php?i=events-home', '', '', '', 0, 1, 0, 'BxEventsPageBrowse', 'modules/boonex/events/classes/BxEventsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('bx_events_home', 1, 'bx_events', '_bx_events_page_block_title_featured_profiles', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:15:"browse_featured";s:6:"params";a:1:{i:0;s:7:"gallery";}}', 0, 1, 0),
+('bx_events_home', 1, 'bx_events', '_bx_events_page_block_title_latest_profiles', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:22:\"browse_recent_profiles\";s:6:"params";a:1:{i:0;b:1;}}', 0, 1, 1),
+('bx_events_home', 1, 'bx_events', '_bx_events_page_block_title_past_profiles', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:20:"browse_past_profiles";s:6:"params";a:1:{i:0;b:0;}}', 0, 1, 2);
+
+-- PAGE: top profiles
+
+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 
+('bx_events_top', '_bx_events_page_title_sys_top', '_bx_events_page_title_top', 'bx_events', 5, 2147483647, 1, 'events-top', 'page.php?i=events-top', '', '', '', 0, 1, 0, 'BxEventsPageBrowse', 'modules/boonex/events/classes/BxEventsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_top', 1, 'bx_events', '_bx_events_page_block_title_top_profiles', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:19:\"browse_top_profiles\";s:6:"params";a:1:{i:0;b:1;}}', 0, 1, 0);
+
+-- PAGE: entries in context
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_context', 'events-context', '_bx_events_page_title_sys_entries_in_context', '_bx_events_page_title_entries_in_context', 'bx_events', 5, 2147483647, 1, '', '', '', '', 0, 1, 0, 'BxEventsPageEntriesInContext', 'modules/boonex/events/classes/BxEventsPageEntriesInContext.php');
+
+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_events_context', 1, 'bx_events', '_bx_events_page_block_title_sys_entries_in_context', '_bx_events_page_block_title_entries_in_context', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:14:\"browse_context\";}', 0, 0, 1, 1),
+('bx_events_context', 1, 'bx_events', '_bx_events_page_block_title_sys_calendar_in_context', '_bx_events_page_block_title_calendar_in_context', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:9:"bx_events";s:6:"method";s:8:"calendar";s:12:"ignore_cache";b:1;s:6:"params";a:1:{i:0;a:1:{s:10:"context_id";s:12:"{profile_id}";}}}', 0, 0, 1, 2);
+
+-- PAGE: upcoming profiles
+
+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 
+('bx_events_upcoming', '_bx_events_page_title_sys_upcoming', '_bx_events_page_title_upcoming', 'bx_events', 5, 2147483647, 1, 'events-upcoming', 'page.php?i=events-upcoming', '', '', '', 0, 1, 0, 'BxEventsPageBrowse', 'modules/boonex/events/classes/BxEventsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_upcoming', 1, 'bx_events', '_bx_events_page_block_title_upcoming_profiles', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:24:"browse_upcoming_profiles";s:6:"params";a:1:{i:0;a:1:{s:13:"empty_message";b:1;}}}', 0, 1, 0);
+
+-- PAGE: past profiles
+
+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 
+('bx_events_past', '_bx_events_page_title_sys_past', '_bx_events_page_title_past', 'bx_events', 5, 2147483647, 1, 'events-past', 'page.php?i=events-past', '', '', '', 0, 1, 0, 'BxEventsPageBrowse', 'modules/boonex/events/classes/BxEventsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_past', 1, 'bx_events', '_bx_events_page_block_title_past_profiles', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:20:"browse_past_profiles";s:6:"params";a:1:{i:0;b:1;}}', 0, 0, 0);
+
+-- PAGE: calendar
+
+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 
+('bx_events_calendar', '_bx_events_page_title_sys_calendar', '_bx_events_page_title_calendar', 'bx_events', 5, 2147483647, 1, 'events-calendar', 'page.php?i=events-calendar', '', '', '', 0, 1, 0, 'BxEventsPageCalendar', 'modules/boonex/events/classes/BxEventsPageCalendar.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_calendar', 1, 'bx_events', '_bx_events_page_block_title_calendar', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:8:"calendar";s:12:"ignore_cache";b:1;}', 0, 1, 0);
+
+-- PAGE: search for entries
+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 
+('bx_events_search', '_bx_events_page_title_sys_entries_search', '_bx_events_page_title_entries_search', 'bx_events', 5, 2147483647, 1, 'events-search', 'page.php?i=events-search', '', '', '', 0, 1, 0, 'BxEventsPageBrowse', 'modules/boonex/events/classes/BxEventsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_events_search', 1, 'bx_events', '_bx_events_page_block_title_search_form', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:8:"get_form";s:6:"params";a:1:{i:0;a:1:{s:6:"object";s:9:"bx_events";}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 1, 1),
+('bx_events_search', 1, 'bx_events', '_bx_events_page_block_title_search_results', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_results";s:6:"params";a:1:{i:0;a:2:{s:6:"object";s:9:"bx_events";s:10:"show_empty";b:1;}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 1, 2),
+('bx_events_search', 1, 'bx_events', '_bx_events_page_block_title_search_form_cmts', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:8:"get_form";s:6:"params";a:1:{i:0;a:1:{s:6:"object";s:14:"bx_events_cmts";}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 0, 3),
+('bx_events_search', 1, 'bx_events', '_bx_events_page_block_title_search_results_cmts', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_results";s:6:"params";a:1:{i:0;a:2:{s:6:"object";s:14:"bx_events_cmts";s:10:"show_empty";b:1;}}s:5:"class";s:27:"TemplSearchExtendedServices";}', 0, 1, 0, 4);
+
+-- PAGE: joined profiles
+
+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 
+('bx_joined_events', '_bx_events_page_title_sys_joined', '_bx_events_page_title_joined', 'bx_events', 5, 2147483647, 1, 'events-joined', 'page.php?i=events-joined', '', '', '', 0, 1, 0, 'BxEventsPageBrowse', 'modules/boonex/events/classes/BxEventsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_joined_events', 1, 'bx_events', '_bx_events_page_block_title_joined_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:21:"browse_joined_entries";s:6:"params";a:2:{i:0;s:12:"{profile_id}";i:1;b:1;}}', 0, 1, 0);
+
+-- PAGE: followed profiles
+
+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 
+('bx_followed_events', '_bx_events_page_title_sys_followed', '_bx_events_page_title_followed', 'bx_events', 5, 2147483647, 1, 'events-followed', 'page.php?i=events-followed', '', '', '', 0, 1, 0, 'BxEventsPageBrowse', 'modules/boonex/events/classes/BxEventsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_followed_events', 1, 'bx_events', '_bx_events_page_block_title_followed_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:23:"browse_followed_entries";s:6:"params";a:2:{i:0;s:12:"{profile_id}";i:1;b:1;}}', 0, 1, 0);
+
+-- PAGE: module manage own
+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 
+('bx_events_manage', '_bx_events_page_title_sys_manage', '_bx_events_page_title_manage', 'bx_events', 5, 2147483647, 1, 'events-manage', 'page.php?i=events-manage', '', '', '', 0, 1, 0, 'BxEventsPageBrowse', 'modules/boonex/events/classes/BxEventsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_manage', 1, 'bx_events', '_bx_events_page_block_title_system_manage', '_bx_events_page_block_title_manage', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:12:\"manage_tools\";}}', 0, 1, 0);
+
+-- PAGE: module manage all
+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 
+('bx_events_administration', '_bx_events_page_title_sys_manage_administration', '_bx_events_page_title_manage', 'bx_events', 5, 192, 1, 'events-administration', 'page.php?i=events-administration', '', '', '', 0, 1, 0, 'BxEventsPageBrowse', 'modules/boonex/events/classes/BxEventsPageBrowse.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_administration', 1, 'bx_events', '_bx_events_page_block_title_system_manage_administration', '_bx_events_page_block_title_manage', 11, 192, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:12:\"manage_tools\";s:6:\"params\";a:1:{i:0;s:14:\"administration\";}}', 0, 1, 0);
+
+-- PAGE: user's events
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_joined', 'joined-events', '_bx_events_page_title_sys_joined_entries', '_bx_events_page_title_joined_entries', 'bx_events', 5, 2147483647, 1, '', '', '', '', 0, 1, 0, 'BxEventsPageJoinedEntries', 'modules/boonex/events/classes/BxEventsPageJoinedEntries.php');
+
+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_events_joined', 1, 'bx_events', '_bx_events_page_block_title_sys_entries_actions', '_bx_events_page_block_title_entries_actions', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:18:"my_entries_actions";}', 0, 0, 1, 1),
+('bx_events_joined', 1, 'bx_events', '_bx_events_page_block_title_sys_entries_of_author', '_bx_events_page_block_title_entries_of_author', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:22:"browse_created_entries";}', 0, 0, 0, 2),
+('bx_events_joined', 1, 'bx_events', '_bx_events_page_block_title_sys_favorites_of_author', '_bx_events_page_block_title_favorites_of_author', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:21:"browse_favorite_lists";s:6:"params";a:2:{s:10:"profile_id";s:12:"{profile_id}";s:6:"params";a:3:{s:9:"unit_view";s:8:"showcase";s:13:"empty_message";b:0;s:13:"ajax_paginate";b:0;}}}', 0, 1, 1, 2),
+('bx_events_joined', 1, 'bx_events', '_bx_events_page_block_title_sys_joined_entries', '_bx_events_page_block_title_joined_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:21:"browse_joined_entries";s:6:"params";a:2:{i:0;i:0;i:1;b:1;}}', 0, 0, 1, 4),
+('bx_events_joined', 1, 'bx_events', '_bx_events_page_block_title_sys_joined_calendar', '_bx_events_page_block_title_joined_calendar', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:8:"calendar";s:6:"params";a:1:{i:0;a:1:{s:10:"profile_id";s:12:"{profile_id}";}}}', 0, 0, 1, 5);
+
+
+-- PAGE: favorites by list
+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 
+('bx_events_favorites', '_bx_events_page_title_sys_entries_favorites', '_bx_events_page_title_entries_favorites', 'bx_events', 12, 2147483647, 1, 'events-favorites', 'page.php?i=events-favorites', '', '', '', 0, 1, 0, 'BxEventsPageListEntry', 'modules/boonex/events/classes/BxEventsPageListEntry.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES 
+('bx_events_favorites', 2, 'bx_events', '_bx_events_page_block_title_sys_favorites_entries', '_bx_events_page_block_title_favorites_entries', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:15:"browse_favorite";s:6:"params";a:2:{s:10:"profile_id";s:12:"{profile_id}";s:6:"params";a:3:{s:9:"unit_view";s:7:"gallery";s:13:"empty_message";b:0;s:13:"ajax_paginate";b:0;}}}', 0, 1, 1),
+('bx_events_favorites', 3, 'bx_events', '', '_bx_events_page_block_title_favorites_entries_info', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:19:"favorites_list_info";}', 0, 0, 0),
+('bx_events_favorites', 3, 'bx_events', '', '_bx_events_page_block_title_favorites_entries_actions', 13, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:22:"favorites_list_actions";}', 0, 0, 1);
+
+
+-- PAGE: add block to homepage
+SET @iBlockOrder = (SELECT `order` FROM `sys_pages_blocks` WHERE `object` = 'sys_home' AND `cell_id` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES
+('sys_home', 1, 'bx_events', '', '_bx_events_page_block_title_latest_profiles', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:22:"browse_recent_profiles";}', 1, 0, 0, IFNULL(@iBlockOrder, 0) + 1),
+('sys_home', 1, 'bx_events', '_bx_events_page_block_title_sys_recommended_entries_view_showcase', '_bx_events_page_block_title_recommended_entries_view_showcase', 11, 2147483647, 'service', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:18:"browse_recommended";}', 1, 0, 0, IFNULL(@iBlockOrder, 0) + 2);
+
+-- PAGE: service blocks
+SET @iBlockOrder = (SELECT `order` FROM `sys_pages_blocks` WHERE `object` = '' AND `cell_id` = 0 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title_system`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`) VALUES
+('', 0, 'bx_events', '', '_bx_events_page_block_title_categories', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:15:"categories_list";s:6:"params";a:2:{i:0;s:14:"bx_events_cats";i:1;a:2:{s:10:\"show_empty\";b:1;s:21:\"show_empty_categories\";b:1;}}s:5:"class";s:20:"TemplServiceCategory";}', 1, 1, IFNULL(@iBlockOrder, 0) + 1),
+('', 0, 'bx_events', '_bx_events_page_block_title_sys_featured_entries_view_showcase', '_bx_events_page_block_title_featured_entries_view_showcase', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:15:\"browse_featured\";s:6:\"params\";a:3:{s:9:\"unit_view\";s:8:\"showcase\";s:13:\"empty_message\";b:0;s:13:\"ajax_paginate\";b:0;}}', 0, 1, IFNULL(@iBlockOrder, 0) + 2),
+('', 0, 'bx_events', '_bx_events_page_block_title_sys_recommended_entries_view_showcase', '_bx_events_page_block_title_recommended_entries_view_showcase', 11, 2147483647, 'service', 'a:3:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:18:\"browse_recommended\";s:6:\"params\";a:3:{s:9:\"unit_view\";s:8:\"showcase\";s:13:\"empty_message\";b:0;s:13:\"ajax_paginate\";b:0;}}', 0,  1, IFNULL(@iBlockOrder, 0) + 3),
+('', 0, 'bx_events', '_bx_events_page_block_title_sys_cover_block', '_bx_events_page_block_title_cover_block', 3, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_events\";s:6:\"method\";s:12:\"entity_cover\";}', 0, 1, IFNULL(@iBlockOrder, 0) + 4);
+ 
+ -- PAGES: add page block to profiles modules (trigger* page objects are processed separately upon modules enable/disable)
+INSERT INTO `sys_pages_blocks` (`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `order`, `active`) VALUES
+('trigger_page_group_view_entry', 2, 'bx_events', '_bx_events_page_block_title_calendar_for_context', 11, 2147483647, 'service', 'a:4:{s:6:"module";s:9:"bx_events";s:6:"method";s:8:"calendar";s:12:"ignore_cache";b:1;s:6:"params";a:1:{i:0;a:1:{s:10:"context_id";s:12:"{profile_id}";}}}', 0, 0, 0, 0),
+('trigger_page_group_view_entry', 4, 'bx_events', '_bx_events_page_block_title_browse_for_context', 11, 2147483647, 'service', 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:14:"browse_context";s:6:"params";a:1:{s:10:"context_id";s:12:"{profile_id}";}}', 0, 0, 1, 0);
+
+
+-- MENU
+
+-- MENU: add to site menu
+
+SET @iSiteMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_site' AND `active` = 1 AND `order` < 9999 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_site', 'bx_events', 'events-home', '_bx_events_menu_item_title_system_entries_home', '_bx_events_menu_item_title_entries_home', 'page.php?i=events-home', '', '', 'calendar col-red2', 'bx_events_submenu', 2147483647, 1, 1, IFNULL(@iSiteMenuOrder, 0) + 1);
+
+-- MENU: add to homepage menu
+
+SET @iHomepageMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_homepage' AND `active` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_homepage', 'bx_events', 'events-home', '_bx_events_menu_item_title_system_entries_home', '_bx_events_menu_item_title_entries_home', 'page.php?i=events-home', '', '', 'calendar col-red2', 'bx_events_submenu', 2147483647, 1, 1, IFNULL(@iHomepageMenuOrder, 0) + 1);
+
+-- MENU: add to "add content" menu
+
+SET @iAddMenuOrder = (SELECT `order` FROM `sys_menu_items` WHERE `set_name` = 'sys_add_content_links' AND `active` = 1 ORDER BY `order` DESC LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_add_content_links', 'bx_events', 'create-event-profile', '_bx_events_menu_item_title_system_create_profile', '_bx_events_menu_item_title_create_profile', 'page.php?i=create-event-profile', '', '', 'calendar col-red2', '', 2147483647, 1, 1, IFNULL(@iAddMenuOrder, 0) + 1);
+
+-- MENU: view actions
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_view_actions', '_bx_events_menu_title_view_profile_actions', 'bx_events_view_actions', 'bx_events', 9, 0, 1, 'BxEventsMenuViewActions', 'modules/boonex/events/classes/BxEventsMenuViewActions.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_events_view_actions', 'bx_events', '_bx_events_menu_set_title_view_profile_actions', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `editable`, `order`) VALUES 
+('bx_events_view_actions', 'bx_events', 'join-event-profile', '_bx_events_menu_item_title_system_pay_and_join', '_bx_events_menu_item_title_pay_and_join', 'page.php?i=join-event-profile&profile_id={profile_id}', '', '', 'sign-in-alt', '', 0, 2147483647, 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:22:"is_paid_join_avaliable";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 1, 0, 0, 5),
+('bx_events_view_actions', 'bx_events', 'profile-fan-add', '_bx_events_menu_item_title_system_become_fan', '{title_add_fan}', 'javascript:void(0)', '{onclick_add_fan}', '', 'sign-in-alt', '', 0, 2147483647, 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:22:"is_free_join_avaliable";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 1, 0, 0, 5),
+('bx_events_view_actions', 'bx_events', 'profile-fan-remove', '_bx_events_menu_item_title_system_leave_group', '{title_remove_fan}', 'javascript:void(0)', 'bx_conn_action(this, \'bx_events_fans\', \'remove\', \'{profile_id}\')', '', 'sign-out-alt', '', 0, 2147483647, '', 1, 0, 1, 6),
+('bx_events_view_actions', 'bx_events', 'profile-subscribe-add', '_bx_events_menu_item_title_system_subscribe', '_bx_events_menu_item_title_subscribe', 'javascript:void(0)', 'bx_conn_action(this, \'sys_profiles_subscriptions\', \'add\', \'{profile_id}\')', '', 'check', '', 0, 2147483647, '', 1, 0, 1, 20),
+('bx_events_view_actions', 'bx_events', 'profile-subscribe-remove', '_bx_events_menu_item_title_system_unsubscribe', '_bx_events_menu_item_title_unsubscribe', 'javascript:void(0)', 'bx_conn_action(this, \'sys_profiles_subscriptions\', \'remove\', \'{profile_id}\')', '', 'check', '', 0, 2147483647, '', 1, 0, 1, 21),
+('bx_events_view_actions', 'bx_events', 'profile-check-in', '_bx_events_menu_item_title_system_check_in', '_bx_events_menu_item_title_check_in', 'javascript:void(0)', 'javascript:{js_object_entry}.checkIn(this, {content_id});', '', 'check', '', 0, 2147483647, '', 1, 0, 1, 25),
+('bx_events_view_actions', 'bx_events', 'profile-set-badges', '_sys_menu_item_title_system_set_badges', '_sys_menu_item_title_set_badges', 'javascript:void(0)', 'bx_menu_popup(''sys_set_badges'', window, {}, {module: ''bx_events'', content_id: {content_id}});', '', 'check-circle', '', 0, 192, 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:19:"is_badges_avaliable";}', 1, 0, 1, 30),
+('bx_events_view_actions', 'bx_events', 'ical-export', '', '_bx_events_menu_item_title_ical_export', 'modules/?r=events/calendar_sync/{content_id}', '', '', 'calendar-plus', '', 0, 2147483647, 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:24:"is_ical_export_avaliable";s:6:"params";a:1:{i:0;s:12:"{content_id}";}}', 1, 0, 1, 40),
+('bx_events_view_actions', 'bx_events', 'profile-actions-more', '_bx_events_menu_item_title_system_more_actions', '_bx_events_menu_item_title_more_actions', 'javascript:void(0)', 'bx_menu_popup(''bx_events_view_actions_more'', this, {}, {profile_id:{profile_id}});', '', 'cog', 'bx_events_view_actions_more', 1, 2147483647, '', 1, 0, 1, 9999);
+
+-- MENU: view actions more
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_view_actions_more', '_bx_events_menu_title_view_profile_actions_more', 'bx_events_view_actions_more', 'bx_events', 6, 0, 1, 'BxEventsMenuViewActions', 'modules/boonex/events/classes/BxEventsMenuViewActions.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_events_view_actions_more', 'bx_events', '_bx_events_menu_set_title_view_profile_actions_more', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `order`) VALUES 
+('bx_events_view_actions_more', 'bx_events', 'event-manage', '_bx_events_menu_item_title_system_view_manage', '_bx_events_menu_item_title_view_manage', 'page.php?i=event-manage&profile_id={profile_id}', '', '', 'users', '', 2147483647, '', 1, 0, 10),
+('bx_events_view_actions_more', 'bx_events', 'notes', '_sys_menu_item_title_system_va_notes', '_sys_menu_item_title_va_notes', 'javascript:void(0)', 'javascript:bx_get_notes(this,  ''{module_uri}'', {content_id});', '', 'exclamation-triangle', '', 192, '', 1, 0, 20),
+('bx_events_view_actions_more', 'bx_events', 'audit', '_sys_menu_item_title_system_va_audit', '_sys_menu_item_title_va_audit', 'page.php?i=dashboard-audit&module=bx_events&content_id={content_id}', '', '', 'history', '', 192, '', 1, 0, 30),
+('bx_events_view_actions_more', 'bx_events', 'edit-event-profile', '_bx_events_menu_item_title_system_edit_profile', '_bx_events_menu_item_title_edit_profile', 'page.php?i=edit-event-profile&id={content_id}', '', '', 'pencil-alt', '', 2147483647, '', 1, 0, 40),
+('bx_events_view_actions_more', 'bx_events', 'event-questionnaire', '_bx_events_menu_item_title_system_edit_questionnaire', '_bx_events_menu_item_title_edit_questionnaire', 'page.php?i=event-questionnaire&profile_id={profile_id}', '', '', 'check-double', '', 2147483647, '', 1, 0, 41),
+('bx_events_view_actions_more', 'bx_events', 'event-sessions', '_bx_events_menu_item_title_system_edit_sessions', '_bx_events_menu_item_title_edit_sessions', 'page.php?i=event-sessions&profile_id={profile_id}', '', '', 'calendar-day', '', 2147483647, '', 1, 0, 42),
+('bx_events_view_actions_more', 'bx_events', 'event-pricing', '_bx_events_menu_item_title_system_edit_pricing', '_bx_events_menu_item_title_edit_pricing', 'page.php?i=event-pricing&profile_id={profile_id}', '', '', 'money-check-alt', '', 2147483647, 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:20:"is_pricing_avaliable";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 1, 0, 43),
+('bx_events_view_actions_more', 'bx_events', 'invite-to-event', '_bx_events_menu_item_title_system_invite', '_bx_events_menu_item_title_invite', 'page.php?i=invite-to-event&id={content_id}', '', '', 'user-friends', '', 2147483647, '', 1, 0, 44),
+('bx_events_view_actions_more', 'bx_events', 'delete-event-profile', '_bx_events_menu_item_title_system_delete_profile', '_bx_events_menu_item_title_delete_profile', 'page.php?i=delete-event-profile&id={content_id}', '', '', 'remove', '', 2147483647, '', 1, 0, 50),
+('bx_events_view_actions_more', 'bx_events', 'approve-event-profile', '_sys_menu_item_title_system_va_approve', '_sys_menu_item_title_va_approve', 'javascript:void(0)', 'javascript:bx_approve(this, ''{module_uri}'', {content_id});', '', 'check', '', 2147483647, '', 1, 0, 60);
+
+-- MENU: all actions menu for view entry 
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `persistent`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_view_actions_all', '_sys_menu_title_view_actions', 'bx_events_view_actions_all', 'bx_events', 15, 1, 0, 1, 'BxEventsMenuViewActionsAll', 'modules/boonex/events/classes/BxEventsMenuViewActionsAll.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_events_view_actions_all', 'bx_events', '_sys_menu_set_title_view_actions', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `order`) VALUES 
+('bx_events_view_actions_all', 'bx_events', 'join-event-profile', '_bx_events_menu_item_title_system_pay_and_join', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 5),
+('bx_events_view_actions_all', 'bx_events', 'profile-fan-add', '_bx_events_menu_item_title_system_become_fan', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 10),
+('bx_events_view_actions_all', 'bx_events', 'profile-fan-remove', '_bx_events_menu_item_title_system_leave_group', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 20),
+('bx_events_view_actions_all', 'bx_events', 'profile-subscribe-add', '_bx_events_menu_item_title_system_subscribe', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 30),
+('bx_events_view_actions_all', 'bx_events', 'profile-subscribe-remove', '_bx_events_menu_item_title_system_unsubscribe', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 40),
+('bx_events_view_actions_all', 'bx_events', 'profile-check-in', '_bx_events_menu_item_title_system_check_in', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 45),
+('bx_events_view_actions_all', 'bx_events', 'profile-set-badges', '_sys_menu_item_title_system_set_badges', '', '', '', '', '', '', '', 0, 192, 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:19:"is_badges_avaliable";}', 1, 0, 50),
+('bx_events_view_actions_all', 'bx_events', 'ical-export', '', '_bx_events_menu_item_title_ical_export', 'modules/?r=events/calendar_sync/{content_id}', '', '', 'calendar-plus', '', '', 0, 2147483647, 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:24:"is_ical_export_avaliable";s:6:"params";a:1:{i:0;s:12:"{content_id}";}}', 1, 0, 60),
+('bx_events_view_actions_all', 'bx_events', 'comment', '_sys_menu_item_title_system_va_comment', '', '', '', '', '', '', '', 0, 2147483647, '', 0, 0, 200),
+('bx_events_view_actions_all', 'bx_events', 'view', '_sys_menu_item_title_system_va_view', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 210),
+('bx_events_view_actions_all', 'bx_events', 'vote', '_sys_menu_item_title_system_va_vote', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 220),
+('bx_events_view_actions_all', 'bx_events', 'score', '_sys_menu_item_title_system_va_score', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 230),
+('bx_events_view_actions_all', 'bx_events', 'favorite', '_sys_menu_item_title_system_va_favorite', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 240),
+('bx_events_view_actions_all', 'bx_events', 'feature', '_sys_menu_item_title_system_va_feature', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 250),
+('bx_events_view_actions_all', 'bx_events', 'repost', '_sys_menu_item_title_system_va_repost', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 260),
+('bx_events_view_actions_all', 'bx_events', 'report', '_sys_menu_item_title_system_va_report', '', '', '', '', '', '', '', 0, 2147483647, '', 1, 0, 270),
+('bx_events_view_actions_all', 'bx_events', 'social-sharing', '_sys_menu_item_title_system_social_sharing', '_sys_menu_item_title_social_sharing', 'javascript:void(0)', 'oBxDolPage.share(this, \'{url_encoded}\')', '', 'share', '', '', 0, 2147483647, '', 1, 0, 300),
+('bx_events_view_actions_all', 'bx_events', 'more-auto', '_sys_menu_item_title_system_va_more_auto', '_sys_menu_item_title_va_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', '', 0, 2147483647, '', 1, 0, 9999);
+
+-- MENU: meta (counters) menu for view entry
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_view_meta', '_bx_events_menu_title_view_profile_meta', 'bx_events_view_meta', 'bx_events', 15, 0, 1, 'BxEventsMenuViewMeta', 'modules/boonex/events/classes/BxEventsMenuViewMeta.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_events_view_meta', 'bx_events', '_bx_events_menu_set_title_view_profile_meta', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `order`) VALUES 
+('bx_events_view_meta', 'bx_events', 'members', '_sys_menu_item_title_system_sm_members', '_bx_events_menu_item_title_sm_members', '', '', '', '', '', 0, 2147483647, '', 1, 0, 10),
+('bx_events_view_meta', 'bx_events', 'subscribers', '_sys_menu_item_title_system_sm_subscribers', '_bx_events_menu_item_title_sm_subscribers', '', '', '', '', '', 0, 2147483647, '', 1, 0, 20),
+('bx_events_view_meta', 'bx_events', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 0, 2147483647, '', 1, 0, 30),
+('bx_events_view_meta', 'bx_events', 'votes', '_sys_menu_item_title_system_sm_votes', '_sys_menu_item_title_sm_votes', '', '', '', '', '', 0, 2147483647, '', 1, 0, 40),
+('bx_events_view_meta', 'bx_events', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 0, 2147483647, '', 1, 0, 50);
+
+-- MENU: actions menu for my entries
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_my', '_bx_events_menu_title_entries_my', 'bx_events_my', 'bx_events', 9, 0, 1, 'BxEventsMenu', 'modules/boonex/events/classes/BxEventsMenu.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_events_my', 'bx_events', '_bx_events_menu_set_title_entries_my', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_events_my', 'bx_events', 'create-event-profile', '_bx_events_menu_item_title_system_create_profile', '_bx_events_menu_item_title_create_profile', 'page.php?i=create-event-profile', '', '', 'plus', '', 2147483647, 1, 0, 0);
+
+-- MENU: module sub-menu
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_submenu', '_bx_events_menu_title_submenu', 'bx_events_submenu', 'bx_events', 8, 0, 1, '', '');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_events_submenu', 'bx_events', '_bx_events_menu_set_title_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_events_submenu', 'bx_events', 'events-home', '_bx_events_menu_item_title_system_entries_recent', '_bx_events_menu_item_title_entries_recent', 'page.php?i=events-home', '', '', '', '', 2147483647, 1, 1, 1),
+('bx_events_submenu', 'bx_events', 'events-top', '_bx_events_menu_item_title_system_entries_top', '_bx_events_menu_item_title_entries_top', 'page.php?i=events-top', '', '', '', '', 2147483647, 1, 1, 2),
+('bx_events_submenu', 'bx_events', 'events-upcoming', '_bx_events_menu_item_title_system_entries_upcoming', '_bx_events_menu_item_title_entries_upcoming', 'page.php?i=events-upcoming', '', '', '', '', 2147483647, 1, 1, 3),
+('bx_events_submenu', 'bx_events', 'events-past', '_bx_events_menu_item_title_system_entries_past', '_bx_events_menu_item_title_entries_past', 'page.php?i=events-past', '', '', '', '', 2147483647, 1, 1, 4),
+('bx_events_submenu', 'bx_events', 'events-calendar', '_bx_events_menu_item_title_system_calendar', '_bx_events_menu_item_title_calendar', 'page.php?i=events-calendar', '', '', '', '', 2147483647, 1, 1, 5),
+('bx_events_submenu', 'bx_events', 'events-search', '_bx_events_menu_item_title_system_entries_search', '_bx_events_menu_item_title_entries_search', 'page.php?i=events-search', '', '', '', '', 2147483647, 1, 1, 6),
+('bx_events_submenu', 'bx_events', 'events-joined', '_bx_events_menu_item_title_system_entries_joined', '_bx_events_menu_item_title_entries_joined', 'page.php?i=events-joined', '', '', '', '', 2147483647, 1, 1, 7),
+('bx_events_submenu', 'bx_events', 'events-followed', '_bx_events_menu_item_title_system_entries_followed', '_bx_events_menu_item_title_entries_followed', 'page.php?i=events-followed', '', '', '', '', 2147483647, 1, 1, 8),
+('bx_events_submenu', 'bx_events', 'events-manage', '_bx_events_menu_item_title_system_entries_manage', '_bx_events_menu_item_title_entries_manage', 'page.php?i=events-manage', '', '', '', '', 2147483646, 1, 1, 9);
+
+-- MENU: view submenu
+
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_view_submenu', '_bx_events_menu_title_view_profile_submenu', 'bx_events_view_submenu', 'bx_events', 18, 0, 1, 'BxEventsMenuView', 'modules/boonex/events/classes/BxEventsMenuView.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_events_view_submenu', 'bx_events', '_bx_events_menu_set_title_view_profile_submenu', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `submenu_popup`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_events_view_submenu', 'bx_events', 'view-event-profile', '_bx_events_menu_item_title_system_view_profile_view', '_bx_events_menu_item_title_view_profile_view', 'page.php?i=view-event-profile&id={content_id}', '', '', 'calendar col-red2', '', '', 0, 2147483647, 1, 0, 1),
+('bx_events_view_submenu', 'bx_events', 'event-profile-info', '_bx_events_menu_item_title_system_view_profile_info', '_bx_events_menu_item_title_view_profile_info', 'page.php?i=event-profile-info&id={content_id}', '', '', 'info-circle col-gray', '', '', 0, 2147483647, 1, 0, 2),
+('bx_events_view_submenu', 'bx_events', 'event-profile-comments', '_bx_events_menu_item_title_system_view_profile_comments', '_bx_events_menu_item_title_view_profile_comments', 'page.php?i=event-profile-comments&id={content_id}', '', '', '', '', '', 0, 2147483647, 0, 0, 3),
+('bx_events_view_submenu', 'bx_events', 'event-fans', '_bx_events_menu_item_title_system_view_fans', '_bx_events_menu_item_title_view_fans', 'page.php?i=event-fans&profile_id={profile_id}', '', '', 'calendar col-blue3', '', '', 0, 2147483647, 1, 0, 4),
+('bx_events_view_submenu', 'bx_events', 'more-auto', '_bx_events_menu_item_title_system_view_profile_more_auto', '_bx_events_menu_item_title_view_profile_more_auto', 'javascript:void(0)', '', '', 'ellipsis-v', '', '', 0, 2147483647, 1, 0, 9999);
+
+-- MENU: custom menu for snippet meta info
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_snippet_meta', '_sys_menu_title_snippet_meta', 'bx_events_snippet_meta', 'bx_events', 15, 0, 1, 'BxEventsMenuSnippetMeta', 'modules/boonex/events/classes/BxEventsMenuSnippetMeta.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_events_snippet_meta', 'bx_events', '_sys_menu_set_title_snippet_meta', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `editable`, `order`) VALUES
+('bx_events_snippet_meta', 'bx_events', 'date', '_sys_menu_item_title_system_sm_date', '_sys_menu_item_title_sm_date', '', '', '', '', '', 2147483647, '', 0, 0, 1, 1),
+('bx_events_snippet_meta', 'bx_events', 'date-start', '_bx_events_menu_item_title_system_sm_date_start', '_bx_events_menu_item_title_sm_date_start', '', '', '', '', '', 2147483647, '', 0, 0, 1, 2),
+('bx_events_snippet_meta', 'bx_events', 'date-end', '_bx_events_menu_item_title_system_sm_date_end', '_bx_events_menu_item_title_sm_date_end', '', '', '', '', '', 2147483647, '', 0, 0, 1, 3),
+('bx_events_snippet_meta', 'bx_events', 'category', '_sys_menu_item_title_system_sm_category', '_sys_menu_item_title_sm_category', '', '', '', '', '', 2147483647, '', 0, 0, 1, 5),
+('bx_events_snippet_meta', 'bx_events', 'tags', '_sys_menu_item_title_system_sm_tags', '_sys_menu_item_title_sm_tags', '', '', '', '', '', 2147483647, '', 0, 0, 1, 10),
+('bx_events_snippet_meta', 'bx_events', 'views', '_sys_menu_item_title_system_sm_views', '_sys_menu_item_title_sm_views', '', '', '', '', '', 2147483647, '', 0, 0, 1, 15),
+('bx_events_snippet_meta', 'bx_events', 'comments', '_sys_menu_item_title_system_sm_comments', '_sys_menu_item_title_sm_comments', '', '', '', '', '', 2147483647, '', 0, 0, 1, 20),
+('bx_events_snippet_meta', 'bx_events', 'members', '_sys_menu_item_title_system_sm_members', '_sys_menu_item_title_sm_members', '', '', '', '', '', 2147483647, '', 1, 0, 1, 25),
+('bx_events_snippet_meta', 'bx_events', 'subscribers', '_sys_menu_item_title_system_sm_subscribers', '_sys_menu_item_title_sm_subscribers', '', '', '', '', '', 2147483647, '', 0, 0, 1, 30),
+('bx_events_snippet_meta', 'bx_events', 'country', '_sys_menu_item_title_system_sm_country', '_sys_menu_item_title_sm_country', '', '', '', '', '', 2147483647, '', 0, 0, 1, 35),
+('bx_events_snippet_meta', 'bx_events', 'country-city', '_sys_menu_item_title_system_sm_country_city', '_sys_menu_item_title_sm_country_city', '', '', '', '', '', 2147483647, '', 0, 0, 1, 40),
+('bx_events_snippet_meta', 'bx_events', 'nl', '_sys_menu_item_title_system_sm_nl', '_sys_menu_item_title_sm_nl', '', '', '', '', '', 2147483647, '', 1, 0, 1, 45),
+('bx_events_snippet_meta', 'bx_events', 'join-paid', '_sys_menu_item_title_system_sm_join_paid', '_sys_menu_item_title_sm_join_paid', '', '', '', '', '', 2147483647, 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:33:"is_paid_join_avaliable_by_content";s:6:"params";a:1:{i:0;s:12:"{content_id}";}}', 1, 0, 1, 50),
+('bx_events_snippet_meta', 'bx_events', 'join', '_sys_menu_item_title_system_sm_join', '_sys_menu_item_title_sm_join', '', '', '', '', '', 2147483647, 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:33:"is_free_join_avaliable_by_content";s:6:"params";a:1:{i:0;s:12:"{content_id}";}}', 1, 0, 1, 55),
+('bx_events_snippet_meta', 'bx_events', 'leave', '_sys_menu_item_title_system_sm_leave', '_sys_menu_item_title_sm_leave', '', '', '', '', '', 2147483647, '', 0, 0, 1, 60),
+('bx_events_snippet_meta', 'bx_events', 'subscribe', '_sys_menu_item_title_system_sm_subscribe', '_sys_menu_item_title_sm_subscribe', '', '', '', '', '', 2147483647, '', 0, 0, 1, 65),
+('bx_events_snippet_meta', 'bx_events', 'unsubscribe', '_sys_menu_item_title_system_sm_unsubscribe', '_sys_menu_item_title_sm_unsubscribe', '', '', '', '', '', 2147483647, '', 0, 0, 1, 70);
+
+-- MENU: profile stats
+SET @iNotifMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name` = 'sys_profile_stats' AND `active` = 1 LIMIT 1);
+INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
+('sys_profile_stats', 'bx_events', 'profile-stats-my-events', '_bx_events_menu_item_title_system_manage_my_groups', '_bx_events_menu_item_title_manage_my_groups', 'page.php?i=joined-events&profile_id={member_id}', '', '_self', 'calendar col-red2', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:41:"get_menu_addon_manage_tools_profile_stats";}', '', 2147483646, 1, 0, @iNotifMenuOrder + 2);
+
+-- MENU: profile followings
+SET @iFollowingsMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_profile_followings' LIMIT 1);
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `collapsed`, `active`, `copyable`, `order`) VALUES 
+('sys_profile_followings', 'bx_events', 'events', '_bx_events_menu_item_title_system_followings', '_bx_events_menu_item_title_followings', 'javascript:void(0)', '', '_self', 'calendar col-red2', '', '', 2147483647, 0, 1, 0, @iFollowingsMenuOrder + 1);
+
+-- MENU: manage tools submenu
+INSERT INTO `sys_objects_menu`(`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
+('bx_events_menu_manage_tools', '_bx_events_menu_title_manage_tools', 'bx_events_menu_manage_tools', 'bx_events', 6, 0, 1, 'BxEventsMenuManageTools', 'modules/boonex/events/classes/BxEventsMenuManageTools.php');
+
+INSERT INTO `sys_menu_sets`(`set_name`, `module`, `title`, `deletable`) VALUES 
+('bx_events_menu_manage_tools', 'bx_events', '_bx_events_menu_set_title_manage_tools', 0);
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('bx_events_menu_manage_tools', 'bx_events', 'clear-reports', '_bx_events_menu_item_title_system_clear_reports', '_bx_events_menu_item_title_clear_reports', 'javascript:void(0)', 'javascript:{js_object}.onClickClearReports({content_id});', '_self', 'eraser', '', 2147483647, 1, 0, 1),
+('bx_events_menu_manage_tools', 'bx_events', 'delete', '_bx_events_menu_item_title_system_delete', '_bx_events_menu_item_title_delete', 'javascript:void(0)', 'javascript:{js_object}.onClickDelete({content_id});', '_self', 'far trash-alt', '', 2147483647, 1, 0, 2),
+('bx_events_menu_manage_tools', 'bx_events', 'delete-with-content', '_bx_events_menu_item_title_system_delete_with_content', '_bx_events_menu_item_title_delete_with_content', 'javascript:void(0)', 'javascript:{js_object}.onClickDeleteWithContent({content_id});', '_self', 'far trash-alt', '', 2147483647, 0, 0, 3);
+
+-- MENU: dashboard manage tools
+SET @iManageMenuOrder = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_menu_items` WHERE `set_name`='sys_account_dashboard_manage_tools' LIMIT 1);
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_account_dashboard_manage_tools', 'bx_events', 'events-administration', '_bx_events_menu_item_title_system_admt_groups', '_bx_events_menu_item_title_admt_groups', 'page.php?i=events-administration', '', '_self', 'calendar', 'a:2:{s:6:"module";s:9:"bx_events";s:6:"method";s:27:"get_menu_addon_manage_tools";}', '', 192, 1, 0, @iManageMenuOrder + 1);
+
+-- MENU: add menu item to profiles modules (trigger* menu sets are processed separately upon modules enable/disable)
+
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `visibility_custom`, `active`, `copyable`, `order`) VALUES 
+('trigger_profile_view_submenu', 'bx_events', 'joined-events', '_bx_events_menu_item_title_system_view_joined_groups', '_bx_events_menu_item_title_view_joined_groups', 'page.php?i=joined-events&profile_id={profile_id}', '', '', 'calendar col-red2', '', 2147483647, '', 1, 0, 0),
+('trigger_group_view_submenu', 'bx_events', 'events-context', '_bx_events_menu_item_title_system_view_entries_in_context', '_bx_events_menu_item_title_view_entries_in_context', 'page.php?i=events-context&profile_id={profile_id}', '', '', 'calendar col-red2', '', 2147483647, 'a:3:{s:6:"module";s:9:"bx_events";s:6:"method";s:21:"is_enable_for_context";s:6:"params";a:1:{i:0;s:12:"{profile_id}";}}', 1, 0, 0);
+
+
+-- ACL
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_events', 'create entry', NULL, '_bx_events_acl_action_create_profile', '', 1, 1);
+SET @iIdActionProfileCreate = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_events', 'use paid join', NULL, '_bx_events_acl_action_use_paid_join', '', 1, 1);
+SET @iIdActionUsePaidJoin = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_events', 'delete entry', NULL, '_bx_events_acl_action_delete_profile', '', 1, 1);
+SET @iIdActionProfileDelete = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_events', 'view entry', NULL, '_bx_events_acl_action_view_profile', '', 1, 0);
+SET @iIdActionProfileView = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_events', 'edit any entry', NULL, '_bx_events_acl_action_edit_any_profile', '', 1, 3);
+SET @iIdActionProfileEditAny = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_events', 'delete any entry', NULL, '_bx_events_acl_action_delete_any_profile', '', 1, 3);
+SET @iIdActionProfileDeleteAny = LAST_INSERT_ID();
+
+INSERT INTO `sys_acl_actions` (`Module`, `Name`, `AdditionalParamName`, `Title`, `Desc`, `Countable`, `DisabledForLevels`) VALUES
+('bx_events', 'delete invites', NULL, '_bx_events_acl_action_delete_invites', '', 1, 3);
+SET @iIdActionProfileDeleteInvites = LAST_INSERT_ID();
+
+SET @iUnauthenticated = 1;
+SET @iAccount = 2;
+SET @iStandard = 3;
+SET @iUnconfirmed = 4;
+SET @iPending = 5;
+SET @iSuspended = 6;
+SET @iModerator = 7;
+SET @iAdministrator = 8;
+SET @iPremium = 9;
+
+INSERT INTO `sys_acl_matrix` (`IDLevel`, `IDAction`) VALUES
+
+-- profile create
+(@iAccount, @iIdActionProfileCreate),
+(@iStandard, @iIdActionProfileCreate),
+(@iUnconfirmed, @iIdActionProfileCreate),
+(@iPending, @iIdActionProfileCreate),
+(@iModerator, @iIdActionProfileCreate),
+(@iAdministrator, @iIdActionProfileCreate),
+(@iPremium, @iIdActionProfileCreate),
+
+-- use paid join
+(@iStandard, @iIdActionUsePaidJoin),
+(@iModerator, @iIdActionUsePaidJoin),
+(@iAdministrator, @iIdActionUsePaidJoin),
+(@iPremium, @iIdActionUsePaidJoin),
+
+-- profile delete
+(@iAccount, @iIdActionProfileDelete),
+(@iStandard, @iIdActionProfileDelete),
+(@iUnconfirmed, @iIdActionProfileDelete),
+(@iPending, @iIdActionProfileDelete),
+(@iModerator, @iIdActionProfileDelete),
+(@iAdministrator, @iIdActionProfileDelete),
+(@iPremium, @iIdActionProfileDelete),
+
+-- profile view
+(@iUnauthenticated, @iIdActionProfileView),
+(@iAccount, @iIdActionProfileView),
+(@iStandard, @iIdActionProfileView),
+(@iUnconfirmed, @iIdActionProfileView),
+(@iPending, @iIdActionProfileView),
+(@iModerator, @iIdActionProfileView),
+(@iAdministrator, @iIdActionProfileView),
+(@iPremium, @iIdActionProfileView),
+
+-- any profile edit
+(@iModerator, @iIdActionProfileEditAny),
+(@iAdministrator, @iIdActionProfileEditAny),
+
+-- any profile delete
+(@iAdministrator, @iIdActionProfileDeleteAny),
+
+-- any invites edit
+(@iModerator, @iIdActionProfileDeleteInvites),
+(@iAdministrator, @iIdActionProfileDeleteInvites);
+
+-- METATAGS
+INSERT INTO `sys_objects_metatags` (`object`, `module`, `table_keywords`, `table_locations`, `table_mentions`, `override_class_name`, `override_class_file`) VALUES
+('bx_events', 'bx_events', 'bx_events_meta_keywords', 'bx_events_meta_locations', 'bx_events_meta_mentions', '', '');
+
+-- CATEGORY
+INSERT INTO `sys_objects_category` (`object`, `module`, `search_object`, `form_object`, `list_name`, `table`, `field`, `join`, `where`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_cats', 'bx_events', 'bx_events', 'bx_event', 'bx_events_cats', 'bx_events_data', 'event_cat', 'INNER JOIN `sys_profiles` ON (`sys_profiles`.`content_id` = `bx_events_data`.`id` AND `sys_profiles`.`type` = ''bx_events'')', 'AND `sys_profiles`.`status` = ''active''', '', '');
+
+-- SEARCH
+SET @iSearchOrder = (SELECT IFNULL(MAX(`Order`), 0) FROM `sys_objects_search`);
+INSERT INTO `sys_objects_search` (`ObjectName`, `Title`, `Order`, `ClassName`, `ClassPath`) VALUES
+('bx_events', '_bx_events', @iSearchOrder + 1, 'BxEventsSearchResult', 'modules/boonex/events/classes/BxEventsSearchResult.php');
+
+-- CONNECTIONS
+INSERT INTO `sys_objects_connection` (`object`, `table`, `profile_initiator`, `profile_content`, `type`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_fans', 'bx_events_fans', 1, 1, 'mutual', 'BxEventsConnectionFans', 'modules/boonex/events/classes/BxEventsConnectionFans.php');
+
+-- STATS
+SET @iMaxOrderStats = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_statistics`);
+INSERT INTO `sys_statistics` (`module`, `name`, `title`, `link`, `icon`, `query`, `order`) VALUES 
+('bx_events', 'bx_events', '_bx_events', 'page.php?i=events-home', 'calendar col-red2', 'SELECT COUNT(*) FROM `bx_events_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id` = `tp`.`content_id` AND `tp`.`type`=''bx_events'' WHERE 1 AND `tp`.`status`=''active''', @iMaxOrderStats + 1);
+
+-- CHARTS
+SET @iMaxOrderCharts = (SELECT IFNULL(MAX(`order`), 0) FROM `sys_objects_chart`);
+INSERT INTO `sys_objects_chart` (`object`, `title`, `table`, `field_date_ts`, `field_date_dt`, `field_status`, `query`, `active`, `order`, `class_name`, `class_file`) VALUES
+('bx_events_growth', '_bx_events_chart_growth', 'bx_events_data', 'added', '', '', 'SELECT {field_date_formatted} AS `period`, COUNT(*) AS {object} FROM {table} LEFT JOIN `sys_profiles` AS `tp` ON {table}.`id` = `tp`.`content_id` AND `tp`.`type`=''bx_events'' WHERE 1 AND `tp`.`status`=''active'' {where_inteval} GROUP BY `period` ORDER BY {table}.{field_date} ASC', 1, @iMaxOrderCharts + 1, 'BxDolChartGrowth', ''),
+('bx_events_growth_speed', '_bx_events_chart_growth_speed', 'bx_events_data', 'added', '', '', 'SELECT {field_date_formatted} AS `period`, COUNT(*) AS {object} FROM {table} LEFT JOIN `sys_profiles` AS `tp` ON {table}.`id` = `tp`.`content_id` AND `tp`.`type`=''bx_events'' WHERE 1 AND `tp`.`status`=''active'' {where_inteval} GROUP BY `period` ORDER BY {table}.{field_date} ASC', 1, @iMaxOrderCharts + 2, 'BxDolChartGrowthSpeed', '');
+
+-- GRID: connections
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `responsive`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_fans', 'Sql', 'SELECT `p`.`id`, `c`.`added`, `c`.`mutual` FROM `sys_profiles` AS `p` INNER JOIN `sys_accounts` AS `a` ON (`a`.`id` = `p`.`account_id`) {join_connections}', 'sys_profiles', 'id', 'c`.`added', '', '', 10, NULL, 'start', '', 'name,email', '', 'auto', '', '', 2147483647, 0, 'BxEventsGridConnections', 'modules/boonex/events/classes/BxEventsGridConnections.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `params`, `order`) VALUES
+('bx_events_fans', 'name', '_sys_name', '40%', '', 10),
+('bx_events_fans', 'role', '_bx_events_txt_role', '10%', '', 15),
+('bx_events_fans', 'role_added', '_bx_events_txt_role_added', '10%', '', 16),
+('bx_events_fans', 'role_expired', '_bx_events_txt_role_expired', '10%', '', 17),
+('bx_events_fans', 'actions', '', '30%', '', 20);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_events_fans', 'single', 'questionnaire', '_bx_events_txt_view_answers', 'check-double', 1, 0, 5),
+('bx_events_fans', 'single', 'accept', '_sys_accept', '', 0, 0, 10),
+('bx_events_fans', 'single', 'set_role', '_bx_events_txt_set_role', '', 0, 0, 20),
+('bx_events_fans', 'single', 'set_role_submit', '', '', 0, 0, 21),
+('bx_events_fans', 'single', 'delete', '', 'remove', 0, 1, 40);
+
+-- GRID: invites
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `responsive`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_invites', 'Sql', 'SELECT `bx_events_invites`.`id`, `bx_events_invites`.`invited_profile_id`, `bx_events_invites`.`added`, `bx_events_invites`.`author_profile_id` FROM `sys_profiles` AS `p` INNER JOIN `sys_accounts` AS `a` ON (`a`.`id` = `p`.`account_id`) INNER JOIN `bx_events_invites` ON `bx_events_invites`.`invited_profile_id` = `p`.`id` ', 'bx_events_invites', 'id', 'bx_events_invites`.`added', '', '', 10, NULL, 'start', '', 'name,email', '', 'auto', '', '', 2147483647, 0, 'BxEventsGridInvites', 'modules/boonex/events/classes/BxEventsGridInvites.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `params`, `order`) VALUES
+('bx_events_invites', 'name', '_sys_name', '33%', '', 10),
+('bx_events_invites', 'added', '_sys_added', '33%', '', 20),
+('bx_events_invites', 'actions', '', '34%', '', 30);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
+('bx_events_invites', 'single', 'delete', '', 'remove', 1, 10);
+
+-- GRIDS: administration
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_administration', 'Sql', 'SELECT `td`.*, `td`.`event_name` AS `name`, `ta`.`email` AS `account`, `td`.`added` AS `added_ts`, `tp`.`status` AS `status_profile` FROM `bx_events_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_events'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_events_data', 'id', 'added', 'status_admin', '', 20, NULL, 'start', '', 'event_name', '', 'like', 'reports', '', 192, 'BxEventsGridAdministration', 'modules/boonex/events/classes/BxEventsGridAdministration.php'),
+('bx_events_common', 'Sql', 'SELECT `td`.*, `td`.`event_name` AS `name`, `ta`.`email` AS `account`, `td`.`added` AS `added_ts`, `tp`.`status` AS `status_profile` FROM `bx_events_data` AS `td` LEFT JOIN `sys_profiles` AS `tp` ON `td`.`id`=`tp`.`content_id` AND `tp`.`type`=''bx_events'' LEFT JOIN `sys_accounts` AS `ta` ON `tp`.`account_id`=`ta`.`id` WHERE 1 ', 'bx_events_data', 'id', 'added', 'status', '', 20, NULL, 'start', '', 'event_name', '', 'like', '', '', 2147483647, 'BxEventsGridCommon', 'modules/boonex/events/classes/BxEventsGridCommon.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_events_administration', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_events_administration', 'switcher', '_bx_events_grid_column_title_adm_active', '8%', 0, '', '', 2),
+('bx_events_administration', 'reports', '_sys_txt_reports_title', '5%', 0, '', '', 3),
+('bx_events_administration', 'name', '_bx_events_grid_column_title_adm_name', '25%', 0, '', '', 4),
+('bx_events_administration', 'added_ts', '_bx_events_grid_column_title_adm_added', '20%', 1, '25', '', 5),
+('bx_events_administration', 'account', '_bx_events_grid_column_title_adm_account', '20%', 0, '25', '', 6),
+('bx_events_administration', 'actions', '', '20%', 0, '', '', 7),
+
+('bx_events_common', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
+('bx_events_common', 'switcher', '_bx_groups_grid_column_title_adm_active', '8%', 0, '', '', 2),
+('bx_events_common', 'name', '_bx_events_grid_column_title_adm_name', '40%', 0, '', '', 3),
+('bx_events_common', 'added_ts', '_bx_events_grid_column_title_adm_added', '30%', 1, '25', '', 4),
+('bx_events_common', 'actions', '', '20%', 0, '', '', 5);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `active`, `order`) VALUES
+('bx_events_administration', 'bulk', 'clear_reports', '_bx_events_grid_action_title_adm_clear_reports', '', 0, 1, 1, 1),
+('bx_events_administration', 'bulk', 'delete', '_bx_events_grid_action_title_adm_delete', '', 0, 1, 1, 2),
+('bx_events_administration', 'bulk', 'delete_with_content', '_bx_events_grid_action_title_adm_delete_with_content', '', 0, 1, 0, 3),
+('bx_events_administration', 'single', 'settings', '_bx_events_grid_action_title_adm_more_actions', 'cog', 1, 0, 1, 2),
+('bx_events_administration', 'single', 'audit_content', '_bx_events_grid_action_title_adm_audit_content', 'search', 1, 0, 1, 3),
+('bx_events_administration', 'single', 'audit_context', '_bx_events_grid_action_title_adm_audit_context', 'search-location', 1, 0, 1, 4),
+
+('bx_events_common', 'bulk', 'delete', '_bx_events_grid_action_title_adm_delete', '', 0, 1, 1, 1),
+('bx_events_common', 'bulk', 'delete_with_content', '_bx_events_grid_action_title_adm_delete_with_content', '', 0, 1, 0, 2),
+('bx_events_common', 'single', 'settings', '_bx_events_grid_action_title_adm_more_actions', 'cog', 1, 0, 1, 1);
+
+-- GRIDS: questionnaire
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_questions_manage', 'Sql', 'SELECT * FROM `bx_events_qnr_questions` WHERE 1 ', 'bx_events_qnr_questions', 'id', 'order', '', '', 100, NULL, 'start', '', 'question', '', 'like', '', '', 2147483647, 'BxEventsGridQuestionsManage', 'modules/boonex/events/classes/BxEventsGridQuestionsManage.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_events_questions_manage', 'checkbox', '_sys_select', '1%', 0, '', '', 1),
+('bx_events_questions_manage', 'order', '', '1%', 0, '', '', 2),
+('bx_events_questions_manage', 'question', '_bx_events_grid_column_title_qn_question', '78%', 0, 64, '', 3),
+('bx_events_questions_manage', 'actions', '', '20%', 0, '', '', 4);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_events_questions_manage', 'independent', 'add', '_bx_events_grid_action_title_qn_add', '', 0, 0, 1),
+('bx_events_questions_manage', 'single', 'edit', '_bx_events_grid_action_title_qn_edit', 'pencil-alt', 1, 0, 1),
+('bx_events_questions_manage', 'single', 'delete', '_bx_events_grid_action_title_qn_delete', 'remove', 1, 1, 2),
+('bx_events_questions_manage', 'bulk', 'delete', '_bx_events_grid_action_title_qn_delete', '', 0, 1, 1);
+
+-- GRIDS: Sessions
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_sessions_manage', 'Sql', 'SELECT * FROM `bx_events_sessions` WHERE 1 ', 'bx_events_sessions', 'id', 'order', '', '', 100, NULL, 'start', '', 'title,description', '', 'like', '', '', 2147483647, 'BxEventsGridSessionsManage', 'modules/boonex/events/classes/BxEventsGridSessionsManage.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_events_sessions_manage', 'checkbox', '_sys_select', '1%', 0, '', '', 1),
+('bx_events_sessions_manage', 'order', '', '1%', 0, '', '', 2),
+('bx_events_sessions_manage', 'title', '_bx_events_grid_column_title_sn_title', '28%', 0, 16, '', 3),
+('bx_events_sessions_manage', 'description', '_bx_events_grid_column_title_sn_description', '30%', 0, 32, '', 4),
+('bx_events_sessions_manage', 'date_start', '_bx_events_grid_column_title_sn_date_start', '10%', 0, 0, '', 5),
+('bx_events_sessions_manage', 'date_end', '_bx_events_grid_column_title_sn_date_end', '10%', 0, 0, '', 6),
+('bx_events_sessions_manage', 'actions', '', '20%', 0, '', '', 7);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_events_sessions_manage', 'independent', 'add', '_bx_events_grid_action_title_sn_add', '', 0, 0, 1),
+('bx_events_sessions_manage', 'single', 'edit', '_bx_events_grid_action_title_sn_edit', 'pencil-alt', 1, 0, 1),
+('bx_events_sessions_manage', 'single', 'delete', '_bx_events_grid_action_title_sn_delete', 'remove', 1, 1, 2),
+('bx_events_sessions_manage', 'bulk', 'delete', '_bx_events_grid_action_title_sn_delete', '', 0, 1, 1);
+
+-- GRIDS: Pricing
+INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `field_id`, `field_order`, `field_active`, `paginate_url`, `paginate_per_page`, `paginate_simple`, `paginate_get_start`, `paginate_get_per_page`, `filter_fields`, `filter_fields_translatable`, `filter_mode`, `sorting_fields`, `sorting_fields_translatable`, `visible_for_levels`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_prices_manage', 'Sql', 'SELECT * FROM `bx_events_prices` WHERE 1 ', 'bx_events_prices', 'id', 'order', '', '', 100, NULL, 'start', '', 'period,period_unit,price', '', 'like', '', '', 2147483647, 'BxEventsGridPricesManage', 'modules/boonex/events/classes/BxEventsGridPricesManage.php'),
+('bx_events_prices_view', 'Sql', 'SELECT * FROM `bx_events_prices` WHERE 1 ', 'bx_events_prices', 'id', 'order', '', '', 100, NULL, 'start', '', 'period,period_unit,price', '', 'like', '', '', 2147483647, 'BxEventsGridPricesView', 'modules/boonex/events/classes/BxEventsGridPricesView.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_events_prices_manage', 'checkbox', '_sys_select', '1%', 0, '', '', 1),
+('bx_events_prices_manage', 'order', '', '1%', 0, '', '', 2),
+('bx_events_prices_manage', 'name', '_bx_events_grid_column_title_name', '38%', 0, 32, '', 3),
+('bx_events_prices_manage', 'price', '_bx_events_grid_column_title_price', '20%', 0, 16, '', 4),
+('bx_events_prices_manage', 'period', '_bx_events_grid_column_title_period', '20%', 0, 16, '', 5),
+('bx_events_prices_manage', 'actions', '', '20%', 0, '', '', 6),
+
+('bx_events_prices_view', 'role_id', '_bx_events_grid_column_title_role_id', '40%', 0, 32, '', 1),
+('bx_events_prices_view', 'price', '_bx_events_grid_column_title_price', '20%', 0, 16, '', 2),
+('bx_events_prices_view', 'period', '_bx_events_grid_column_title_period', '20%', 0, 16, '', 3),
+('bx_events_prices_view', 'actions', '', '20%', 0, '', '', 4);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `icon_only`, `confirm`, `order`) VALUES
+('bx_events_prices_manage', 'independent', 'add', '_bx_events_grid_action_title_add', '', 0, 0, 1),
+('bx_events_prices_manage', 'single', 'edit', '_bx_events_grid_action_title_edit', 'pencil-alt', 1, 0, 1),
+('bx_events_prices_manage', 'single', 'delete', '_bx_events_grid_action_title_delete', 'remove', 1, 1, 2),
+('bx_events_prices_manage', 'bulk', 'delete', '_bx_events_grid_action_title_delete', '', 0, 1, 1),
+
+('bx_events_prices_view', 'single', 'buy', '_bx_events_grid_action_title_buy', 'cart-plus', 0, 0, 1),
+('bx_events_prices_view', 'single', 'subscribe', '_bx_events_grid_action_title_subscribe', 'credit-card', 0, 0, 2),
+('bx_events_prices_view', 'single', 'choose', '_bx_events_grid_action_title_choose', 'far check-square', 0, 0, 3);
+
+
+-- ALERTS
+INSERT INTO `sys_alerts_handlers` (`name`, `class`, `file`, `service_call`) VALUES 
+('bx_events', 'BxEventsAlertsResponse', 'modules/boonex/events/classes/BxEventsAlertsResponse.php', '');
+SET @iHandler := LAST_INSERT_ID();
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('system', 'save_setting', @iHandler),
+('system', 'check_allowed_fan_add', @iHandler),
+('bx_timeline', 'post_common', @iHandler),
+('bx_events_pics', 'file_deleted', @iHandler),
+('bx_events_fans', 'connection_added', @iHandler),
+('bx_events_fans', 'connection_removed', @iHandler),
+('profile', 'delete', @iHandler),
+('bx_events', 'fan_added', @iHandler),
+('bx_events', 'join_invitation', @iHandler),
+('bx_events', 'join_request', @iHandler),
+('bx_events', 'join_request_accepted', @iHandler),
+('bx_events', 'timeline_view', @iHandler),
+('bx_events', 'timeline_post', @iHandler),
+('bx_events', 'timeline_delete', @iHandler),
+('bx_events', 'timeline_comment', @iHandler),
+('bx_events', 'timeline_vote', @iHandler),
+('bx_events', 'timeline_score', @iHandler),
+('bx_events', 'timeline_report', @iHandler),
+('bx_events', 'timeline_repost', @iHandler),
+('bx_events', 'timeline_pin', @iHandler),
+('bx_events', 'timeline_promote', @iHandler);
+
+-- PRIVACY 
+INSERT INTO `sys_objects_privacy` (`object`, `module`, `action`, `title`, `default_group`, `spaces`, `table`, `table_field_id`, `table_field_author`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_allow_view_to', 'bx_events', 'view', '_bx_events_form_profile_input_allow_view_to', '3', 'bx_groups,bx_spaces', 'bx_events_data', 'id', 'author', 'BxEventsPrivacy', 'modules/boonex/events/classes/BxEventsPrivacy.php'),
+('bx_events_allow_view_notification_to', 'bx_events', 'view_event', '_bx_events_form_profile_input_allow_view_notification_to', '3', '', 'bx_notifications_events', 'id', 'object_owner_id', 'BxEventsPrivacyNotifications', 'modules/boonex/events/classes/BxEventsPrivacyNotifications.php'),
+('bx_events_allow_post_to', 'bx_events', 'post', '_bx_events_form_profile_input_allow_post_to', 'p', '', 'bx_events_data', 'id', 'author', 'BxEventsPrivacyPost', 'modules/boonex/events/classes/BxEventsPrivacyPost.php'),
+('bx_events_allow_view_favorite_list', 'bx_events', 'view_favorite_list', '_bx_events_form_profile_input_allow_view_favorite_list', '3', '', 'bx_events_favorites_lists', 'id', 'author_id', 'BxEventsPrivacy', 'modules/boonex/events/classes/BxEventsPrivacy.php');
+
+
+-- EMAIL TEMPLATES
+INSERT INTO `sys_email_templates` (`Module`, `NameSystem`, `Name`, `Subject`, `Body`) VALUES
+('bx_events', '_bx_events_email_join_request', 'bx_events_join_request', '_bx_events_email_join_request_subject', '_bx_events_email_join_request_body'),
+('bx_events', '_bx_events_email_join_reject', 'bx_events_join_reject', '_bx_events_email_join_reject_subject', '_bx_events_email_join_reject_body'),
+('bx_events', '_bx_events_email_join_confirm', 'bx_events_join_confirm', '_bx_events_email_join_confirm_subject', '_bx_events_email_join_confirm_body'),
+('bx_events', '_bx_events_email_fan_remove', 'bx_events_fan_remove', '_bx_events_email_fan_remove_subject', '_bx_events_email_fan_remove_body'),
+('bx_events', '_bx_events_email_fan_become_admin', 'bx_events_fan_become_admin', '_bx_events_email_fan_become_admin_subject', '_bx_events_email_fan_become_admin_body'),
+('bx_events', '_bx_events_email_admin_become_fan', 'bx_events_admin_become_fan', '_bx_events_email_admin_become_fan_subject', '_bx_events_email_admin_become_fan_body'),
+('bx_events', '_bx_events_email_set_role', 'bx_events_set_role', '_bx_events_email_set_role_subject', '_bx_events_email_set_role_body'),
+('bx_events', '_bx_events_email_invitation', 'bx_events_invitation', '_bx_events_email_invitation_subject', '_bx_events_email_invitation_body'),
+('bx_events', '_bx_events_email_reminder', 'bx_events_reminder', '_bx_events_email_reminder_subject', '_bx_events_email_reminder_body');
+
+-- UPLOADERS
+INSERT INTO `sys_objects_uploader` (`object`, `active`, `override_class_name`, `override_class_file`) VALUES
+('bx_events_cover_crop', 1, 'BxEventsUploaderCoverCrop', 'modules/boonex/events/classes/BxEventsUploaderCoverCrop.php'),
+('bx_events_picture_crop', 1, 'BxEventsUploaderPictureCrop', 'modules/boonex/events/classes/BxEventsUploaderPictureCrop.php');
+
+-- CRON
+INSERT INTO `sys_cron_jobs` (`name`, `time`, `class`, `file`, `service_call`) VALUES
+('bx_events_process_reminders', '55 * * * *', 'BxEventsCronProcessReminders', 'modules/boonex/events/classes/BxEventsCronProcessReminders.php', ''),
+('bx_events_pruning', '0 0 * * *', 'BxEventsCronPruning', 'modules/boonex/events/classes/BxEventsCronPruning.php', ''),
+('bx_events_publishing', '* * * * *', 'BxEventsCronPublishing', 'modules/boonex/events/classes/BxEventsCronPublishing.php', '');

+ 51 - 0
modules/boonex/files/updates/14.0.2_14.0.3/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Files',
+    'version_from' => '14.0.2',
+    'version_to' => '14.0.3',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/files/updates/update_14.0.2_14.0.3/',
+    'home_uri' => 'files_update_1402_1403',
+
+    'module_dir' => 'boonex/files/',
+    'module_uri' => 'files',
+
+    'db_prefix' => 'bx_files_',
+    'class_prefix' => 'BxFiles',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Files',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/files/updates/14.0.2_14.0.3/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxFilesUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 220 - 0
modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesConfig.php

@@ -0,0 +1,220 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Files Files
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxFilesConfig extends BxBaseModTextConfig
+{
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $aMenuItems2Methods = array (
+            'approve' => 'checkAllowedApprove',
+            'download-file' => 'checkAllowedView',
+            'edit-file' => 'checkAllowedEdit',
+            'delete-file' => 'checkAllowedDelete',
+        );
+
+        $this->CNF = array (
+
+            // module icon
+            'ICON' => 'far file col-red3',
+
+            // database tables
+            'TABLE_ENTRIES' => $aModule['db_prefix'] . 'main',
+            'TABLE_ENTRIES_FULLTEXT' => 'search_fields',
+            'TABLE_FILES' => $aModule['db_prefix'] . 'files',
+            'TABLE_BOOKMARKS' => $aModule['db_prefix'] . 'bookmarks',
+            'TABLE_FOLDERS' => $aModule['db_prefix'] . 'folders',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_AUTHOR' => 'author',
+            'FIELD_ADDED' => 'added',
+            'FIELD_CHANGED' => 'changed',
+            'FIELD_FILE_ID' => 'file_id',
+            'FIELD_FOR_STORING_FILE_ID' => 'file_id',
+            'FIELD_TITLE' => 'title',
+            'FIELD_CATEGORY' => 'cat',
+            'FIELD_TEXT' => 'desc',
+            'FIELD_TEXT_ID' => 'file-desc',
+            'FIELD_ALLOW_VIEW_TO' => 'allow_view_to',
+            'FIELD_CF' => 'cf',
+            'FIELD_PHOTO' => 'attachments',
+            'FIELD_VIEWS' => 'views',
+            'FIELD_COMMENTS' => 'comments',
+            'FIELD_STATUS' => 'status',
+            'FIELD_STATUS_ADMIN' => 'status_admin',
+            'FIELD_LOCATION' => 'location',
+            'FIELD_LOCATION_PREFIX' => 'location',
+            'FIELD_LABELS' => 'labels',
+            'FIELDS_WITH_KEYWORDS' => 'auto', // can be 'auto', array of fields or comma separated string of field names, works only when OBJECT_METATAGS is specified
+            'FIELD_BOOKMARKS_ID' => 'object_id',
+            'FIELD_BOOKMARKS_PROFILE' => 'profile_id',
+            'FIELD_MIME_TYPE' => 'mime_type',
+			
+            // some params
+            'PARAM_PER_PAGE_FOR_FAVORITES_LISTS' => 'bx_files_per_page_for_favorites_lists',
+            
+            // page URIs
+            'URI_VIEW_ENTRY' => 'view-file',
+            'URI_AUTHOR_ENTRIES' => 'files-author',
+            'URI_ENTRIES_BY_CONTEXT' => 'files-context',
+            'URI_ADD_ENTRY' => 'create-file',
+            'URI_EDIT_ENTRY' => 'edit-file',
+            'URI_MANAGE_COMMON' => 'files-manage',
+            'URI_FAVORITES_LIST' => 'files-favorites',
+
+            'URL_HOME' => 'page.php?i=files-home',
+            'URL_POPULAR' => 'page.php?i=files-popular',
+            'URL_TOP' => 'page.php?i=files-top',
+            'URL_UPDATED' => 'page.php?i=files-updated',
+            'URL_MANAGE_COMMON' => 'page.php?i=files-manage',
+            'URL_MANAGE_ADMINISTRATION' => 'page.php?i=files-administration',
+            'URL_DOWNLOAD' => 'modules/?r=files/download/{file_download_token}/{content_id}.{file_ext}',
+
+            // some params
+            'PARAM_AUTO_APPROVE' => 'bx_files_enable_auto_approve',
+            'PARAM_CHARS_SUMMARY' => 'bx_files_summary_chars',
+            'PARAM_CHARS_SUMMARY_PLAIN' => '',
+            'PARAM_NUM_RSS' => 'bx_files_rss_num',
+            'PARAM_SEARCHABLE_FIELDS' => 'bx_files_searchable_fields',
+            'PARAM_LINK_TO_PREVIEW' => 'bx_files_show_link_to_preview',
+            'PARAM_MAX_NESTING_LEVEL' => 'bx_files_max_nesting_level',
+            'PARAM_MAX_BULK_DOWNLOAD_SIZE' => 'bx_files_max_bulk_download_size',
+            'PARAM_ALLOWED_EXT' => 'bx_files_allowed_ext',
+            'PARAM_DEFAULT_LAYOUT_MODE' => 'bx_files_default_layout_mode',
+
+            // objects
+            'OBJECT_STORAGE' => 'bx_files_files',
+            'OBJECT_IMAGES_TRANSCODER_PREVIEW' => 'bx_files_preview',
+            'OBJECT_IMAGES_TRANSCODER_GALLERY' => 'bx_files_gallery',
+            'OBJECT_SOUNDS_TRANSCODER' => 'bx_files_sounds_mp3',
+            'OBJECT_VIDEOS_TRANSCODERS' => [
+                'poster' => 'bx_files_videos_poster', 
+            	'poster_preview' => 'bx_files_videos_poster_preview',
+            	'mp4' => 'bx_files_videos_mp4', 
+            	'mp4_hd' => 'bx_files_videos_mp4_hd'
+            ],
+            'OBJECT_REPORTS' => 'bx_files',
+            'OBJECT_VIEWS' => 'bx_files',
+            'OBJECT_VOTES' => 'bx_files',
+            'OBJECT_REACTIONS' => 'bx_files_reactions',
+            'OBJECT_SCORES' => 'bx_files',
+            'OBJECT_FAVORITES' => 'bx_files',
+            'OBJECT_FEATURED' => 'bx_files',
+            'OBJECT_METATAGS' => 'bx_files',
+            'OBJECT_COMMENTS' => 'bx_files',
+            'OBJECT_NOTES' => 'bx_files_notes',
+            'OBJECT_CATEGORY' => 'bx_files_cats',
+            'OBJECT_PRIVACY_VIEW' => 'bx_files_allow_view_to',
+            'OBJECT_PRIVACY_LIST_VIEW' => 'bx_files_allow_view_favorite_list',
+            'OBJECT_FORM_ENTRY_UPLOAD' => 'bx_files_upload',
+            'OBJECT_FORM_ENTRY_DISPLAY_UPLOAD' => 'bx_files_entry_upload',
+            'OBJECT_FORM_ENTRY' => 'bx_files',
+            'OBJECT_FORM_ENTRY_DISPLAY_VIEW' => 'bx_files_entry_view',
+            'OBJECT_FORM_ENTRY_DISPLAY_ADD' => 'bx_files_entry_edit',
+            'OBJECT_FORM_ENTRY_DISPLAY_EDIT' => 'bx_files_entry_edit',
+            'OBJECT_FORM_ENTRY_DISPLAY_DELETE' => 'bx_files_entry_delete',
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY' => 'bx_files_view', // actions menu on view entry page
+            'OBJECT_MENU_ACTIONS_VIEW_ENTRY_ALL' => 'bx_files_view_actions', // all actions menu on view entry page
+            'OBJECT_MENU_ACTIONS_MY_ENTRIES' => 'bx_files_my', // actions menu on my entries page
+            'OBJECT_MENU_SUBMENU' => 'bx_files_submenu', // main module submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY' => 'bx_files_view_submenu', // view entry submenu
+            'OBJECT_MENU_SUBMENU_VIEW_ENTRY_MAIN_SELECTION' => 'files-home', // first item in view entry submenu from main module submenu
+            'OBJECT_MENU_SNIPPET_META' => 'bx_files_snippet_meta', // menu for snippet meta info
+            'OBJECT_MENU_MANAGE_TOOLS' => 'bx_files_menu_manage_tools', //manage menu in content administration tools
+            'OBJECT_GRID_ADMINISTRATION' => 'bx_files_administration',
+            'OBJECT_GRID_COMMON' => 'bx_files_common',
+            'OBJECT_UPLOADERS' => array('sys_html5'),
+
+            // menu items which visibility depends on custom visibility checking
+            'MENU_ITEM_TO_METHOD' => array (
+                'bx_files_my' => array (
+                    'create-file' => 'checkAllowedAdd',
+                ),
+                'bx_files_view' => $aMenuItems2Methods,
+            ),
+
+            // informer messages
+            'INFORMERS' => array (
+                'approving' => array (
+                    'name' => 'bx-files-approving',
+                    'map' => array (
+                        'pending' => array('msg' => '_bx_files_txt_msg_status_pending', 'type' => BX_INFORMER_ALERT),
+                        'hidden' => array('msg' => '_bx_files_txt_msg_status_hidden', 'type' => BX_INFORMER_ERROR),
+                    ),
+                ),
+            ),
+
+            // some language keys
+            'T' => array (
+                'txt_sample_single' => '_bx_files_txt_sample_single',
+            	'txt_sample_single_with_article' => '_bx_files_txt_sample_single_with_article',
+            	'txt_sample_comment_single' => '_bx_files_txt_sample_comment_single',
+            	'txt_sample_vote_single' => '_bx_files_txt_sample_vote_single',
+                'txt_sample_reaction_single' => '_bx_files_txt_sample_reaction_single',
+                'txt_sample_score_up_single' => '_bx_files_txt_sample_score_up_single',
+                'txt_sample_score_down_single' => '_bx_files_txt_sample_score_down_single',
+             	'form_field_author' => '_bx_files_form_entry_input_author',
+                'form_entry_upload_single_for_update' => '_bx_files_form_entry_input_pictures_upload',
+            	'grid_action_err_delete' => '_bx_files_grid_action_err_delete',
+            	'grid_txt_account_manager' => '_bx_files_grid_txt_account_manager',
+                'filter_item_active' => '_bx_files_grid_filter_item_title_adm_active',
+            	'filter_item_hidden' => '_bx_files_grid_filter_item_title_adm_hidden',
+                'filter_item_pending' => '_bx_files_grid_filter_item_title_adm_pending',
+            	'filter_item_select_one_filter1' => '_bx_files_grid_filter_item_title_adm_select_one_filter1',
+                'filter_item_select_one_filter2' => '_bx_files_grid_filter_item_title_adm_select_one_filter2',
+            	'menu_item_manage_my' => '_bx_files_menu_item_title_manage_my',
+            	'menu_item_manage_all' => '_bx_files_menu_item_title_manage_all',
+                'txt_all_entries_by' => '_bx_files_txt_all_entries_by',
+                'txt_all_entries_in' => '_bx_files_txt_all_entries_in',
+                'txt_all_entries_by_author' => '_bx_files_page_title_browse_by_author',
+                'txt_all_entries_by_context' => '_bx_files_page_title_browse_by_context',
+            ),
+        );
+
+        $this->_aJsClasses = array(
+        	'manage_tools' => 'BxFilesManageTools',
+            'toolbar_tools' => 'BxFilesBrowserTools',
+        );
+
+        $this->_aJsObjects = array(
+        	'manage_tools' => 'oBxFilesManageTools',
+            'toolbar_tools' => 'oBxFilesBrowserTools',
+        );
+
+        $this->_aGridObjects = array(
+        	'common' => $this->CNF['OBJECT_GRID_COMMON'],
+        	'administration' => $this->CNF['OBJECT_GRID_ADMINISTRATION'],
+        	
+        );
+    }
+
+    public function getJsClass($mixedType)
+    {
+        if (is_array($mixedType)) {
+            return parent::getJsClass($mixedType['type']);
+        } else {
+            return parent::getJsClass($mixedType);
+        }
+    }
+
+    public function getJsObject($mixedType)
+    {
+        if (is_array($mixedType)) {
+            return parent::getJsObject($mixedType['type']).$mixedType['uniq'];
+        } else {
+            return parent::getJsObject($mixedType);
+        }
+    }
+}
+
+/** @} */

+ 39 - 0
modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesFormsEntryHelper.php

@@ -0,0 +1,39 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Files Files
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Entry forms helper functions
+ */
+class BxFilesFormsEntryHelper extends BxBaseModFilesFormsEntryHelper
+{
+    public function __construct($oModule)
+    {
+        $this->_sDisplayForFormAdd ='bx_files_entry_upload';
+        $this->_sObjectNameForFormAdd ='bx_files_upload';
+
+        parent::__construct($oModule);
+    }
+
+    protected function redirectAfterDelete($aContentInfo)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        $oProfile = BxDolProfile::getInstance($aContentInfo[$CNF['FIELD_AUTHOR']]);
+        if ($oProfile)
+            $this->_redirectAndExit('page.php?i=' . $CNF['URI_AUTHOR_ENTRIES'] . '&profile_id=' . $oProfile->id());
+        else
+            $this->_redirectAndExit($CNF['URL_HOME'], true, array(
+                'account_id' => getLoggedId(),
+                'profile_id' => bx_get_logged_profile_id(),
+            ));
+    }
+}
+
+/** @} */

+ 647 - 0
modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesModule.php

@@ -0,0 +1,647 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Files Files
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Files module
+ */
+class BxFilesModule extends BxBaseModFilesModule
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+
+    public function serviceMyEntriesActions ($iProfileId = 0)
+    {
+        if (!$iProfileId)
+            $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
+
+        if (!$this->serviceIsAllowedAddContentToContext($iProfileId))
+            return false;
+
+        $oMenu = BxTemplMenu::getObjectInstance($this->_oConfig->CNF['OBJECT_MENU_ACTIONS_MY_ENTRIES']);
+        return $oMenu ? $oMenu->getCode() : false;
+    }
+
+    public function checkAllowedSetThumb ($iContentId = 0)
+    {
+        return _t('_sys_txt_access_denied');
+    }
+
+    public function actionDownload($sToken = '', $iContentId = 0, $sBulk = '')
+    {
+        $CNF = $this->_oConfig->CNF;
+
+        $aFilesToDownload = [$iContentId];
+        if (!empty($sBulk)) {
+            $aFilesToDownload = json_decode($sBulk);
+        }
+
+        if (!is_array($aFilesToDownload)) {
+            $this->_oTemplate->displayPageNotFound();
+            return;
+        }
+
+        $iContentId = reset($aFilesToDownload);
+
+        $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+        if (!$aData) {
+            $this->_oTemplate->displayPageNotFound();
+            return;
+        }
+
+        if (CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedView($aData)) {
+            $this->_oTemplate->displayAccessDenied();
+            return;
+        }
+
+        if ($aData['type'] == 'folder' || $aFilesToDownload && count($aFilesToDownload) > 1) {
+            bx_import('BulkDownloader', $this->_aModule);
+            $oDownloader = new BxFilesBulkDownloader($this);
+            $aResult = $oDownloader->createDownloadingJob($aFilesToDownload, $aData[$CNF['FIELD_TITLE']]);
+
+            switch ($aResult['status']) {
+                case BX_FILES_DOWNLOADER_STATUS_TOO_LARGE:
+                    $this->_oTemplate->displayMsg(_t('_bx_files_txt_max_size_reached', _t_format_size(intval(getParam('bx_files_max_bulk_download_size')) * 1024 * 1024)));
+                    break;
+                case BX_FILES_DOWNLOADER_STATUS_DOWNLOADING:
+                    $this->_oTemplate->addJsTranslation('_bx_files_txt_status_downloading_ready_msg');
+                    $this->_oTemplate->displayMsg(['title' => _t('_bx_files_txt_status_downloading'), 'content' => $this->_oTemplate->parseHtmlByName('bulk_downloader_process.html', [
+                        'timeout' => BX_FILES_DOWNLOADER_TIMEOUT_REQUESTS,
+                        'action_url' => BX_DOL_URL_ROOT.$this->_oConfig->getBaseUri().'process_download/'.$aResult['job'],
+                    ])]);
+                    break;
+                case BX_FILES_DOWNLOADER_STATUS_READY:
+                    bx_smart_readfile($aResult['file'], $aResult['filename'], 'application/zip');
+                    break;
+                case BX_FILES_DOWNLOADER_STATUS_EMPTY:
+                default:
+                    $this->_oTemplate->displayPageNotFound();
+                    break;
+            }
+            exit;
+        }
+
+        $aFile = $this->getContentFile($aData);
+        if (!$aFile) {
+            $this->_oTemplate->displayPageNotFound();
+            return;
+        }
+
+        $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+        if (!$oStorage) {
+            $this->_oTemplate->displayErrorOccured();
+            return;
+        }
+
+        if (!$oStorage->download($aFile['remote_id'], $sToken, true)) {
+            $this->_oTemplate->displayErrorOccured();
+            return;
+        }
+        
+        exit;   
+    }
+
+    public function actionProcessDownload($iDownloadingJobId) {
+        if (!$iDownloadingJobId) die;
+
+        $aJob = $this->_oDb->getDownloadingJob($iDownloadingJobId);
+        if (!$aJob || $aJob['owner'] != bx_get_logged_profile_id()) die;
+
+        if ($aJob['result']) {
+            bx_smart_readfile($aJob['result'], $aJob['name'], 'application/zip');
+            exit;
+        }
+
+        bx_import('BulkDownloader', $this->_aModule);
+        $oDownloader = new BxFilesBulkDownloader($this);
+
+        $mRes = $oDownloader->processDownloading($aJob['files']);
+        if ($mRes === BX_FILES_DOWNLOADER_STATUS_DOWNLOADING) {
+            $this->_oDb->updateDownloadingJob($iDownloadingJobId, $aJob['files'], '');
+            echoJson([
+                'status' => 'downloading',
+            ]);
+        } else {
+            $this->_oDb->updateDownloadingJob($iDownloadingJobId, $aJob['files'], $mRes);
+            echoJson([
+                'status' => 'ready',
+            ]);
+        }
+    }
+    
+    public function getContentFile($aData) 
+    {
+        $CNF = $this->_oConfig->CNF;
+
+        if (!isset($aData[$CNF['FIELD_AUTHOR']]) || !isset($aData[$CNF['FIELD_ID']]))
+            return false;
+
+        if (isset($aData['type']) && $aData['type'] == 'folder') {
+            return [
+                'id' => $aData[$CNF['FIELD_ID']],
+                'profile_id' => $aData[$CNF['FIELD_AUTHOR']],
+                'remote_id' => 0,
+                'path' => '',
+                'file_name' => $aData[$CNF['FIELD_TITLE']],
+                'mime_type' => '',
+                'ext' => '',
+                'size' => 0,
+                'added' => $aData[$CNF['FIELD_ADDED']],
+                'modified' => 0,
+                'private' => false,
+                'is_image' => false,
+            ];
+        }
+
+        $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+        if (!$oStorage)
+            return false;
+
+        if (isset($aData[$CNF['FIELD_FILE_ID']]) && $aData[$CNF['FIELD_FILE_ID']])
+            return $this->_addFileFields($oStorage->getFile($aData[$CNF['FIELD_FILE_ID']]));
+        
+        $aGhostFiles = $oStorage->getGhosts ($aData[$CNF['FIELD_AUTHOR']], $aData[$CNF['FIELD_ID']]);
+        if (!$aGhostFiles)
+            return false;
+
+        return $this->_addFileFields(array_pop($aGhostFiles));
+    }
+    
+    public function getDownloadLink($mixedData) 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!is_array($mixedData))
+            $mixedData = $this->_oDb->getContentInfoById((int)$mixedData);
+        if(empty($mixedData) || !is_array($mixedData))
+            return false;
+        
+        $aFile = $this->getContentFile($mixedData);
+        if($aFile === false)
+            return false;
+
+        $sFileExt = !empty($aFile['ext']) ? $aFile['ext'] : '';
+
+        $aMarkers = array_merge([
+            'content_id' => $mixedData[$CNF['FIELD_ID']]
+        ], ($aFile['private'] && ($oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE'])) ? [
+            'file_download_token' => $oStorage->genToken($aFile['id']),
+            'file_ext' => $sFileExt
+        ] : [
+            'file_download_token' => '0',
+            'file_ext' => $sFileExt,
+        ]));
+
+        return bx_replace_markers(BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($CNF['URL_DOWNLOAD']), $aMarkers);
+    }
+
+    protected function _addFileFields($aFile) 
+    {
+        if (is_array($aFile))
+            $aFile['is_image'] = in_array($aFile['ext'], array('jpg', 'jpeg', 'png', 'gif', /* when ImageMagick is used - 'tif', 'tiff', 'bmp', 'ico', 'psd' */));
+        return $aFile;
+    }
+
+    public function serviceEntityFilePreview($iContentId = 0)
+    {
+        return $this->_serviceTemplateFunc ('entryFilePreview', $iContentId);
+    }
+
+    public function serviceProcessFilesData($iNumberOfFilesToProcessAtOnce = 3)
+    {
+        $CNF = $this->_oConfig->CNF;
+        
+        if (!defined('BX_SYSTEM_JAVA') || !constant('BX_SYSTEM_JAVA'))
+            return;
+        
+        $a = $this->_oDb->getNotProcessedFiles($iNumberOfFilesToProcessAtOnce);
+        if (!$a)
+            return;
+
+        $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+        if (!$oStorage)
+            return false;
+        
+        foreach ($a as $aContentInfo) {
+            $aFile = $this->getContentFile($aContentInfo);
+            if (!$aFile) {
+                $this->_oDb->updateFileData ($aContentInfo[$CNF['FIELD_ID']], '');
+                continue;
+            }
+
+            $sFileUrl = $oStorage->getFileUrlById($aFile['id']);
+            $sFilePath = BX_DIRECTORY_PATH_TMP . $aFile['remote_id'] . '.' . $aFile['ext'];
+            @file_put_contents($sFilePath, file_get_contents($sFileUrl));
+            if (!file_exists($sFilePath)) {
+                $this->_oDb->updateFileData ($aContentInfo[$CNF['FIELD_ID']], '');
+                continue;
+            }
+            
+            $sCommand = '"' . constant('BX_SYSTEM_JAVA') . '" -Djava.awt.headless=true -jar "' . $this->_oConfig->getHomePath() . 'data/tika-app.jar" --encoding=UTF-8 --text "' . $sFilePath . '"';
+            $sData = `$sCommand`;
+            @unlink($sFilePath);
+
+            $this->_oDb->updateFileData ($aContentInfo[$CNF['FIELD_ID']], $sData);
+        }
+    }
+
+    public function servicePruneDownloadingJobs()
+    {
+        $aFiles = $this->_oDb->deleteOldDownloadingJobs();
+        if ($aFiles) foreach ($aFiles as $sFile) @unlink($sFile);
+    }
+
+    protected function _getFilesForTimelinePost($aEvent, $aContentInfo, $sUrl, $aBrowseParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aFile = $this->getContentFile($aContentInfo);
+        if(!$aFile || $aFile['is_image'])
+            return [];
+
+        $sIconSrc = '';
+        if(($oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE'])) !== false)
+            $sIconSrc = $this->_oTemplate->getIconUrl($oStorage->getIconNameByFileName($aFile['file_name']));
+
+        return [[
+            'id' => $aFile['id'],
+            'src' => $sIconSrc,
+            'src_medium' => $sIconSrc,
+            'src_orig' => '',
+            'url' => $sUrl
+        ]];
+    }
+
+    protected function _getImagesForTimelinePost($aEvent, $aContentInfo, $sUrl, $aBrowseParams = array())
+    {
+        $aFile = $this->getContentFile($aContentInfo);
+        if (!$aFile)
+            return [];
+
+        if (!$aFile['is_image'])
+            return [];
+
+        $sPhotoThumb = '';
+        if ($oImagesTranscoder = BxDolTranscoderImage::getObjectInstance($this->_oConfig->CNF['OBJECT_IMAGES_TRANSCODER_GALLERY']))
+            $sPhotoThumb = $oImagesTranscoder->getFileUrl($aFile['id']);
+
+        return [[
+            'id' => $aFile['id'], 
+            'url' => $sUrl, 
+            'src' => $sPhotoThumb
+        ]];
+    }
+
+    public function actionBookmark($iContentId) {
+        if (!$this->isLogged()) return;
+
+        $aFilesToBookmark = [];
+        if ($iContentId) $aFilesToBookmark = [$iContentId];
+        elseif (isset($_POST['bulk'])) $aFilesToBookmark = $_POST['bulk'];
+
+        if ($aFilesToBookmark)
+            foreach ($aFilesToBookmark as $iContentId) {
+                if ($iContentId <= 0) continue;
+
+                $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+                if (!$aData) continue;
+
+                $this->_oDb->bookmarkFile((int)$iContentId, bx_get_logged_profile_id());
+            }
+    }
+
+    public function actionEntryPreview($iContentId) {
+        $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+
+        $sJsCss = '';
+
+        $sPreviewCode = $this->_serviceTemplateFunc ('entryFilePreview', $iContentId);
+
+        // various init scripts could be there, which fail to work properly
+        // because of a fact that it is inside a popup which is at the moment is hidden
+        // so we need a way to postpone that initialization
+        $aScripts = [];
+        $sPreviewCode = preg_replace_callback('/<script>(.*)<\/script>/s', function($aMatch) use (&$aScripts) {
+            $aScripts[] = ['code' => $aMatch[1]];
+            return '';
+        }, $sPreviewCode);
+
+        // if there is a script then most likely it is a text + codemirror initialization, so include it here.
+        if ($aScripts) {
+            $sJsCss .= $this->_oTemplate->addJs('codemirror/codemirror-ext.min.js', true);
+            $sJsCss .= $this->_oTemplate->addCss(BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'codemirror/|codemirror.css', true);
+        }
+
+        $sJsCss .= $this->_oTemplate->addCss('file_handler.css', true);
+
+        $sPopupCode = $this->_oTemplate->parseHtmlByName('entry-popup.html', [
+            'content' => $sPreviewCode,
+            'bx_repeat:init_scripts' => $aScripts,
+        ]);
+
+        echoJson([
+            'popup' => [
+                'html' => PopupBox('bx-files-popup', bx_process_output($aData['title']), $sJsCss.$sPopupCode),
+                'options' => [
+                    'onShow' => 'bx_files_popup_init()',
+                ],
+            ],
+        ]);
+    }
+
+    public function actionEntryInfo($iContentId) {
+        $aData = $this->_oDb->getContentInfoById((int)$iContentId);
+
+        if (CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedView($aData)) {
+            echoJson([
+                'popup' => [
+                    'html' => PopupBox('bx-files-popup', _t('_Access denied'), _t('_Access denied')),
+                ],
+            ]);
+            return;
+        }
+
+        echoJson([
+            'popup' => [
+                'html' => PopupBox('bx-files-popup', bx_process_output($aData['title']), $this->_oTemplate->entryInfoPopup($aData)),
+            ],
+        ]);
+    }
+
+    public function actionEntryEditTitle($iContentId) {
+        $iContentId = intval($iContentId);
+        $aData = $this->_oDb->getContentInfoById($iContentId);
+
+        $CNF = &$this->_oConfig->CNF;
+
+        if (!$aData) {
+            echoJson(['message' => _t('_bx_files_txt_error')]);
+            exit;
+        }
+
+        if ($this->checkAllowedEdit($aData, false) !== CHECK_ACTION_RESULT_ALLOWED) {
+            echoJson(['message' => _t('_bx_files_txt_permission_denied')]);
+            exit;
+        }
+
+        setlocale(LC_ALL,'C.UTF-8'); //otherwise pathinfo returns empty string on a non-latin characters filenames
+
+        if (isset($_POST['title'])) {
+            if ($sNewTitle = bx_get('title')) {
+                $sExt = pathinfo($aData[$CNF['FIELD_TITLE']], PATHINFO_EXTENSION);
+                if ($sExt) $sExt = '.'.$sExt;
+
+                $this->_oDb->updateEntryTitle($iContentId, $sNewTitle.$sExt);
+                $aData = $this->_oDb->getContentInfoById($iContentId);
+                echo bx_process_output($aData[$CNF['FIELD_TITLE']]);
+            }
+            exit;
+        }
+
+
+        $sActionUrl = BX_DOL_URL_ROOT.$this->_oConfig->getBaseUri();
+        echoJson([
+            'eval' => "bx_prompt('"._t('_bx_files_form_entry_input_title')."', '".bx_js_string(pathinfo($aData[$CNF['FIELD_TITLE']], PATHINFO_FILENAME))."', function(val) {                
+                $.post('{$sActionUrl}entry_edit_title/{$iContentId}', {title: val.getValue()}, function(sTitle) {
+                    if (sTitle)
+                        $('.bx-file-entry[file_id=".$aData[$CNF['FIELD_ID']]."] .bx-files-filename a').html(sTitle);
+                });                
+            })",
+        ]);
+    }
+
+    public function actionEntryDelete($iContentId) {
+        $aFilesToDelete = [];
+        if ($iContentId) $aFilesToDelete = [$iContentId];
+        elseif (isset($_POST['bulk'])) $aFilesToDelete = $_POST['bulk'];
+
+        if ($aFilesToDelete)
+            foreach ($aFilesToDelete as $iContentId) {
+                $iContentId = intval($iContentId);
+                if ($iContentId <= 0) continue;
+
+                $aData = $this->_oDb->getContentInfoById($iContentId);
+
+                $CNF = &$this->_oConfig->CNF;
+
+                if (!$aData) {
+                    echoJson(['message' => _t('_bx_files_txt_error')]);
+                    exit;
+                }
+
+                if ($this->checkAllowedDelete($aData, false) !== CHECK_ACTION_RESULT_ALLOWED) {
+                    continue;
+                }
+
+                $this->serviceDeleteEntity($iContentId);
+            }
+
+        if (!isset($_POST['bulk'])) {
+            echoJson([
+                'eval' => "
+                    $('.bx-file-entry[file_id={$iContentId}]').fadeOut();
+                    $(window).trigger('files_browser.update');
+                ",
+            ]);
+        } else {
+            echoJson([]);
+        }
+    }
+
+    public function actionClearGhosts() {
+        if (!isLogged()) die;
+
+        $oStorage = BxDolStorage::getObjectInstance($this->_oConfig->CNF['OBJECT_STORAGE']);
+        if ($oStorage) {
+            $aFiles = $oStorage->getGhosts(bx_get_logged_profile_id(), 0);
+
+            if ($aFiles) foreach ($aFiles as $aFile) {
+                $oStorage->deleteFile($aFile['id']);
+            }
+        }
+    }
+
+    public function actionUploadCompleted() {
+        $iContext = intval(bx_get('context'));
+        $iFolder = intval(bx_get('folder'));
+
+        if (!isLogged()) {
+            echoJson(['message' => _t('_Access denied')]);
+            exit;
+        }
+
+        if ($this->checkAllowedAdd() != CHECK_ACTION_RESULT_ALLOWED || $iContext && !$this->serviceIsAllowedAddContentToContext($iContext)) {
+            echoJson(['message' => _t('_Access denied')]);
+            exit;
+        }
+
+        $oStorage = BxDolStorage::getObjectInstance($this->_oConfig->CNF['OBJECT_STORAGE']);
+        if ($oStorage) {
+            $iAuthorProfile = bx_get_logged_profile_id();
+            $aFiles = $oStorage->getGhosts($iAuthorProfile, 0);
+
+            if ($aFiles) {
+                $aFilesIDs = [];
+                foreach($aFiles as $aFile) {
+                    $aFilesIDs[] = $aFile['id'];
+                    bx_set('title-'.$aFile['id'], $aFile['file_name'], 'post');
+                }
+                bx_set('attachments', $aFilesIDs, 'post');
+                bx_set('profile_id', $iAuthorProfile, 'post');
+                bx_set('allow_view_to', !$iContext ? BX_DOL_PG_ALL : -$iContext, 'post');
+                bx_set('cf', 1, 'post');
+
+                $oForm = BxDolForm::getObjectInstance($this->_oConfig->CNF['OBJECT_FORM_ENTRY_UPLOAD'], $this->_oConfig->CNF['OBJECT_FORM_ENTRY_DISPLAY_UPLOAD']);
+                $aFiles = $oForm->insert();
+
+                if ($iFolder != 0 && !empty($aFiles))
+                    $this->_oDb->moveFilesToFolder($aFiles, $iFolder);
+            }
+        }
+
+        echoJson(['success' => true]);
+    }
+
+    public function actionCreateFolder() {
+        $iContext = intval(bx_get('context'));
+        $iFolder = intval(bx_get('current_folder'));
+        $sName = trim(bx_get('name'));
+
+        if (!isLogged()) {
+            echoJson(['message' => _t('_Access denied')]);
+            exit;
+        }
+
+        if ($this->checkAllowedAdd() != CHECK_ACTION_RESULT_ALLOWED || $iContext && !$this->serviceIsAllowedAddContentToContext($iContext)) {
+            echoJson(['message' => _t('_Access denied')]);
+            exit;
+        }
+
+        if (!empty($sName)) {
+            $iAuthor = bx_get_logged_profile_id();
+            // in case it is posted to context make the context profile as an author of an entry
+            // to leave folder in case creator is removed
+            // and to delete folder when context profile is being deleted
+            $this->_oDb->createFolder($iFolder, $iContext ? $iContext : $iAuthor, !$iContext || $iAuthor == $iContext ? BX_DOL_PG_ALL : -$iContext, $sName);
+        }
+
+        echoJson([]);
+        exit;
+    }
+
+    public function actionMoveFiles() {
+        $iContentId = intval(bx_get('file'));
+
+        $aFilesToMove = [];
+        if ($iContentId) $aFilesToMove = [$iContentId];
+        elseif (isset($_POST['bulk'])) $aFilesToMove = $_POST['bulk'];
+
+        $iContentId = reset($aFilesToMove);
+
+        $aData = $this->_oDb->getContentInfoById($iContentId);
+
+        if (!$aData) {
+            echoJson(['message' => _t('_bx_files_txt_error')]);
+            exit;
+        }
+
+        $CNF = &$this->_oConfig->CNF;
+
+        $iContext = $aData[$CNF['FIELD_ALLOW_VIEW_TO']] < 0 ? -$aData[$CNF['FIELD_ALLOW_VIEW_TO']] : $aData[$CNF['FIELD_AUTHOR']];
+
+        if (!isAdmin() && ($this->checkAllowedAdd() != CHECK_ACTION_RESULT_ALLOWED || !$this->serviceIsAllowedAddContentToContext($iContext))) {
+            echoJson(['message' => _t('_Access denied')]);
+            exit;
+        }
+
+        if (isset($_POST['move_to'])) {
+            $iMoveTo = intval(bx_get('move_to'));
+            if ($iMoveTo) {
+                $aDataMoveTo = $this->_oDb->getContentInfoById($iMoveTo);
+                if (!$aDataMoveTo) {
+                    echoJson(['message' => _t('_bx_files_txt_error')]);
+                    exit;
+                }
+
+                $iContextMoveTo = $aDataMoveTo[$CNF['FIELD_ALLOW_VIEW_TO']] < 0 ? -$aDataMoveTo[$CNF['FIELD_ALLOW_VIEW_TO']] : $aDataMoveTo[$CNF['FIELD_AUTHOR']];
+
+                if (!isAdmin() && ($this->checkAllowedAdd() != CHECK_ACTION_RESULT_ALLOWED || !$this->serviceIsAllowedAddContentToContext($iContextMoveTo))) {
+                    echoJson(['message' => _t('_Access denied')]);
+                    exit;
+                }
+            }
+
+            $this->_oDb->moveFilesToFolder($aFilesToMove, $iMoveTo);
+
+            echoJson([
+                'eval' => "                        
+                    $(window).trigger('files_browser.update');
+                ",
+            ]);
+            exit;
+        }
+
+        $aFolders = $this->_oDb->getFoldersInContext($iContext);
+        if (!$aFolders) {
+            echoJson(['message' => _t('_bx_files_txt_move_to_nowhere')]);
+            exit;
+        }
+
+        echoJson([
+            'popup' => [
+                'html' => PopupBox('bx-files-popup', _t('_bx_files_txt_move_to'), $this->_oTemplate->getJsTree($aFilesToMove, $aFolders)),
+                'options' => [],
+            ],
+        ]);
+    }
+
+    public function serviceDeleteEntitiesByAuthor ($iProfileId) {
+        $this->_oDb->deleteProfileBookmarks($iProfileId);
+        return parent::serviceDeleteEntitiesByAuthor ($iProfileId);
+    }
+
+    public function serviceDeleteEntity ($iContentId, $sFuncDelete = 'deleteData') {
+        $aData = $this->_oDb->getContentInfoById($iContentId);
+        if ($aData['type'] == 'folder') {
+            $aNestedFiles = $this->_oDb->getFolderFiles($aData[$this->_oConfig->CNF['FIELD_ID']]);
+            if ($aNestedFiles)
+                foreach ($aNestedFiles as $iFile) {
+                    $this->serviceDeleteEntity($iFile);
+                }
+        }
+        parent::serviceDeleteEntity($iContentId, $sFuncDelete);
+    }
+
+    public function serviceBrowseContext ($iProfileId = 0, $aParams = array()) {
+        if(!$iProfileId)
+            $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
+        if (!$iProfileId)
+            return;
+        // do not show files in context block for persons profiles
+        $oProfile = BxDolProfile::getInstance($iProfileId);
+        if ($oProfile && $oProfile->getModule() == 'bx_persons') return;
+
+        return parent::serviceBrowseContext($iProfileId, $aParams);
+    }
+
+    public function serviceGetFile ($iContentId, $aParams = []) 
+    {
+        $aResult = parent::serviceGetFile ($iContentId, $aParams);
+        if(!$aResult)
+            return $aResult;
+
+        $aResult['url_download'] = $this->getDownloadLink($iContentId);
+        return $aResult;
+    }
+}
+
+/** @} */

+ 546 - 0
modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesSearchResult.php

@@ -0,0 +1,546 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Files Files
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxFilesSearchResult extends BxBaseModTextSearchResult
+{
+    protected $sOrderParamName = 'order';
+    protected $sBookmarksParamName = 'bookmarks';
+    protected $sSortingParamName = 'sorting';
+    protected $sCurrentFolderParamName = 'folder';
+    protected $sCurrentView;
+    protected $aSortingOptions;
+    protected $bFileManagerMode = false;
+    protected $iFileManagerUploadTo = 0;
+    protected $sMode = '';
+
+    function __construct($sMode = '', $aParams = array())
+    {
+        $aParams['unit_views'] = [
+            'extended' => 'unit.html',
+            'gallery' => 'unit_gallery.html',
+            'full' => 'unit_full.html',
+            'showcase' => 'unit_showcase.html',
+            'table' => 'unit_simple_row.html',
+        ];
+
+        //Enble filemanager (toolbar, table files layout, dynamic menus, folders support) only for author/context blocks.
+        if (($sMode == 'author' || $sMode == 'context') && !isset($aParams['no_toolbar'])) {
+            $this->bFileManagerMode = true;
+            $this->setUnitParams(['show_inline_menu' => true]);
+
+            if (!bx_get('unit_view'))
+                $aParams['unit_view'] = getParam('bx_files_default_layout_mode');
+        }
+
+        $this->sMode = $sMode;
+
+        parent::__construct($sMode, $aParams);
+
+        $this->sCurrentView = array_flip($aParams['unit_views'])[$this->sUnitTemplate];
+
+        $this->aGetParams = array_merge($this->aGetParams, [
+            $this->sOrderParamName,
+            $this->sBookmarksParamName,
+            $this->sSortingParamName,
+            $this->sUnitViewParamName,
+            $this->sCurrentFolderParamName,
+        ]);
+
+
+        $this->aSortingOptions = [
+            'name',
+            'date',
+            'type',
+            'author',
+        ];
+
+
+        $this->aCurrent = array(
+            'name' => 'bx_files',
+            'module_name' => 'bx_files',
+            'object_metatags' => 'bx_files',
+            'title' => _t('_bx_files_page_title_browse'),
+            'table' => 'bx_files_main',
+            'ownFields' => array('id', 'file_id', 'title', 'desc', 'author', 'added', 'type', 'parent_folder_id', 'allow_view_to'),
+            'searchFields' => array(),
+            'restriction' => array(
+                'author' => array('value' => '', 'field' => 'author', 'operator' => '='),
+                'featured' => array('value' => '', 'field' => 'featured', 'operator' => '<>'),
+                'status' => array('value' => 'active', 'field' => 'status', 'operator' => '='),
+                'statusAdmin' => array('value' => 'active', 'field' => 'status_admin', 'operator' => '='),
+                'files_only' => array('value' => 'file', 'field' => 'type', 'operator' => '='),
+                'context_filter' => array('value' => '', 'field' => 'allow_view_to', 'operator' => '>'),
+                'folder' => array('value' => '', 'field' => 'parent_folder_id', 'operator' => '='),
+            ),
+            'paginate' => array('perPage' => getParam('bx_files_per_page_browse'), 'start' => 0),
+            'sorting' => 'last',
+            'rss' => array(
+                'title' => '',
+                'link' => '',
+                'image' => '',
+                'profile' => 0,
+                'fields' => array (
+                    'Guid' => 'link',
+                    'Link' => 'link',
+                    'Title' => 'title',
+                    'DateTimeUTS' => 'added',
+                    'Desc' => 'desc',
+            		'Image' => 'thumb'
+                ),
+            ),
+            'ident' => 'id',
+        );
+
+        $this->sFilterName = 'bx_files_filter';
+        $this->oModule = $this->getMain();
+
+        $CNF = &$this->oModule->_oConfig->CNF;
+
+        $sSearchFields = getParam($CNF['PARAM_SEARCHABLE_FIELDS']);
+        $this->aCurrent['searchFields'] = !empty($sSearchFields) ? explode(',', $sSearchFields) : '';
+
+        $oProfileAuthor = null;
+
+        switch ($sMode) {
+            case 'author':
+                if(!$this->_updateCurrentForAuthor($sMode, $aParams, $oProfileAuthor))
+                    $this->isError = true;
+                else {
+                    //show only those files/folders which are not posted to some context
+                    $this->aCurrent['restriction']['context_filter']['value'] = 0;
+                }
+                break;
+
+            case 'context':
+                if(!$this->_updateCurrentForContext($sMode, $aParams, $oProfileAuthor))
+                    $this->isError = true;
+                break;
+
+            case 'favorite':
+                if(!$this->_updateCurrentForFavorite($sMode, $aParams, $oProfileAuthor)) 
+                    $this->isError = true;
+                break;
+
+            case 'public':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_HOME']);
+                $this->aCurrent['title'] = _t('_bx_files_page_title_browse_recent');
+                $this->aCurrent['rss']['link'] = 'modules/?r=files/rss/' . $sMode;
+                break;
+
+            case 'featured':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_HOME']);
+                $this->aCurrent['title'] = _t('_bx_files_page_title_browse_featured');
+                $this->aCurrent['restriction']['featured']['value'] = '0';
+                $this->aCurrent['rss']['link'] = 'modules/?r=files/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'featured';
+                break;
+
+            case 'popular':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_POPULAR']);
+                $this->aCurrent['title'] = _t('_bx_files_page_title_browse_popular');
+                $this->aCurrent['rss']['link'] = 'modules/?r=files/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'popular';
+                break;
+
+            case 'top':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_TOP']);
+                $this->aCurrent['title'] = _t('_bx_files_page_title_browse_top');
+                $this->aCurrent['rss']['link'] = 'modules/?r=files/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'top';
+                break;
+
+            case 'updated':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_UPDATED']);
+                $this->aCurrent['title'] = _t('_bx_files_page_title_browse_updated');
+                $this->aCurrent['rss']['link'] = 'modules/?r=files/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'updated';
+                break;
+
+            case '': // search results
+                $this->sBrowseUrl = BX_DOL_SEARCH_KEYWORD_PAGE;
+                $this->aCurrent['title'] = _t('_bx_files');
+                unset($this->aCurrent['paginate']['perPage'], $this->aCurrent['rss']);
+                break;
+
+            default:
+                $sMode = '';
+                $this->isError = true;
+        }
+
+        $this->processReplaceableMarkers($oProfileAuthor);
+        $this->addConditionsForPrivateContent($CNF, $oProfileAuthor);
+        $this->addCustomConditions($CNF, $oProfileAuthor, $sMode, $aParams);
+
+
+        //show to a profile author all of his files regardless of CF
+        if ($sMode == 'author' && $oProfileAuthor && bx_get_logged_profile_id() == $oProfileAuthor->id())
+            unset($this->aCurrent['restriction']['cf_viewer']);
+
+        if ($this->bFileManagerMode) {
+            $this->addContainerClass('bx-files-with-toolbar');
+
+            unset($this->aCurrent['restriction']['files_only']);
+            if (bx_get($this->sBookmarksParamName))
+                $this->addConditionsForBookmarks($CNF);
+
+            $this->aCurrent['sorting'] = 'name';
+            if (bx_get($this->sSortingParamName)) $this->aCurrent['sorting'] = bx_get($this->sSortingParamName);
+
+            if ($this->aCurrent['sorting'] == 'type')
+                $this->addConditionsForMimeType($CNF);
+
+            $this->removeContainerClass('bx-def-margin-sec-lefttopright-neg');
+
+            if (!$this->isError) {
+                if ($sMode == 'author' && $oProfileAuthor->id() == bx_get_logged_profile_id() || $sMode == 'context')
+                    $this->iFileManagerUploadTo = $oProfileAuthor->id();
+            }
+
+            if ($this->sCurrentView == 'table')
+                $this->removeContainerClass('bx-def-margin-bottom-neg');
+
+
+            $this->aCurrent['restriction']['folder']['value'] = intval(bx_get($this->sCurrentFolderParamName));
+            if ($this->aCurrent['restriction']['folder']['value'] < 0) {
+                //navigate folder top
+                bx_set($this->sCurrentFolderParamName, $this->oModule->_oDb->getParentFolderId(-$this->aCurrent['restriction']['folder']['value']));
+                $this->aCurrent['restriction']['folder']['value'] = bx_get($this->sCurrentFolderParamName);
+            }
+
+            //free up one place for folder up item
+            if ($this->aCurrent['restriction']['folder']['value']) {
+                $this->aCurrent['paginate']['perPage']--;
+            }
+        }
+    }
+
+    protected function addConditionsForBookmarks(&$CNF) {
+        if(empty($this->aCurrent['join']) || !is_array($this->aCurrent['join'])) $this->aCurrent['join'] = [];
+
+        $this->aCurrent['join'] = array_merge($this->aCurrent['join'], [
+            'bookmarks' => [
+                'type' => 'INNER',
+                'table' => $CNF['TABLE_BOOKMARKS'],
+                'mainTable' => $CNF['TABLE_ENTRIES'],
+                'mainField' => $CNF['FIELD_ID'],
+                'onField' => $CNF['FIELD_BOOKMARKS_ID'],
+                'joinFields' => array($CNF['FIELD_BOOKMARKS_PROFILE']),
+            ],
+        ]);
+
+        $this->aCurrent['restriction']['bookmarks'] = [
+            'value' => bx_get_logged_profile_id(),
+            'field' => $CNF['FIELD_BOOKMARKS_PROFILE'],
+            'operator' => '=',
+            'table' => $CNF['TABLE_BOOKMARKS'],
+        ];
+    }
+
+    protected function addConditionsForMimeType(&$CNF) {
+        if(empty($this->aCurrent['join']) || !is_array($this->aCurrent['join'])) $this->aCurrent['join'] = [];
+
+        $this->aCurrent['join'] = array_merge($this->aCurrent['join'], [
+            'files' => [
+                'type' => 'LEFT',
+                'table' => $CNF['TABLE_FILES'],
+                'mainTable' => $CNF['TABLE_ENTRIES'],
+                'mainField' => $CNF['FIELD_ID'],
+                'onField' => $CNF['FIELD_ID'],
+                'joinFields' => array($CNF['FIELD_MIME_TYPE']),
+            ],
+        ]);
+    }
+
+    public function addCustomParts() {
+        if ($this->_bLiveSearch) return;
+
+        if (!$this->bFileManagerMode) return;
+
+        $this->oModule->_oTemplate->addCss('main.css');
+
+        $aParams = [
+            'aRequestParams' => [
+                'unit_view_param' => $this->sUnitViewParamName,
+                'bookmarks_param' => $this->sBookmarksParamName,
+                'sorting_param' => $this->sSortingParamName,
+                'keyword_param' => 'keyword',
+                'current_folder' => $this->sCurrentFolderParamName,
+            ],
+
+            'layout' => $this->sCurrentView,
+            'bookmarks' => intval(bx_get($this->sBookmarksParamName)),
+            'sorting' => $this->aCurrent['sorting'],
+            'sorting_options' => $this->aSortingOptions,
+            'keyword' => bx_get('keyword') ? bx_get('keyword') : '',
+            'current_folder' => $this->aCurrent['restriction']['folder']['value'],
+            'current_page' => $this->aCurrent['paginate']['start'],
+        ];
+
+        return $this->getBrowseToolbar($aParams);
+    }
+
+    function getSearchData ()
+    {
+        $CNF = &$this->oModule->_oConfig->CNF;
+
+        $aData = parent::getSearchData();
+
+        if ($this->bFileManagerMode && $this->aCurrent['restriction']['folder']['value']) {
+            $aData = array_merge([0 => [
+                $CNF['FIELD_ID'] => '-1',
+                $CNF['FIELD_FILE_ID'] => '0',
+                $CNF['FIELD_TITLE'] => '..',
+                $CNF['FIELD_AUTHOR'] => bx_get_logged_profile_id(),
+                $CNF['FIELD_ADDED'] => 0,
+                $CNF['FIELD_ALLOW_VIEW_TO'] => BX_DOL_PG_ALL,
+                'type' => 'folder',
+            ]], $aData);
+            $this->aCurrent['paginate']['num']++;
+        }
+
+        return $aData;
+    }
+
+    function processing ()
+    {
+        $iViewedProfile = 0;
+        if ($this->_sMode == 'context' && $this->iFileManagerUploadTo) $iViewedProfile = $this->iFileManagerUploadTo;
+        if ($this->_sMode == 'author' && $this->aCurrent['restriction']['author']['value']) $iViewedProfile = $this->aCurrent['restriction']['author']['value'];
+
+        if ($iViewedProfile) {
+            if (is_array($iViewedProfile)) $iViewedProfile = abs($iViewedProfile[0]);
+            $oProfile = BxDolProfile::getInstance($iViewedProfile);
+            if ($oProfile->checkAllowedProfileView() !== CHECK_ACTION_RESULT_ALLOWED) {
+                $this->aCurrent['paginate']['num'] = 1;
+                return MsgBox(_t('_sys_access_denied_to_private_content'));
+            }
+        }
+
+        $sCode = parent::processing();
+        if (!$this->aCurrent['paginate']['num']  && $this->bFileManagerMode) {
+            //to show toolbar in case of empty results
+            $sCode = $this->displaySearchBox($this->addCustomParts().MsgBox(_t('_Empty')));
+        }
+        return $sCode;
+    }
+
+    function showPagination($bAdmin = false, $bChangePage = true, $bPageReload = true)
+    {
+        $aAdditionalParams = [
+            $this->sUnitViewParamName => $this->sCurrentView,
+            $this->sBookmarksParamName => intval(bx_get($this->sBookmarksParamName)),
+            $this->sSortingParamName => $this->aCurrent['sorting'],
+            $this->sCurrentFolderParamName => $this->aCurrent['restriction']['folder']['value'],
+        ];
+        $sPageUrl = $this->getCurrentUrl($aAdditionalParams, false);
+        $sOnClick = $this->getCurrentOnclick($aAdditionalParams, false);
+
+        $oPaginate = new BxTemplPaginate([
+            'page_url' => $sPageUrl,
+            'on_change_page' => $sOnClick,
+            'num' => $this->aCurrent['paginate']['num'],
+            'per_page' => $this->aCurrent['paginate']['perPage'],
+            'start' => $this->aCurrent['paginate']['start'],
+        ]);
+
+        return $sOnClick ? $oPaginate->getSimplePaginate() : $oPaginate->getPaginate();
+    }
+
+    function getAlterOrder() {
+        if (in_array($this->aCurrent['sorting'], $this->aSortingOptions)) {
+            $CNF = &$this->oModule->_oConfig->CNF;
+
+            $aSql = array();
+            switch ($this->aCurrent['sorting']) {
+                case 'name':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`type` DESC, `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_TITLE'].'` ASC';
+                    break;
+                case 'date':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`type` DESC, `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_ID'].'` DESC';
+                    break;
+                case 'author':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`type` DESC, `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_AUTHOR'].'` ASC, `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_TITLE'].'` ASC';
+                    break;
+                case 'type':
+                    $aSql['order'] = ' ORDER BY `'.$CNF['TABLE_ENTRIES'].'`.`type` DESC, `'.$CNF['TABLE_FILES'].'`.`'.$CNF['FIELD_MIME_TYPE'].'` ASC, `'.$CNF['TABLE_ENTRIES'].'`.`'.$CNF['FIELD_TITLE'].'` ASC';
+                    break;
+            }
+            return $aSql;
+        } else {
+            return parent::getAlterOrder();
+        }
+
+    }
+
+    protected function getBrowseToolbarControls(&$aParams, $sJsObject) {
+        $aSortingOptions = [];
+        if (isset($aParams['sorting_options'])) {
+            foreach ($aParams['sorting_options'] as $sSorting) {
+                $aSortingOptions[$sSorting] = _t('_bx_files_toolbar_sorting_' . $sSorting);
+            }
+        }
+
+        $oForm = new BxTemplFormView([], $this->oModule->_oTemplate);
+        $aInputSorting = [
+            'type' => 'select',
+            'name' => 'sorting',
+            'value' => $aParams['sorting'],
+            'values' => $aSortingOptions,
+            'attrs' => [
+                'onchange' => $sJsObject.'.setSorting(this.value);',
+            ],
+        ];
+
+        $aInputFilter = [
+            'type' => 'text',
+            'name' => 'keyword',
+            'value' => $aParams['keyword'],
+            'attrs' => [
+                'placeholder' => _t('_sys_search_placeholder'),
+                'onkeyup' => $sJsObject.'.onChangeFilter(this);',
+                'onpaste' => $sJsObject.'.onChangeFilter(this);',
+            ],
+        ];
+
+        $aUploadButtonParams = [];
+        $bUploadAllowed = $this->oModule->checkAllowedAdd() == CHECK_ACTION_RESULT_ALLOWED && $this->oModule->serviceIsAllowedAddContentToContext($this->iFileManagerUploadTo);
+        if ($bUploadAllowed) {
+            $sUniqId = genRndPwd (8, false);
+            $oUploader = BxDolUploader::getObjectInstance('bx_files_html5', $this->oModule->_oConfig->CNF['OBJECT_STORAGE'], $sUniqId, $this->oModule->_oTemplate);
+            $iMaxNestingLevel = intval(getParam($this->oModule->_oConfig->CNF['PARAM_MAX_NESTING_LEVEL']));
+            
+            $aParamsJs = array_merge($oUploader->getUploaderJsParams(), 
+                [
+                    'content_id' => 0,
+                    'storage_private' => '0',
+                    'acceptedFiles' => '',
+                    'multiple' => 1,
+                    'images_transcoder' => 'bx_files_preview',
+                ]
+            );
+            
+            $aParamsBtn = [
+                    'content_id' => 0,
+                    'storage_private' => '0',
+                    'button_template' => 'uploader_button_html5_attach.html'
+            ];
+            
+            $aUploadButtonParams = [
+                'js_object' => $sJsObject,
+                'uploader_code' => $oUploader->getUploaderButton($aParamsBtn) . $oUploader->getUploaderJs('', true, $aParamsJs, true),
+                'uploader_js_object' => $oUploader->getNameJsInstanceUploader(),
+                'uploader_click_handler' => $oUploader->getNameJsInstanceUploader() . '.showUploaderForm();',
+                'bx_if:create_folder_allowed' => [
+                    'condition' => $iMaxNestingLevel == 0 || $this->oModule->_oDb->getFolderNestingLevel($this->aCurrent['restriction']['folder']['value']) < $iMaxNestingLevel,
+                    'content' => [
+                        'js_object' => $sJsObject,
+                        'folder_name_message' => _t('_bx_files_txt_folder_name'),
+                    ]
+                ],
+            ];
+        }
+
+        $aBulkActions = [];
+        $aBulkActions[] = ['js_object' => $sJsObject, 'handler' => 'downloadFiles', 'title' => _t('_bx_files_bulk_action_title_download'), 'icon' => 'download'];
+        if (isLogged()) {
+            $aBulkActions[] = ['js_object' => $sJsObject, 'handler' => 'bookmarkFiles', 'title' => _t('_bx_files_bulk_action_title_bookmark'), 'icon' => 'star'];
+            if ($bUploadAllowed) {
+                $aBulkActions[] = ['js_object' => $sJsObject, 'handler' => 'deleteFiles', 'title' => _t('_bx_files_bulk_action_title_delete'), 'icon' => 'remove'];
+                $aBulkActions[] = ['js_object' => $sJsObject, 'handler' => 'moveFiles', 'title' => _t('_bx_files_bulk_action_title_move_to'), 'icon' => 'file-export'];
+            }
+        }
+
+        return [
+            'sorting_dropdown' => $oForm->genRowStandard($aInputSorting),
+            'filter_box' => $oForm->genRowStandard($aInputFilter),
+            'bx_if:upload_visible' => [
+                'condition' => $bUploadAllowed,
+                'content' => $aUploadButtonParams,
+            ],
+            'bx_if:bookmarks_visible' => [
+                'condition' => isLogged(),
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'bookmark_enabled' => $aParams['bookmarks'] ? 'fas' : 'far',
+                ],
+            ],
+            'bx_if:table_layout_btn' => [
+                'condition' => $aParams['layout'] == 'table',
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'unit_view_param' => $aParams['aRequestParams']['unit_view_param'],
+                ],
+            ],
+            'bx_if:gallery_layout_btn' => [
+                'condition' => $aParams['layout'] == 'gallery',
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'unit_view_param' => $aParams['aRequestParams']['unit_view_param'],
+                ],
+            ],
+            'bx_repeat:sorting_options' => $aSortingOptions,
+            'bx_repeat:bulk_actions' => $aBulkActions,
+        ];
+    }
+
+    protected function getBrowseToolbarBreadcrumbs($sJsObject) {
+        $aFolderPathTmpl = [];
+        $aFolderPath = $this->oModule->_oDb->getFolderPath($this->aCurrent['restriction']['folder']['value']);
+        if ($aFolderPath) {
+            $aFolderPathTmpl[] = [
+                'name' => _t('_bx_files_txt_folder_root'),
+                'folder' => '0',
+                'js_object' => $sJsObject,
+            ];
+
+            foreach ($aFolderPath as $aEntry) {
+                $aFolderPathTmpl[] = [
+                    'name' => strmaxtextlen($aEntry['name'], 20),
+                    'folder' => $aEntry['folder'],
+                    'js_object' => $sJsObject,
+                ];
+            }
+        }
+
+        return $aFolderPathTmpl;
+    }
+
+    protected function getBrowseToolbar(&$aParams) {
+        $this->oModule->_oTemplate->addJs('toolbar_tools.js');
+
+        $sUniqueIdent = mt_rand();
+        $aParams['unique_ident'] = $sUniqueIdent;
+        $aParams['context'] = $this->sMode == 'context' ? $this->iFileManagerUploadTo : '';
+
+        $sJsCode = $this->oModule->_oTemplate->getJsCode(['type' => 'toolbar_tools', 'uniq' => $sUniqueIdent], $aParams);
+        $sJsObject = $this->oModule->_oConfig->getJsObject(['type' => 'toolbar_tools', 'uniq' => $sUniqueIdent]);
+
+        $this->setUnitParams(['toolbar_js_object' => $sJsObject]);
+
+        $aToolbarControls = $this->getBrowseToolbarControls($aParams, $sJsObject);
+
+        return $this->oModule->_oTemplate->parseHtmlByName('files_browser_toolbar.html', array_merge($aToolbarControls, [
+            'js_object' => $sJsObject,
+            'js_code' => $sJsCode,
+            'unique_ident' => $sUniqueIdent,
+            'bx_repeat:folder_path' => $this->getBrowseToolbarBreadcrumbs($sJsObject),
+            'bx_if:select_all_checkbox' => [
+                'condition' => $aParams['layout'] == 'table',
+                'content' => [
+                    'js_object' => $sJsObject,
+                    'unique_ident' => $sUniqueIdent,
+                ],
+            ],
+        ]));
+    }
+}
+
+/** @} */

+ 231 - 0
modules/boonex/files/updates/14.0.2_14.0.3/source/classes/BxFilesTemplate.php

@@ -0,0 +1,231 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Files Files
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/*
+ * Module representation.
+ */
+class BxFilesTemplate extends BxBaseModTextTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+        $this->MODULE = 'bx_files';
+        parent::__construct($oConfig, $oDb);
+    }
+
+    protected function getUnitThumbAndGallery ($aData)
+    {
+        $aFile = BxDolModule::getInstance($this->MODULE)->getContentFile($aData);
+
+        if (!$aFile || !$aFile['is_image'])
+            return array('', '');
+
+        $sPhotoThumb = '';
+        if ($oImagesTranscoder = BxDolTranscoderImage::getObjectInstance(BxDolModule::getInstance($this->MODULE)->_oConfig->CNF['OBJECT_IMAGES_TRANSCODER_GALLERY']))
+            $sPhotoThumb = $oImagesTranscoder->getFileUrl($aFile['id']);
+
+        return array($sPhotoThumb, $sPhotoThumb);
+    }
+    
+    function unit ($aData, $isCheckPrivateContent = true, $sTemplateName = 'unit.html', $aParams = array())
+    {
+        $oModule = $this->getModule();
+
+        $sResult = $this->checkPrivacy($aData, $isCheckPrivateContent, $oModule, $sTemplateName);
+        if ($sResult)
+            return $sResult;
+
+        $CNF = &$oModule->_oConfig->CNF;
+
+        $aFile = $oModule->getContentFile($aData);
+        $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+
+        if(isset($CNF['FIELD_TITLE']) && empty($aData[$CNF['FIELD_TITLE']]))
+            $aData[$CNF['FIELD_TITLE']] = _t('_sys_txt_no_title');
+
+        $aParams['template_name'] = $sTemplateName;
+        $aVars = $this->getUnit($aData, $aParams);
+        $aVars['icon'] = $aFile && $oStorage ? $oStorage->getFontIconNameByFileName($aFile['file_name']) : 'far file';
+
+        $aVars['bx_if:no_thumb']['content']['icon'] = $aVars['icon'];
+
+
+        $aVars['bx_if:inline_menu']['condition'] = $aData[$CNF['FIELD_ID']] > 0 && isset($aParams['show_inline_menu']) && $aParams['show_inline_menu'];
+        $aVars['bx_if:inline_menu']['content'] = [];
+        if ($aVars['bx_if:inline_menu']['condition']) {
+            bx_import('BxTemplMenu');
+            $oMenu = BxTemplMenu::getObjectInstance('bx_files_view_inline', $this);
+            if ($oMenu) {
+                $oMenu->setContentId($aData[$CNF['FIELD_ID']]);
+                $oMenu->setBookmarked($this->_oDb->isFileBookmarked($aData[$CNF['FIELD_ID']], bx_get_logged_profile_id()));
+                $bEditAllowed = $this->getModule()->checkAllowedEdit($aData, false) === CHECK_ACTION_RESULT_ALLOWED;
+                $oMenu->setAllowEditOptions($bEditAllowed);
+                $aVars['bx_if:inline_menu']['content']['menu'] = $oMenu->getCode();
+            }
+        }
+
+        $sAltContentUrlHandler = '';
+        if ($oModule->_oDb->getParam($CNF['PARAM_LINK_TO_PREVIEW'])) {
+            $sAltContentUrlHandler = '$.get(\'' . BX_DOL_URL_ROOT . $oModule->_oConfig->getBaseUri() . 'entry_preview/' . $aData[$CNF['FIELD_ID']] . '\', processJsonData, \'json\'); return false;';
+        }
+
+        if ($aData['type'] == 'folder') {
+            $aVars['bx_if:meta']['condition'] = false;
+            $aVars['bx_if:no_thumb']['content']['icon'] = $aVars['icon'] = 'folder';
+            $sAltContentUrlHandler = "{$aParams['toolbar_js_object']}.folderNavigate({$aData[$CNF['FIELD_ID']]}); return false;";
+        }
+
+        $aVars['alt_content_url_handler'] = $sAltContentUrlHandler;
+        $aVars['bx_if:no_thumb']['content']['alt_content_url_handler'] = $sAltContentUrlHandler;
+        $aVars['bx_if:thumb']['content']['alt_content_url_handler'] = $sAltContentUrlHandler;
+
+        return $this->parseHtmlByName($sTemplateName, $aVars);
+    }
+
+    public function entryFilePreview ($aData)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $oModule = BxDolModule::getInstance($this->MODULE);
+
+        $sNoPreview = MsgBox(_t('_bx_files_txt_preview_not_available'));
+        if (!($aFile = $oModule->getContentFile($aData)))
+            return $sNoPreview;
+        if (!($oFileHandler = BxDolFileHandler::getObjectInstanceByFile($aFile['file_name'])))
+            return $sNoPreview;
+        if (!($oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE'])))
+            return $sNoPreview;
+        if (!($sFileUrl = $oStorage->getFileUrlById($aFile['id'])))
+            return $sNoPreview;
+
+        if(strncmp('audio/', $aFile['mime_type'], 6) === 0 && !empty($CNF['OBJECT_SOUNDS_TRANSCODER']))
+            $oFileHandler->setTranscoder(BxDolTranscoderAudio::getObjectInstance($CNF['OBJECT_SOUNDS_TRANSCODER']));
+        else if(strncmp('video/', $aFile['mime_type'], 6) === 0 && !empty($CNF['OBJECT_VIDEOS_TRANSCODERS']) && is_array($CNF['OBJECT_VIDEOS_TRANSCODERS'])) {
+            $aTranscoders = $CNF['OBJECT_VIDEOS_TRANSCODERS'];
+            array_walk($aTranscoders, function(&$sValue) {
+                $sValue = BxDolTranscoderVideo::getObjectInstance($sValue);
+            });
+
+            $oFileHandler->setTranscoder($aTranscoders);
+        }
+
+        return $oFileHandler->display($sFileUrl, $aFile);
+    }
+
+    public function entryText ($aData, $sTemplateName = 'entry-text.html')
+    {
+        $aTmplVars = $this->getTmplVarsText($aData);
+
+        return $this->_bIsApi ? $aTmplVars : $this->parseHtmlByName($sTemplateName, $aTmplVars);
+    }
+
+    public function entryInfoPopup($aData) {
+        $CNF = &$this->getModule()->_oConfig->CNF;
+
+        $aFile = $this->getModule()->getContentFile($aData);
+
+        $oAuthor = BxDolProfile::getInstance($aData[$CNF['FIELD_AUTHOR']]);
+        if (!$oAuthor) $oAuthor = BxDolProfileUndefined::getInstance();
+
+        $aForm = [
+            'params' => [
+                'view_mode' => true,
+            ],
+            'inputs' => [
+                'size' => [
+                    'caption' => _t('_bx_files_entry_info_size'),
+                    'type' => 'value',
+                    'value' => _t_format_size($aFile['size']),
+                ],
+                'date' => [
+                    'caption' => _t('_bx_files_entry_info_date'),
+                    'type' => 'date_time',
+                    'value' => $aData[$CNF['FIELD_ADDED']],
+                ],
+                'author' => [
+                    'caption' => _t('_bx_files_entry_info_author'),
+                    'type' => 'textarea',
+                    'html' => 1,
+                    'value' => '<a href="'.$oAuthor->getUrl().'">'.$oAuthor->getDisplayName().'</a>',
+                ],
+            ],
+        ];
+
+        if ($aData['type'] == 'folder') {
+            unset($aForm['inputs']['size']);
+            unset($aForm['inputs']['author']);
+        }
+
+        if ($aData[$CNF['FIELD_ALLOW_VIEW_TO']] < 0) {
+            $oProfile = BxDolProfile::getInstance(-$aData[$CNF['FIELD_ALLOW_VIEW_TO']]);
+            if ($oProfile) {
+                $aForm['inputs']['context'] = [
+                    'caption' => _t('_bx_files_entry_info_context'),
+                    'type' => 'textarea',
+                    'html' => 1,
+                    'value' => '<a href="' . $oProfile->getUrl() . '">' . $oProfile->getDisplayName() . '</a>',
+                ];
+            }
+        }
+
+        $oForm = new BxTemplFormView($aForm);
+        return $this->parseHtmlByName('entry-popup.html', [
+            'content' => $oForm->getCode(),
+            'bx_repeat:init_scripts' => [],
+        ]);
+    }
+
+    public function getTmplVarsText($aData)
+    {
+        $aVars = parent::getTmplVarsText($aData);
+        $aVars = array_merge($aVars, array(
+            'entry_preview' => $this->entryFilePreview($aData),
+            'bx_if:show_content' => array(
+                'condition' => !empty($aVars['entry_title']) || !empty($aVars['entry_text']),
+                'content' => array(
+                    'entry_title' => $aVars['entry_title'],
+                    'entry_text' => $aVars['entry_text']
+                )
+            )
+        ));
+
+        return $aVars;
+    }
+
+    public function getJsTree($aFilesToMove, $aFolders) {
+        $sJsCss = '';
+        $sJsCss .= $this->addCss(BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'jstree/themes/default/|style.min.css', true);
+        $sJsCss .= $this->addJs(BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'jstree/|jstree.min.js', true);
+
+        return $this->parseHtmlByName('folders_tree.html', [
+            'js_css' => $sJsCss,
+            'list' => $this->foldersToList($aFolders, 0),
+            'files' => json_encode($aFilesToMove),
+            'actions_url' => BX_DOL_URL_ROOT.$this->getModule()->_oConfig->getBaseUri(),
+        ]);
+    }
+
+    private function foldersToList($aFolders, $iLevel) {
+        if (!$aFolders) return;
+
+        $sTreeList = '<ul>';
+        foreach ($aFolders as $aFolder) {
+            $sTreeList .=
+                '<li id="bx-files-folder-'.$aFolder['id'].'" '.($iLevel == 0 ? 'class="jstree-open"' : '').' data-jstree=\'{"icon":"sys-icon folder"}\'>'.
+                    bx_process_output($aFolder['title']).
+                    $this->foldersToList($aFolder['subfolders'], $iLevel+1).
+                '</li>';
+        }
+        $sTreeList .= '</ul>';
+
+        return $sTreeList;
+    }
+}
+
+/** @} */

+ 142 - 0
modules/boonex/files/updates/14.0.2_14.0.3/source/install/config.php

@@ -0,0 +1,142 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Files Files
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_files',
+    'title' => 'Files',
+    'note' => 'Basic blogging module.',
+    'version' => '14.0.3',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/files/',
+    'home_uri' => 'files',
+
+    'db_prefix' => 'bx_files_',
+    'class_prefix' => 'BxFiles',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Files',
+
+    /**
+     * List of page triggers.
+     */
+    'page_triggers' => array (
+    	'trigger_page_profile_view_entry',
+    ),
+    
+    /**
+     * Menu triggers.
+     */
+    'menu_triggers' => array(
+    	'trigger_profile_view_submenu',
+    	'trigger_group_view_submenu',
+    ),
+
+	/**
+     * Storage objects to automatically delete files from upon module uninstallation.
+     * Note. Don't add storage objects used in transcoder objects.
+     */
+    'storages' => array(
+    	'bx_files_files'
+    ),
+
+    /**
+     * Transcoders.
+     */
+    'transcoders' => array(
+        'bx_files_preview',
+        'bx_files_gallery',
+
+        'bx_files_sounds_mp3',
+
+        'bx_files_videos_poster',
+        'bx_files_videos_poster_preview',
+        'bx_files_videos_mp4',
+        'bx_files_videos_mp4_hd'
+    ),
+
+	/**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_files',
+    	'bx_files_cmts',
+    ),
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+    	'process_esearches' => 1,
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable_success' => array(
+        'process_menu_triggers' => 1,
+        'process_page_triggers' => 1,
+    	'process_esearches' => 1,
+    	'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+    	'update_relations' => 1,
+    	'unregister_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+    	'register_transcoders' => 1,
+    	'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Relations Section
+     */
+    'relations' => array(
+    	'bx_timeline',
+    	'bx_notifications',
+    ),
+
+);
+
+/** @} */

+ 51 - 0
modules/boonex/groups/updates/14.0.3_14.0.4/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Groups',
+    'version_from' => '14.0.3',
+    'version_to' => '14.0.4',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/groups/updates/update_14.0.3_14.0.4/',
+    'home_uri' => 'groups_update_1403_1404',
+
+    'module_dir' => 'boonex/groups/',
+    'module_uri' => 'groups',
+
+    'db_prefix' => 'bx_groups_',
+    'class_prefix' => 'BxGroups',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Groups',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/groups/updates/14.0.3_14.0.4/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxGroupsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 152 - 0
modules/boonex/groups/updates/14.0.3_14.0.4/source/install/config.php

@@ -0,0 +1,152 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Groups Groups
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_groups',
+    'title' => 'Groups',
+    'note' => 'Basic group profiles functionality.',
+    'version' => '14.0.4',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/groups/',
+    'home_uri' => 'groups',
+
+    'db_prefix' => 'bx_groups_',
+    'class_prefix' => 'BxGroups',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Groups',
+
+    /**
+     * Connections.
+     */
+    'connections' => array(
+    	'sys_profiles_friends' => array ('type' => 'profiles'),
+		'sys_profiles_subscriptions' => array ('type' => 'profiles'),
+    ),
+
+    /**
+     * Menu triggers.
+     */
+    'menu_triggers' => array(
+    	'trigger_profile_view_submenu',
+        'trigger_group_view_submenu',
+        'trigger_group_snippet_meta',
+        'trigger_group_view_actions',
+    ),
+
+    /**
+     * Page triggers.
+     */
+    'page_triggers' => array (
+    	'trigger_page_group_view_entry', 
+    ),
+
+	/**
+     * Storage objects to automatically delete files from upon module uninstallation.
+     * Note. Don't add storage objects used in transcoder objects.
+     */
+    'storages' => array(
+    	'bx_groups_pics'
+    ),
+
+	/**
+     * Transcoders.
+     */
+    'transcoders' => array(
+    	'bx_groups_icon', 
+    	'bx_groups_thumb', 
+    	'bx_groups_avatar', 
+        'bx_groups_avatar_big', 
+    	'bx_groups_picture', 
+    	'bx_groups_cover', 
+    	'bx_groups_cover_thumb',
+        'bx_groups_gallery'
+    ),
+
+    /**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_groups',
+    	'bx_groups_cmts',
+    ),
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+    	'process_esearches' => 1,
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    	'process_connections' => 1,
+        'process_deleted_profiles' => 1,
+    	'update_relations' => 1,        
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable_success' => array(
+    	'process_menu_triggers' => 1,
+    	'process_page_triggers' => 1,
+    	'process_esearches' => 1,
+    	'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'unregister_transcoders' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+    	'register_transcoders' => 1,
+    	'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Connections Section
+     */
+    'relations' => array(
+        'bx_timeline',
+        'bx_notifications',
+    ),
+);
+
+/** @} */

+ 20 - 0
modules/boonex/groups/updates/14.0.3_14.0.4/source/js/main.js

@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Groups Groups
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+function BxGroupsMain(oOptions) {
+    BxBaseModGroupsMain.call(this, oOptions);
+
+    this._sObjName = oOptions.sObjName == undefined ? 'oBxGroupsMain' : oOptions.sObjName;
+}
+
+BxGroupsMain.prototype = Object.create(BxBaseModGroupsMain.prototype);
+BxGroupsMain.prototype.constructor = BxGroupsMain;
+
+/** @} */

+ 51 - 0
modules/boonex/invites/updates/14.0.3_14.0.4/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Invitations',
+    'version_from' => '14.0.3',
+    'version_to' => '14.0.4',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/invites/updates/update_14.0.3_14.0.4/',
+    'home_uri' => 'invites_update_1403_1404',
+
+    'module_dir' => 'boonex/invites/',
+    'module_uri' => 'invites',
+
+    'db_prefix' => 'bx_inv_',
+    'class_prefix' => 'BxInv',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Invitations',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/invites/updates/14.0.3_14.0.4/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxInvUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 302 - 0
modules/boonex/invites/updates/14.0.3_14.0.4/source/classes/BxInvTemplate.php

@@ -0,0 +1,302 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Invites Invites
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxInvTemplate extends BxBaseModGeneralTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+        parent::__construct($oConfig, $oDb);
+    }
+
+    public function getCssJs()
+    {
+        $this->addCss(array(
+            'main.css',
+        ));
+
+        $this->addJs(array(
+            'clipboard.min.js',
+            'jquery.form.min.js',
+            'jquery.anim.js',
+            'main.js'
+        ));
+    }
+
+    public function getInclude()
+    {
+        $this->getCssJs();
+        return $this->getJsCode('main');
+    }
+
+    public function getBlockRequestText($aRequest)
+    {
+        $sText = bx_process_output(nl2br($aRequest['text']), BX_DATA_TEXT_MULTILINE);
+        if($this->_bIsApi)
+            return [['text' => $sText]];
+
+        return $this->parseHtmlByName('request_text.html', array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'text' => $sText
+        ));
+    }
+    
+    public function getBlockInviteInfo($aRequest)
+    {
+        $aVars = array(
+            'date_sent_title' => _t('_bx_invites_title_date_invite_sent'),
+            'date_seen_title' => _t('_bx_invites_title_date_invite_seen'),
+            'bx_repeat:items' => array()
+        );
+        foreach ($aRequest as $r) {
+            $aVars['bx_repeat:items'][] = array (
+                 'date_sent' => bx_time_js($r['date'], BX_FORMAT_DATE_TIME, true),
+                 'date_seen' => ($r['date_seen'] != '' ? bx_time_js($r['date_seen'], BX_FORMAT_DATE_TIME, true) : ' - ')
+            );
+        }
+        return $this->parseHtmlByName('invite_info.html', $aVars);
+    }
+    
+    public function getBlockInvite($iAccountId, $iProfileId, $bRedirect = false)
+    {
+        $mInvitesRemain = $this->_oConfig->getCountPerUser();
+        if($mInvitesRemain === true)
+            $mInvitesRemain = _t('_bx_invites_txt_unlimited');
+        
+        $sUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink($this->_oConfig->CNF['URL_INVITE']));
+
+        if($bRedirect) {
+            $sRedirectCode = $this->_oConfig->getRedirectCode();
+
+            list($sPageLink, $aPageParams) = bx_get_base_url_inline();
+            $sRedirectValue = bx_append_url_params($sPageLink, $aPageParams);
+            if($this->_bIsApi){
+                $sRedirectValue = $aPageParams['params'][0];
+            }
+
+            $oSession = BxDolSession::getInstance();
+            if($oSession->isValue($sRedirectCode))
+                $oSession->unsetValue($sRedirectCode);
+            $oSession->setValue($sRedirectCode, $sRedirectValue);
+        }
+        
+        if($this->_bIsApi){
+            return [bx_api_get_block('invite', ['remain' => $mInvitesRemain, 'request_url' => 'bx_invites/get_link/'])];
+        }
+
+        $this->getCssJs();
+        return $this->parseHtmlByName('block_invite.html', [
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'js_object' => $this->_oConfig->getJsObject('main'),
+            'text' => _t('_bx_invites_txt_invite_block_text', $mInvitesRemain),
+            'url' => $sUrl,
+            'js_code' => $this->getJsCode('main')
+        ]);
+    }
+
+    public function getBlockRequest()
+    {
+        $sUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink($this->_oConfig->CNF['URL_REQUEST']));
+
+        $this->getCssJs();
+        return $this->parseHtmlByName('block_request.html', array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'text' => _t('_bx_invites_txt_request_block_text'),
+            'bx_if:show_button_request' => array(
+                'condition' => $this->_oConfig->isRequestInvite(),
+                'content' => array(
+                    'url' => $sUrl
+                )
+            )
+        ));
+    }
+    public function getBlockFormRequest()
+    {
+        if(!$this->_oConfig->isRequestInvite())
+            return $this->_bIsApi ? [bx_api_get_msg(_t('_bx_invites_err_not_available'))] : MsgBox(_t('_bx_invites_err_not_available'));
+
+        $mixedAllowed = $this->getModule()->isAllowedRequest(0);
+        if($mixedAllowed !== true)
+            return $this->_bIsApi ? [bx_api_get_msg($mixedAllowed)] : MsgBox($mixedAllowed);
+
+        $CNF = &$this->_oConfig->CNF;
+
+        $sJsObject = $this->_oConfig->getJsObject('main');
+        $oPermalink = BxDolPermalinks::getInstance();
+
+        $oForm = BxDolForm::getObjectInstance($this->_oConfig->getObject('form_request'), $this->_oConfig->getObject('form_display_request_send'));
+        $oForm->aFormAttrs['action'] = bx_absolute_url($oPermalink->permalink($CNF['URL_REQUEST']));
+
+        $sFormId = $oForm->getId();
+        $sEval = $sJsObject . '.onRequestFormSubmit(oData)';
+
+        $oForm->initChecker();
+        if(!$oForm->isSubmittedAndValid()) {
+            $sForm = $oForm->getCode();
+            if(!$oForm->isSubmitted()) {
+                $this->getCssJs();
+                return $this->_bIsApi ? [bx_api_get_block('form', $oForm->getCodeAPI(), ['ext' => ['request' => ['url' => '/api.php?r=' . $this->_oModule->getName() . '/get_block_form_request', 'immutable' => true]]])] : $this->parseHtmlByName('block_request_form.html', array(
+                    'style_prefix' => $this->_oConfig->getPrefix('style'),
+                    'js_object' => $sJsObject,
+                    'js_code' => $this->getJsCode('main'),
+                    'form' => $sForm,
+                    'form_id' => $sFormId,
+                ));
+            }
+
+            if(!$oForm->isValid())
+                return $this->_bIsApi ? [bx_api_get_block('form', $oForm->getCodeAPI(), ['ext' => ['request' => ['url' => '/api.php?r=' . $this->_oModule->getName() . '/get_block_form_request', 'immutable' => true]]])] : array('content' => $sForm, 'content_id' => $sFormId, 'eval' => $sEval);
+        }
+        
+        $sEmail = $oForm->getCleanValue('email');
+        $oAccountQuery = BxDolAccountQuery::getInstance();
+        $iAccount = $oAccountQuery->getIdByEmail($sEmail);
+        if ($iAccount > 0)
+            return $this->_bIsApi ? [bx_api_get_msg(_t('_bx_invites_err_already_registed', bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=forgot-password'))))] : array('content' => MsgBox(_t('_bx_invites_err_already_registed', bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=forgot-password')))), 'content_id' => $sFormId, 'eval' => $sEval);
+        $iCountByEmail = $this->_oDb->getRequests(array('type' => 'count_by_email', 'value' => $sEmail));
+        if ($iCountByEmail > 0)
+            return  $this->_bIsApi ? [bx_api_get_msg(_t('_bx_invites_err_already_send'))] : array('content' => MsgBox(_t('_bx_invites_err_already_send')), 'content_id' => $sFormId, 'eval' => $sEval);
+       
+        $sIp = getVisitorIP();
+        $iId = (int)$oForm->insert(array('nip' => ip2long($sIp),'date' => time()));
+        if(!$iId)
+            return $this->_bIsApi ? [bx_api_get_msg(_t('_bx_invites_err_cannot_perform'))] : array('content' => MsgBox(_t('_bx_invites_err_cannot_perform')), 'content_id' => $sFormId, 'eval' => $sEval);
+        $this->getModule()->onRequest($iId);
+        $sRequestsEmail = $this->_oConfig->getRequestsEmail();
+        if(!empty($sRequestsEmail)) {
+            $sManageUrl = bx_absolute_url($oPermalink->permalink($CNF['URL_REQUESTS']));
+
+            $aMessage = BxDolEmailTemplates::getInstance()->parseTemplate('bx_invites_request_form_message', array(
+                'sender_name' => bx_process_output($oForm->getCleanValue('name')),
+                'sender_email' => bx_process_output($oForm->getCleanValue('email')),
+                'sender_ip' => $sIp,
+                'manage_url' => $sManageUrl
+            ));
+            
+            $aRequestsEmails = explode(',', $sRequestsEmail);
+            foreach ($aRequestsEmails as $s) {
+                sendMail(trim($s), $aMessage['Subject'], $aMessage['Body'], 0, array(), BX_EMAIL_SYSTEM);
+            }
+        }
+
+        return $this->_bIsApi ? [bx_api_get_msg(_t('_bx_invites_msg_request_sent'))] : array('content' => MsgBox(_t('_bx_invites_msg_request_sent')), 'content_id' => $sFormId, 'eval' => $sEval);
+    }
+
+    public function getLinkPopup($sLink)
+    {
+        $sId = $this->_oConfig->getHtmlIds('link_popup');
+        $sTitle = _t('_bx_invites_txt_link_popup_title');
+        $sContent = $this->parseHtmlByName('popup_link.html', array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'html_id_link' => $this->_oConfig->getHtmlIds('link_input'),
+            'link' => $sLink
+        ));
+
+        return BxTemplFunctions::getInstance()->popupBox($sId, $sTitle, $sContent, true);
+    }
+    
+    public function getJsCode($sType, $aParams = array(), $mixedWrap = true)
+    {
+        $sGrid = 'requests';
+        if(!empty($aParams['grid'])) {
+            $sGrid = $aParams['grid'];
+            unset($aParams['grid']);
+        }
+
+        $aParams = array_merge(array(
+            'aHtmlIds' => $this->_oConfig->getHtmlIds(),
+            'sObjNameGrid' => $this->_oConfig->getObject('grid_' . $sGrid),
+        ), $aParams);
+
+        return parent::getJsCode($sType, $aParams);
+    }
+
+    public function getMenuForManageBlocks($sType)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        
+        $sMenu = '';
+		$oPermalink = BxDolPermalinks::getInstance();
+
+		$aMenuItems = array();
+		$aMenuItems[] = array('id' => 'invites-requests', 'name' => 'invites-requests', 'class' => '', 'link' => $oPermalink->permalink($CNF['URL_REQUESTS']), 'target' => '_self', 'title' => _t('_bx_invites_menu_item_title_manage_requests'), 'active' => 1);
+		$aMenuItems[] = array('id' => 'invites-invites', 'name' => 'invites-invites', 'class' => '', 'link' => $oPermalink->permalink($CNF['URL_INVITES']), 'target' => '_self', 'title' => _t('_bx_invites_menu_item_title_manage_invites'), 'active' => 1);
+
+		if(count($aMenuItems) > 1) {
+	        $oMenu = new BxTemplMenu(array(
+	            'template' => 'menu_vertical.html', 
+	            'menu_items' => $aMenuItems
+	        ), $this);
+	        $oMenu->setSelected($this->getModule()->_aModule['name'], 'invites-' . $sType);
+	        $sMenu = $oMenu->getCode();
+		}
+        return $sMenu;
+    }
+    
+    public function getProfilesByAccount($iAccountId, $iMaxVisible = 2)
+    {
+        $aProfiles = BxDolAccount::getInstance($iAccountId)->getProfiles();
+        $iProfiles = count($aProfiles);
+
+        $aTmplVars = array (
+            'class_cnt' => '',
+            'bx_repeat:profiles' => array(),
+            'bx_if:profiles_more' => array(
+                'condition' => $iProfiles > $iMaxVisible,
+                'content' => array(
+                    'html_id' => $this->_oConfig->getHtmlIds('profile_more_popup') . $iAccountId,
+                    'more' => _t('_bx_accnt_txt_more', $iProfiles - $iMaxVisible),
+                    'more_attr' => bx_html_attribute(_t('_bx_accnt_txt_see_more')),
+                    'popup' => '',
+                ),
+            ),
+        );
+
+        $aTmplVarsPopup = array (
+            'class_cnt' => ' bx-def-padding',
+            'bx_repeat:profiles' => array(),
+            'bx_if:profiles_more' => array(
+                'condition' => false,
+                'content' => array(),
+            ),
+        );
+
+        $i = 0;
+        foreach ($aProfiles as $iProfileId => $aProfile) {
+            $oProfile = BxDolProfile::getInstance($iProfileId);
+            if(!$oProfile)
+                continue;
+
+            $sName = $oProfile->getDisplayName();
+            $aTmplVarsProfile = array (
+                'html_id' => $this->_oConfig->getHtmlIds('profile') . $aProfile['id'],
+                'id' => $oProfile->id(),
+                'url' => $oProfile->getUrl(),
+                'name' => $sName,
+                'name_attr' => bx_html_attribute($sName)
+            );
+
+            if($i < $iMaxVisible)
+                $aTmplVars['bx_repeat:profiles'][] = $aTmplVarsProfile;
+            if($i >= $iMaxVisible)
+                $aTmplVarsPopup['bx_repeat:profiles'][] = $aTmplVarsProfile;
+
+            ++$i;
+        }
+
+        if($aTmplVarsPopup['bx_repeat:profiles']) {
+            $aTmplVars['bx_if:profiles_more']['content']['popup'] = BxTemplFunctions::getInstance()->transBox('', $this->parseHtmlByName('profiles.html', $aTmplVarsPopup));
+        }
+
+        return $this->parseHtmlByName('profiles.html', $aTmplVars);
+    }
+}
+
+/** @} */

+ 80 - 0
modules/boonex/invites/updates/14.0.3_14.0.4/source/install/config.php

@@ -0,0 +1,80 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Invites Invites
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_invites',
+    'title' => 'Invitations',
+    'note' => 'Invitations module.',
+    'version' => '14.0.4',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/invites/',
+    'home_uri' => 'invites',
+
+    'db_prefix' => 'bx_inv_',
+    'class_prefix' => 'BxInv',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Invitations',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+    
+    /**
+      * Relations Section
+      */
+    'relations' => array(
+    	'bx_notifications'
+    ),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/lucid/updates/14.0.0_14.0.1/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Lucid',
+    'version_from' => '14.0.0',
+    'version_to' => '14.0.1',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/lucid/updates/update_14.0.0_14.0.1/',
+    'home_uri' => 'lucid_update_1400_1401',
+
+    'module_dir' => 'boonex/lucid/',
+    'module_uri' => 'lucid',
+
+    'db_prefix' => 'bx_lucid_',
+    'class_prefix' => 'BxLucid',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Boonex Lucid Template',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 26 - 0
modules/boonex/lucid/updates/14.0.0_14.0.1/install/installer.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+class BxLucidUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+
+    public function update($aParams)
+    {
+        $aResult = parent::update($aParams);
+        if(!$aResult['result'])
+            return $aResult;
+
+        $oCacheUtilities = BxDolCacheUtilities::getInstance();
+        $oCacheUtilities->clear('css');
+        $oCacheUtilities->clear('template');
+
+        return $aResult;
+    }
+}

+ 630 - 0
modules/boonex/lucid/updates/14.0.0_14.0.1/source/data/template/system/css/menu.less

@@ -0,0 +1,630 @@
+@import (reference) 'default.less';
+@import '../../../../../../../template/css/menu.css';
+
+.@{bx-root-class-name} {
+    
+    .bx-menu-toolbar-padding {
+        padding-top: 0;
+    }
+
+
+    /*--- Main (Top) Menu ---*/
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site {
+        display: block;
+        white-space: normal;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li {
+        width: auto;
+        height: auto;
+        margin: 0;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li:not(.bx-menu-item-more-auto) {
+        display: block;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site > li > a,
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site ul.bx-menu-submenu-more-auto > li > a {
+        padding: 0px 0.5rem;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li:hover {
+        opacity: 1.0;
+    }
+
+    .bx-sliding-smenu-submenu {
+        position: absolute;
+        display: none;
+
+        top: 100%;
+        left: 0px;
+        min-width: 100%;
+
+        background-color: #ffffff;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site > li.bx-sliding-smenu-tab-active .bx-sliding-smenu-submenu {
+        display: block;
+    }
+
+    .bx-sliding-smenu-submenu .bx-sliding-sms-cnt ul li,
+    .bx-sliding-smenu-submenu .bx-sliding-sms-cnt ul li a {
+        margin: 0px;
+        padding: 0px;
+    }
+    .bx-sliding-smenu-submenu .bx-sliding-sms-cnt ul li a {
+        line-height: 2.0rem;
+    }
+    .bx-sliding-smenu-submenu .bx-sliding-sms-cnt ul li:hover,
+    .bx-sliding-smenu-submenu .bx-sliding-sms-cnt ul li a:hover {
+        background-color: transparent;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-floating-blocks-icon, 
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-floating-blocks-title,
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-floating-blocks-arrow {
+        display: inline-block;
+        vertical-align: middle;
+    }
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-floating-blocks-icon img,
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-floating-blocks-icon .sys-icon {
+        width: auto;
+        max-height: 2rem;
+
+        margin: 0 0.5rem 0 0;
+    }
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-floating-blocks-icon .sys-icon {
+        line-height: 2rem;
+        font-size: 1.2rem;
+    }
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-submenu-more-auto li .bx-menu-floating-blocks-icon .sys-icon { 
+        vertical-align: baseline;
+        height: auto;
+        line-height: normal;
+    }
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-floating-blocks-icon img {
+        background-pocition: center center;
+        background-repeat: none;
+        background-size: 60% 60%;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-floating-blocks-arrow .sys-icon {
+        width: auto;
+        height: auto;
+
+        margin: 0px;
+
+        font-size: 1.2rem;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li .bx-menu-floating-blocks-arrow .sys-icon.active {
+        display: none;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li.bx-sliding-smenu-tab-active .bx-menu-floating-blocks-arrow .sys-icon.active {
+        display: inline;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li.bx-sliding-smenu-tab-active .bx-menu-floating-blocks-arrow .sys-icon.inactive {
+        display: none;
+    }
+
+
+    /*--- Submenu ---*/
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu-wrp {
+        position: relative;
+
+        -webkit-overflow-scrolling: touch;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-bar:not(.bx-menu-more-auto) .bx-menu-main-submenu-wrp {
+        overflow-x: auto;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu-group {
+        position: relative;
+
+        width: 100%;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-bar:not(.bx-menu-more-auto) .bx-menu-main-submenu-group {
+        display: table;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu-hr {
+        width: 100%;
+        height: 0.1875rem;
+        -webkit-border-radius: 2px;
+        border-radius: 2px;
+        background-color: rgba(15, 93, 162, 0.2);
+        box-shadow: 0 1px 0 0 rgba(255,255,255,1);
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu {
+        line-height: 2.875rem;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu li {
+        position: relative;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu > li > a {
+        display: -webkit-flex;
+        display: flex;
+        -webkit-flex-flow: row nowrap;
+        flex-flow: row nowrap;
+        -webkit-align-items: center;
+        align-items: center;
+        line-height: 3.125rem;
+    }
+
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu li.bx-menu-tab-active > a {
+        font-weight: 900;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu li > a span {
+        margin-left: 0.625rem;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu li > a span:first-child {
+        margin-left: 0;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu li .bx-mms-item-hr {
+        position: absolute;
+        display: none;
+
+        width: 100%;
+        height: 0.1875rem;
+        left: 0;
+        bottom: -0.1875rem;
+
+        -webkit-border-radius: 0.25rem;
+        border-radius: 0.25rem;
+        background-color: rgba(29, 75, 96, 1);
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu li.bx-menu-tab-active .bx-mms-item-hr {
+        display: block;
+    }
+
+
+    /*--- Profile View Submenu ---*/
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu-wrp.bx-menu-profile-view-submenu-wrp {
+        overflow-x: visible;
+    }
+    .bx-menu-main-bar-wrapper .bx-menu-main-submenu-wrp.bx-menu-profile-view-submenu-wrp .bx-menu-main-submenu-group {
+        display: block;
+    }
+
+
+    /*--- Toolbars ---*/
+    .bx-toolbar-item .bx-menu-toolbar .bx-menu-toolbar-item a.bx-btn {
+        width: auto;
+        height: 1.875rem;
+
+        line-height: 1.675rem;
+    }
+
+    .bx-toolbar-item .bx-menu-toolbar .bx-menu-toolbar-item a {
+        width: auto;
+        height: auto;
+
+        line-height: normal;
+    }
+
+    .bx-toolbar-item .bx-menu-toolbar .bx-menu-toolbar-item a .sys-icon,
+    .bx-toolbar-item .bx-menu-toolbar .bx-menu-toolbar-item a .sys-icon-a,
+    .bx-toolbar-item .bx-menu-toolbar .bx-menu-toolbar-item a img,
+    .bx-toolbar-item .bx-menu-toolbar .bx-menu-toolbar-item a span {
+        vertical-align: middle;
+    }
+
+    .bx-toolbar-item .bx-menu-toolbar .bx-menu-toolbar-item a .sys-icon,
+    .bx-toolbar-item .bx-menu-toolbar .bx-menu-toolbar-item a .sys-icon-a {
+        font-size: 1.0rem;
+    }
+
+    ul.bx-menu-toolbar li a .bx-menu-item-addon {
+        bottom: auto;
+    }
+
+
+    /*--- Toolbar Site ---*/
+    #bx-menu-toolbar-1-container .bx-menu-toolbar li {
+        padding: 0.625rem;
+    }
+
+    #bx-menu-toolbar-1-container ul.bx-menu-toolbar li a .sys-icon-a {
+        margin-top: 0;
+    }
+
+
+    /*--- Toolbar Member ---*/
+    #bx-menu-toolbar-2-container .bx-menu-toolbar li {
+        position: relative;
+        display: block;
+        float: none;
+
+        -webkit-flex: 0 0 auto; 
+        -ms-flex:  0 0 auto; 
+        flex:  0 0 auto;
+
+        padding: 0.5rem;
+    }
+
+    #bx-menu-toolbar-2-container .bx-menu-toolbar li.bx-menu-item-account {
+        -webkit-flex-grow: 1; 
+        flex-grow: 1;
+        white-space: nowrap;
+    }
+
+    #bx-menu-toolbar-2-container .bx-menu-toolbar li.bx-menu-item-account a {
+        width: auto;
+    }
+
+    #bx-menu-toolbar-2-container .bx-menu-toolbar li.bx-menu-item-account .bx-menu-toolbar-item-title {
+        display: none;
+    }
+
+    #bx-menu-toolbar-2-container .bx-menu-toolbar li .sys-icon-a {
+        margin-top: 0px;
+        vertical-align: middle;
+    }
+
+    #bx-menu-toolbar-2-container .bx-menu-toolbar .bx-menu-toolbar-item a .bx-def-thumb-size {
+        width: 2rem;
+        height: 2rem;
+        line-height: 1.9rem;
+        font-size: 1.5rem;
+    }
+
+    #bx-menu-toolbar-2-container .bx-menu-toolbar li a {
+        position: relative;
+        width: 2rem;
+        height: 2rem;
+
+        background-color: rgba(237, 241, 247, 1);
+        border-radius: 1.5rem;
+
+        font-size: 0.875rem;
+    }
+
+
+    /*--- Sliding Menu ---*/
+    .bx-sliding-menu, 
+    .bx-sliding-menu-main {
+        left: auto !important;
+        right: 0;
+        max-width: 31.25rem;
+    }
+
+    /*--- Search Popup ---*/
+    #bx-sliding-menu-search.bx-sliding-menu-main .bx-def-page-width {
+        width: 100%;
+    }
+
+    /*--- Notifications Popup ---*/
+    #bx_notifications_preview.bx-sliding-menu .bx-def-page-width {
+        width: 100%;
+    }
+
+    /*--- Account Popup ---*/
+    #bx-sliding-menu-account.bx-sliding-menu-main.bx-sliding-menu-fullheight {
+        height: calc(100% - 3.75rem);
+    }
+
+
+    /*--- Account Popup Submenu & Add Content Menu ---*/
+    .bx-menu-account-popup-profile-active,
+    .bx-menu-account-popup-profile-notifications {
+        text-align: left;
+    }
+    .bx-menu-account-popup-profile-notifications ul.bx-menu-floating-blocks-wide,
+    .bx-menu-object-sys_account_notifications li,
+    .bx-menu-object-sys_add_content li {
+        display: block;
+        width: 100%;
+        height: auto;
+    }
+    .bx-menu-object-sys_account_notifications li .bx-menu-floating-blocks-icon,
+    .bx-menu-object-sys_account_notifications li .bx-menu-floating-blocks-title,
+    .bx-menu-object-sys_add_content li .bx-menu-floating-blocks-icon,
+    .bx-menu-object-sys_add_content li .bx-menu-floating-blocks-title {
+        display: inline-block;
+        vertical-align: top;
+    }
+    .bx-menu-object-sys_account_notifications li a,
+    .bx-menu-object-sys_account_notifications li .bx-menu-floating-blocks-title,
+    .bx-menu-object-sys_add_content li .bx-menu-floating-blocks-title {
+        line-height: 1.5rem;
+    }
+    .bx-menu-object-sys_account_notifications li .bx-menu-floating-blocks-title,
+    .bx-menu-object-sys_add_content li .bx-menu-floating-blocks-title {
+        padding-left: 0.5rem;
+        padding-right: 0.5rem;
+    }
+    .bx-menu-object-sys_account_notifications li img,
+    .bx-menu-object-sys_account_notifications li .sys-icon,
+    .bx-menu-object-sys_account_notifications li .bx-menu-floating-blocks-icon img,
+    .bx-menu-object-sys_account_notifications li .bx-menu-floating-blocks-icon .sys-icon,
+    .bx-menu-object-sys_add_content li .bx-menu-floating-blocks-icon img,
+    .bx-menu-object-sys_add_content li .bx-menu-floating-blocks-icon .sys-icon {
+        height: 1.0rem;
+        width: 1.0rem;
+
+        margin: 0px;	
+    }
+    .bx-menu-object-sys_account_notifications li .sys-icon,
+    .bx-menu-object-sys_account_notifications li .bx-menu-floating-blocks-icon .sys-icon,
+    .bx-menu-object-sys_add_content li .bx-menu-floating-blocks-icon .sys-icon {
+        line-height: 1.0rem;
+        font-size: 1.0rem;
+    }
+    .bx-menu-object-sys_account_notifications li img,
+    .bx-menu-object-sys_account_notifications li .bx-menu-floating-blocks-icon img,
+    .bx-menu-object-sys_add_content li .bx-menu-floating-blocks-icon img {
+        background-pocition: center center;
+        background-repeat: none;
+        background-size: 60% 60%;
+    }
+    .bx-menu-object-sys_account_notifications li .bx-def-unit-alert-middle {
+        top: calc(50% - 9px);
+        right: 4px;
+    }
+    .bx-menu-object-sys_account_notifications li:hover {
+        opacity: 1.0;
+    }
+
+    /*--- Add Content Menu ---*/
+    #bx-sliding-menu-sys_add_content.bx-sliding-menu-main .bx-menu-object-sys_add_content {
+        display: block;
+        width: 100%;
+    }
+
+    /*--- Vertical (Vertical Custom) Menus (in Popup) ---*/
+    .bx-popup .bx-menu-ver li,
+    .bx-popup .bx-menu-custom-ver li,
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li {
+        border-radius: 0; 
+    }
+    .bx-popup .bx-menu-ver li,
+    .bx-popup .bx-menu-custom-ver li,
+    .bx-popup .bx-menu-ver li.bx-def-color-bg-hl-hover,
+    .bx-popup .bx-menu-custom-ver li.bx-def-color-bg-hl-hover,
+    .bx-menu-ver.bx-menu-vertical-more-less li.bx-def-color-bg-hl-hover,
+    .bx-menu-floating-blocks.bx-menu-object-sys_add_content li.bx-def-color-bg-hl-hover,
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li.bx-def-color-bg-hl-hover {
+        border: 1px solid transparent;
+    }
+    .bx-popup .bx-menu-ver li:hover,
+    .bx-popup .bx-menu-custom-ver li:hover,
+    .bx-popup .bx-menu-ver li.bx-def-color-bg-hl-hover:hover,
+    .bx-popup .bx-menu-custom-ver li.bx-def-color-bg-hl-hover:hover,
+    .bx-menu-ver.bx-menu-vertical-more-less li.bx-def-color-bg-hl-hover:hover,
+    .bx-menu-floating-blocks.bx-menu-object-sys_add_content li.bx-def-color-bg-hl-hover:hover,
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li.bx-def-color-bg-hl-hover:hover {
+        border-radius: 0.5rem;
+        border-color: transparent;
+    }
+    .bx-popup .bx-menu-ver li.bx-def-color-bg-active,
+    .bx-popup .bx-menu-custom-ver li.bx-def-color-bg-active {
+        border-radius: 0.5rem;
+        border-color: transparent;
+    }
+    .bx-menu-ver.bx-menu-vertical-more-less li.bx-def-color-bg-active,
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li.bx-def-color-bg-active {
+        border-color: transparent;
+        background-color: transparent;
+    }
+    .bx-popup .bx-menu-custom-ver:not(.bx-menu-submenu-more-auto) li > span,
+    .bx-popup .bx-menu-custom-ver:not(.bx-menu-submenu-more-auto) li > .bx-def-margin-thd-topbottom {
+        width: 100%;
+        margin: 0 !important;
+        padding: 0.25rem 0.5rem;
+    }
+    .bx-popup .bx-menu-custom-ver li a,
+    .bx-menu-floating-blocks.bx-menu-object-sys_add_content li > a, 
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li > a {
+        position: relative;
+        display: -webkit-flex;
+        display: flex;
+        -webkit-flex-flow: row nowrap;
+        flex-flow: row nowrap;
+        -webkit-align-items: center;
+        align-items: center;
+        -webkit-justify-content: flex-start;
+        justify-content: flex-start;
+        box-sizing: border-box;
+        width: 100%;
+        margin: 0;
+        padding: 0;
+    }
+    .bx-popup .bx-menu-custom.bx-menu-custom-ver.bx-menu-submenu-more-auto li.bx-menu-item,
+    .bx-menu-custom.bx-menu-custom-hor.bx-menu-more-auto .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-item {
+        padding: 0 0.5rem;
+        line-height: normal;
+    }
+    .bx-popup .bx-menu-custom.bx-menu-custom-ver.bx-menu-submenu-more-auto li > span,
+    .bx-popup .bx-menu-custom.bx-menu-custom-ver.bx-menu-submenu-more-auto li > .bx-def-margin-thd-topbottom {
+        margin: 0;
+        padding-right: 0.5rem;
+    }
+    .bx-popup .bx-menu-custom.bx-menu-custom-ver.bx-menu-submenu-more-auto li > span .bx-menu-addon {
+        padding-right: 0;
+    }
+    .bx-popup .bx-menu-custom.bx-menu-custom-ver.bx-menu-submenu-more-auto li.bx-menu-item a {
+        display: block;
+        line-height: 1.5rem;
+    }
+    .bx-menu-floating-blocks.bx-menu-object-sys_add_content li > a,
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li > a {
+        padding: 0.25rem 0.5rem;
+    }
+    .bx-popup .bx-menu-ver li.bx-def-color-bg-active a,
+    .bx-popup .bx-menu-custom-ver li.bx-def-color-bg-active a,
+    .bx-menu-ver.bx-menu-vertical-more-less li.bx-def-color-bg-active a,
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li.bx-def-color-bg-active a, 
+    .bx-popup .bx-menu-ver li.bx-def-color-bg-active .sys-icon,
+    .bx-popup .bx-menu-custom-ver li.bx-def-color-bg-active .sys-icon,
+    .bx-menu-ver.bx-menu-vertical-more-less li.bx-def-color-bg-active .sys-icon,
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li.bx-def-color-bg-active .sys-icon {
+        font-weight: 800;
+        color: rgba(20,80,100,1) !important;
+    }
+    .bx-popup .bx-menu-ver li img,
+    .bx-popup .bx-menu-ver li .sys-icon,
+    .bx-popup .bx-menu-custom-ver li img,
+    .bx-popup .bx-menu-custom-ver li .sys-icon {
+        display: inline-block;
+        vertical-align: middle;
+        width: 1.6rem;
+        height: 1.6rem;
+        margin-right: 0;
+        line-height: 1.6rem;
+        font-size: 1.0rem;
+    }
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li img,
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li .sys-icon {
+        width: 1.5rem;
+        height: 1.5rem;
+        line-height: 1.5rem;
+    }
+    .bx-popup .bx-menu-ver li a div {
+        padding: 0.25rem 0.5rem;
+        white-space: nowrap;
+    }
+    .bx-popup .bx-menu-ver li a > span,
+    .bx-popup .bx-menu-ver li a div > span,
+    .bx-popup .bx-menu-custom-ver li a > span {
+        white-space: nowrap;
+        vertical-align: baseline;
+    }
+    .bx-menu-floating-blocks-wide.bx-menu-object-sys_account_notifications li .bx-menu-item-addon {
+        top: 0.25rem;
+    }
+
+
+    /* --------------- profile stats menu */
+    .bx-menu-ver.bx-menu-vertical-more-less li .bx-menu-addon-cnt.bx-def-color-bg-hl {
+        background-color: transparent;
+    }
+
+
+    /*--- Custom Menu with More Auto ---*/
+    .bx-menu-main-submenu.bx-menu-more-auto .bx-menu-submenu-more-auto .bx-menu-item .bx-btn,
+    .bx-menu-custom.bx-menu-custom-hor.bx-menu-more-auto .bx-menu-submenu-more-auto .bx-menu-item a,
+    .bx-menu-custom.bx-menu-custom-hor.bx-menu-more-auto .bx-menu-submenu-more-auto .bx-menu-item .bx-btn {
+        font-weight: normal;
+    }
+
+    .bx-menu-main-submenu.bx-menu-more-auto .bx-menu-submenu-more-auto .bx-menu-item .bx-btn .sys-icon,
+    .bx-menu-custom.bx-menu-custom-hor.bx-menu-more-auto .bx-menu-submenu-more-auto .bx-menu-item .bx-btn .sys-icon {
+        color: @bx-color-icon-body;
+    }
+
+    /*--- Actions menu ---*/
+    .bx-base-general-entity-actions ul.bx-menu-hor li a > span,
+    .bx-base-general-entity-actions ul.bx-menu-custom-hor li a > span {
+        vertical-align: top;
+    }
+}
+
+:where(.bx-media-tablet, .bx-media-desktop) .@{bx-root-class-name} {
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main {
+        position: relative;
+        height: 2.125rem;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-sliding-menu-main-cnt {
+        position: absolute;
+        width: 100%;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site {
+        position: relative;
+        display: -webkit-flex;
+        display: flex;
+        -webkit-align-items: center;
+        align-items: center;
+        height: 100%;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li {
+        position: relative;
+
+        height: 2.125rem;
+        border: 1px solid transparent;
+        border-radius: 0.5rem;
+    }
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li:not(.bx-menu-item-more-auto) {
+        display: block;
+    }
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li > a {
+        position: relative;
+        display: -webkit-flex;
+        display: flex;
+        -webkit-align-items: center;
+        align-items: center;
+        height: 100%;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li .bx-menu-floating-blocks-line {
+        position: absolute;
+        display: none;
+
+        top: 0px;
+        left: 0px;
+        width: 100%;
+        height: 0.1875rem;
+
+        -webkit-border-radius: 0.25rem;
+        border-radius: 0.25rem;
+        background-color: rgba(255, 255, 255, 1.0);
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li .bx-sliding-smenu-submenu ul,
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li .bx-sliding-smenu-submenu ul li{
+        display: block; 
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site > li:hover,
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site > li.bx-menu-tab-active:hover {
+        border: 1px solid rgba(0, 0, 0, 0.05);
+        background-color: rgba(0, 0, 0, 0.05);
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site > li.bx-menu-tab-active {
+        border-color: transparent;
+        background-color: transparent;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site > li:hover .bx-sliding-smenu-submenu {
+        display: block;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li:hover .bx-menu-floating-blocks-arrow .sys-icon.active {
+        display: inline;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main .bx-menu-object-sys_site li:hover .bx-menu-floating-blocks-arrow .sys-icon.inactive {
+        display: none;
+    }
+
+    #bx-sliding-menu-account.bx-sliding-menu-main,
+    #bx-sliding-menu-sys_add_content.bx-sliding-menu-main {
+        width: auto;
+        max-width: none;
+    }
+}
+
+.bx-media-phone .@{bx-root-class-name} {
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li {
+       height: auto;
+    }
+
+    #bx-sliding-menu-sys_site.bx-sliding-smenu-main ul.bx-menu-object-sys_site li .bx-sliding-sms-cnt {
+       padding: 0px 0px 0px 1.7rem;
+    }
+
+    .bx-sliding-smenu-submenu {
+        position: relative;
+
+        top: 0px;
+        left: 0px;
+
+        background-color: transparent;
+        box-shadow: none;
+    }
+}

+ 68 - 0
modules/boonex/lucid/updates/14.0.0_14.0.1/source/install/config.php

@@ -0,0 +1,68 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Lucid Lucid template
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_TEMPLATE,
+    'name' => 'bx_lucid',
+    'title' => 'Lucid',
+    'note' => 'Design template',
+    'version' => '14.0.1',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/lucid/',
+    'home_uri' => 'lucid',
+
+    'db_prefix' => 'bx_lucid_',
+    'class_prefix' => 'BxLucid',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Boonex Lucid Template',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    	'clear_db_cache' => 1
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    	'clear_db_cache' => 1
+    ),
+    'enable' => array(
+        'execute_sql' => 1
+    ),
+    'disable' => array(
+        'execute_sql' => 1
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/market/updates/14.0.4_14.0.5/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Market',
+    'version_from' => '14.0.4',
+    'version_to' => '14.0.5',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/market/updates/update_14.0.4_14.0.5/',
+    'home_uri' => 'market_update_1404_1405',
+
+    'module_dir' => 'boonex/market/',
+    'module_uri' => 'market',
+
+    'db_prefix' => 'bx_market_',
+    'class_prefix' => 'BxMarket',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Market',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/market/updates/14.0.4_14.0.5/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxMarketUpdater extends BxDolStudioUpdater
+{
+    public function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 1457 - 0
modules/boonex/market/updates/14.0.4_14.0.5/source/classes/BxMarketModule.php

@@ -0,0 +1,1457 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Market Market
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+define('BX_MARKET_LICENSE_TYPE_SINGLE', 'single'); //--- one-time payment license
+define('BX_MARKET_LICENSE_TYPE_RECURRING', 'recurring'); //--- recurring payment license
+
+define('BX_MARKET_FILE_TYPE_VERSION', 'version');
+define('BX_MARKET_FILE_TYPE_UPDATE', 'update');
+
+/**
+ * Market module
+ */
+class BxMarketModule extends BxBaseModTextModule
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+
+        $CNF = &$this->_oConfig->CNF;
+        $this->_aSearchableNamesExcept = array_merge($this->_aSearchableNamesExcept, array(
+            $CNF['FIELD_NAME'],
+            $CNF['FIELD_DURATION_RECURRING'],
+            $CNF['FIELD_TRIAL_RECURRING'],
+            $CNF['FIELD_ALLOW_PURCHASE_TO'],
+            $CNF['FIELD_ALLOW_COMMENT_TO'],
+            $CNF['FIELD_ALLOW_VOTE_TO'],
+            $CNF['FIELD_NOTES_PURCHASED']
+        ));
+    }
+
+    public function actionUpdateImage($sFiledName, $iContentId, $sValue)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aData = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aData) || !is_array($aData))
+            return;
+
+        $mixedResult = parent::actionUpdateImage($sFiledName, $iContentId, $sValue);
+        if($mixedResult === false)
+            return;
+        
+        if(!empty($aData[$sFiledName]) && ($oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE'])) !== false)
+            $oStorage->deleteFile($aData[$sFiledName]);
+
+        echo $mixedResult;
+    }
+
+    public function actionPerform()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aContentInfo = $this->_getContentInfo();
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return echoJson(array('code' => 1));
+
+        $sAction = bx_process_input(bx_get('action'));
+        if(empty($CNF['MENU_ITEM_TO_METHOD'][$CNF['OBJECT_MENU_ACTIONS_VIEW_ENTRY']][$sAction])) 
+            return echoJson(array('code' => 2));
+
+        $sMethodCheck = $CNF['MENU_ITEM_TO_METHOD'][$CNF['OBJECT_MENU_ACTIONS_VIEW_ENTRY']][$sAction];
+        $sMethodPerform = '_perform' . bx_gen_method_name($sAction, array('-', '_'));
+        if(!method_exists($this, $sMethodCheck) || $this->$sMethodCheck($aContentInfo) !== CHECK_ACTION_RESULT_ALLOWED || !method_exists($this, $sMethodPerform))
+            return echoJson(array('code' => 3, 'msg' => _t('_sys_txt_access_denied')));
+
+        return $this->$sMethodPerform($aContentInfo);
+    }
+    
+    public function decodeDataAPI($aData, $aParams = [])
+    {
+        $CNF = $this->_oConfig->CNF;
+
+        $aResult = parent::decodeDataAPI($aData, $aParams);
+        $aResult = array_merge($aResult, [
+            'price_single' => $aData[$CNF['FIELD_PRICE_SINGLE']],
+            'price_recurring' => $aData[$CNF['FIELD_PRICE_RECURRING']],
+            'duration_recurring' => $aData[$CNF['FIELD_DURATION_RECURRING']],
+            'cover_raw' => $aData[$CNF['FIELD_COVER_RAW']],
+            'cover' => $this->serviceGetCover($aData[$CNF['FIELD_COVER']])
+        ]);
+        return $aResult;
+    }
+
+    public function actionCheckName()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sName = '';
+
+    	$sTitle = bx_process_input(bx_get('title'));
+    	if(empty($sTitle))
+    		return echoJson(array());
+
+        $iId = (int)bx_get('id');
+        if(!empty($iId)) {
+            $aEntry = $this->_oDb->getContentInfoById($iId); 
+            if(strcmp($sTitle, $aEntry[$CNF['FIELD_NAME']]) == 0) 
+                $sName = $sTitle;
+        }
+
+    	echoJson(array(
+    		'title' => $sTitle,
+    		'name' => !empty($sName) ? $sName : $this->_oConfig->getEntryName($sTitle, $iId)
+    	));
+    }
+
+    public function actionGetSubentries()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sTerm = bx_get('term');
+        $iLimit = (int)bx_get('limit');
+        if(empty($iLimit))
+            $iLimit = 10;
+
+        $aResult = array();
+        $aEntries = $this->_oDb->searchByAuthorTerm($this->_iProfileId, $sTerm, $iLimit);
+        foreach ($aEntries as $aEntry)
+            $aResult[] = array('value' => $aEntry[$CNF['FIELD_ID']], 'label' => $aEntry[$CNF['FIELD_TITLE']]);
+
+        // sort result
+        usort($aResult, function($r1, $r2) {
+            return strcmp($r1['label'], $r2['label']);
+        });
+
+        echoJson(array_slice($aResult, 0, $iLimit));
+    }
+
+    public function serviceGetSafeServices()
+    {
+        $a = parent::serviceGetSafeServices();
+        return array_merge($a, array (
+            'EntityDownload' => '',
+            'EntityAuthorEntities' => '',
+            'BlockLicenses' => '',
+        ));
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-other Other
+     * @subsubsection bx_market-get_searchable_fields get_searchable_fields
+     * 
+     * @code bx_srv('bx_market', 'get_searchable_fields', [...]); @endcode
+     * 
+     * Get a list of searchable fields. Is used in common search engine. 
+     *
+     * @return an array with a list of searchable fields.
+     * 
+     * @see BxMarketModule::serviceGetSearchableFields
+     */
+    /** 
+     * @ref bx_market-get_searchable_fields "get_searchable_fields"
+     */
+    public function serviceGetSearchableFields ($aInputsAdd = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aResult = parent::serviceGetSearchableFields($aInputsAdd);
+        unset($aResult[$CNF['FIELD_PRICE_SINGLE']], $aResult[$CNF['FIELD_TRIAL_RECURRING']]);
+
+        return $aResult;
+    }
+    
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-page_blocks Page Blocks
+     * @subsubsection bx_market-entity_create entity_create
+     * 
+     * @code bx_srv('bx_market', 'entity_create', [...]); @endcode
+     * 
+     * Get page block with product creation form or an error message if something wasn't configured correctly.
+     *
+     * @param $sDisplay form display name to use
+     * @return HTML string with block content to display on the site, all necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxMarketModule::serviceEntityCreate
+     */
+    /** 
+     * @ref bx_market-entity_create "entity_create"
+     */
+    public function serviceEntityCreate ($sDisplay = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(getParam($CNF['PARAM_NO_PAYMENTS']) != 'on') {
+            $oPayments = BxDolPayments::getInstance();
+            if(!$oPayments->isActive())
+                return MsgBox(_t('_bx_market_err_no_payments'));
+
+            if(!$oPayments->isAcceptingPayments($this->_iProfileId))
+                return MsgBox(_t('_bx_market_err_not_accept_payments', $oPayments->getDetailsUrl()));
+        }
+
+    	return parent::serviceEntityCreate($sDisplay);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-page_blocks Page Blocks
+     * @subsubsection bx_market-entity_download entity_download
+     * 
+     * @code bx_srv('bx_market', 'entity_download', [...]); @endcode
+     * 
+     * Get page block with a list of downloadable packages attached to the product. If something is wrong an error message is returned.
+     *
+     * @param $iContentId (optional) product's ID. If empty value is provided, an attempt to get it from GET/POST arrays will be performed.
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxMarketModule::serviceEntityDownload
+     */
+    /** 
+     * @ref bx_market-entity_download "entity_download"
+     */
+    public function serviceEntityDownload ($iContentId = 0)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	if(!$iContentId)
+            $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        if(!$iContentId)
+            return false;
+
+		$aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+		if(empty($aContentInfo) || !is_array($aContentInfo))
+            return MsgBox(_t('_sys_txt_error_occured'));
+
+    	if($this->checkAllowedDownload($aContentInfo) !== CHECK_ACTION_RESULT_ALLOWED)
+    		return MsgBox(_t('_bx_market_err_access_denied'));
+
+        $sJsObject = $this->_oConfig->getJsObject('entry');
+        $oMenu = new BxTemplMenu(array('template' => 'menu_vertical.html', 'menu_id'=> 'bx-market-downloads', 'menu_items' => array(
+            array('id' => 'bx-market-show-more', 'name' => 'bx-market-show-more', 'class' => '', 'link' => 'javascript:void(0)', 'target' => '_self', 'onclick' => 'javascript:' . $sJsObject . '.showMore();', 'title' => _t('_bx_market_menu_item_title_downloads_more')),
+        )));
+
+        return array(
+            'menu' => $oMenu,
+            'content' => $this->_oTemplate->entryAttachmentsByStorage($CNF['OBJECT_STORAGE_FILES'], $aContentInfo, array(
+                'filter_field' => ''
+            ))
+        );
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-page_blocks Page Blocks
+     * @subsubsection bx_market-entity_rating entity_rating
+     * 
+     * @code bx_srv('bx_market', 'entity_rating', [...]); @endcode
+     * 
+     * Get page block with Stars based product's rating.
+     *
+     * @param $iContentId (optional) product's ID. If empty value is provided, an attempt to get it from GET/POST arrays will be performed.
+     * @return HTML string with block content to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxMarketModule::serviceEntityRating
+     */
+    /** 
+     * @ref bx_market-entity_rating "entity_rating"
+     */
+    public function serviceEntityRating($iContentId = 0)
+    {
+    	return $this->_serviceTemplateFunc ('entryRating', $iContentId);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-page_blocks Page Blocks
+     * @subsubsection bx_market-entity_author_entities entity_author_entities
+     * 
+     * @code bx_srv('bx_market', 'entity_author_entities', [...]); @endcode
+     * 
+     * Get page block with a list of other products of the same author.
+     *
+     * @param $iContentId (optional) product's ID. If empty value is provided, an attempt to get it from GET/POST arrays will be performed.
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxMarketModule::serviceEntityAuthorEntities
+     */
+    /** 
+     * @ref bx_market-entity_author_entities "entity_author_entities"
+     */
+	public function serviceEntityAuthorEntities($iContentId = 0)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$aContentInfo = $this->_getContentInfo($iContentId);
+    	if($aContentInfo === false)
+    		return false;
+
+		$oProfile = BxDolProfile::getInstance($aContentInfo[$CNF['FIELD_AUTHOR']]);
+        if (!$oProfile)
+            $oProfile = BxDolProfileUndefined::getInstance();
+
+		$aBlock = $this->_serviceBrowse ('author', array('author' => $aContentInfo[$CNF['FIELD_AUTHOR']], 'except' => array($iContentId), 'per_page' => 2), BX_DB_PADDING_DEF, true);
+		$aBlock['title'] = _t('_bx_market_page_block_title_entry_author_entries', $oProfile->getDisplayName());
+
+    	return $aBlock;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-page_blocks Page Blocks
+     * @subsubsection bx_market-block_licenses block_licenses
+     * 
+     * @code bx_srv('bx_market', 'block_licenses', [...]); @endcode
+     * 
+     * Get page block with a list of licenses purchased by currently logged member.
+     *
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxMarketModule::serviceBlockLicenses
+     */
+    /** 
+     * @ref bx_market-block_licenses "block_licenses"
+     */
+    public function serviceBlockLicenses() 
+    {
+        return $this->_getBlockLicenses();
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-page_blocks Page Blocks
+     * @subsubsection bx_market-block_licenses_administration block_licenses_administration
+     * 
+     * @code bx_srv('bx_market', 'block_licenses_administration', [...]); @endcode
+     * 
+     * Get page block with a list of all licenses. It's needed for moderators/administrators.
+     *
+     * @return an array describing a block to display on the site or false if there is no enough input data. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxMarketModule::serviceBlockLicensesAdministration
+     */
+    /** 
+     * @ref bx_market-block_licenses "block_licenses"
+     */
+    public function serviceBlockLicensesAdministration() 
+    {
+        return $this->_getBlockLicenses('administration');
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-page_blocks Page Blocks
+     * @subsubsection bx_market-block_licenses_note block_licenses_note
+     * 
+     * @code bx_srv('bx_market', 'block_licenses_note', [...]); @endcode
+     * 
+     * Get page block with a notice for licenses usage.
+     *
+     * @return HTML string with block content to display on the site.
+     * 
+     * @see BxMarketModule::serviceBlockLicensesNote
+     */
+    /** 
+     * @ref bx_market-block_licenses_note "block_licenses_note"
+     */
+	public function serviceBlockLicensesNote()
+	{
+	    return MsgBox(_t('_bx_market_page_block_content_licenses_note'));
+	}
+
+	/**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-licenses Licenses
+     * @subsubsection bx_market-get_unused_licenses_num get_unused_licenses_num
+     * 
+     * @code bx_srv('bx_market', 'get_unused_licenses_num', [...]); @endcode
+     * 
+     * Get number of unused licenses for some profile. It can be used in menus with "alert" counters.
+     *
+     * @param $iProfileId profile to get unused licenses for, if omitted then currently logged in profile is used
+     * @return an integer value with a number of unused licenses.
+     * 
+     * @see BxMarketModule::serviceGetUnusedLicensesNum
+     */
+    /** 
+     * @ref bx_market-get_unused_licenses_num "get_unused_licenses_num"
+     */
+    public function serviceGetUnusedLicensesNum ($iProfileId = 0)
+    {
+        if(!$iProfileId)
+			$iProfileId = bx_get_logged_profile_id();
+
+        $aLicenses = $this->_oDb->getLicense(array('type' => 'new', 'profile_id' => (int)$iProfileId));
+        return !empty($aLicenses) && is_array($aLicenses) ? count($aLicenses) : 0;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-licenses Licenses
+     * @subsubsection bx_market-has_license has_license
+     * 
+     * @code bx_srv('bx_market', 'has_license', [...]); @endcode
+     * 
+     * Check whether a profile has a license for specified product. Domain on which the license is used can be attached to checking.
+     *
+     * @param $iProfileId profile which ownership will be checked.
+     * @param $iProductId product which the license(s) will be searched for.  
+     * @param $sDomain (optional) domain name on which the license is used.
+     * @return boolean value determining where the license is available or not.
+     * 
+     * @see BxMarketModule::serviceHasLicense
+     */
+    /** 
+     * @ref bx_market-has_license "has_license"
+     */
+	public function serviceHasLicense ($iProfileId, $iProductId, $sDomain = '')
+    {
+    	return $this->_oDb->hasLicense($iProfileId, $iProductId, $sDomain);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-licenses Licenses
+     * @subsubsection bx_market-get_license get_license
+     * 
+     * @code bx_srv('bx_market', 'has_license', [...]); @endcode
+     * 
+     * Get a list of licenses which meet requirements provided in the input array.
+     *
+     * @param $aParams array of requirements which will be used for searching.
+     * @return an array of licenses which meet requirements.
+     * 
+     * @see BxMarketModule::serviceGetLicense
+     */
+    /** 
+     * @ref bx_market-get_license "get_license"
+     */
+    public function serviceGetLicense ($aParams)
+    {
+    	return $this->_oDb->getLicense($aParams);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-licenses Licenses
+     * @subsubsection bx_market-register_license register_license
+     * 
+     * @code bx_srv('bx_market', 'register_license', [...]); @endcode
+     * 
+     * Register license(s) by provided details.
+     * 
+     * @param $iProfileId 
+     * @return boolean value determining where the license was registered or not.
+     * 
+     * @see BxMarketModule::serviceRegisterLicense
+     */
+    /** 
+     * @ref bx_market-register_license "register_license"
+     */
+    public function serviceRegisterLicense($iProfileId, $iProductId, $iCount, $sOrder, $sLicense, $sType = BX_PAYMENT_TYPE_SINGLE, $sDuration = '', $iTrial = 0)
+    {
+    	return $this->_oDb->registerLicense($iProfileId, $iProductId, $iCount, $sOrder, $sLicense, $sType, $sDuration, $iTrial);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-licenses Licenses
+     * @subsubsection bx_market-unregister_license unregister_license
+     * 
+     * @code bx_srv('bx_market', 'unregister_license', [...]); @endcode
+     * 
+     * Unregister license(s) by provided details.
+     * 
+     * @param $iProfileId 
+     * @return boolean value determining where the license was unregistered or not.
+     * 
+     * @see BxMarketModule::serviceRegisterLicense
+     */
+    /** 
+     * @ref bx_market-unregister_license "unregister_license"
+     */
+    public function serviceUnregisterLicense($iProfileId, $iProductId, $sOrder, $sLicense, $sType = BX_PAYMENT_TYPE_SINGLE)
+    {
+    	return $this->_oDb->unregisterLicense($iProfileId, $iProductId, $sOrder, $sLicense, $sType);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-licenses Licenses
+     * @subsubsection bx_market-update_license update_license
+     * 
+     * @code bx_srv('bx_market', 'update_license', [...]); @endcode
+     * 
+     * Update license(s) changing necessary fields. Updatable license(s) should meet requirements provided in the input array.
+     *
+     * @param $aSet an array of data to be saved  
+     * @param $aWhere an array of requirements which will be used for searching.
+     * @return an array of licenses which meet requirements.
+     * 
+     * @see BxMarketModule::serviceUpdateLicense
+     */
+    /** 
+     * @ref bx_market-update_license "update_license"
+     */
+    public function serviceUpdateLicense ($aSet, $aWhere)
+    {
+    	return $this->_oDb->updateLicense($aSet, $aWhere);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-browsing Browsing
+     * @subsubsection bx_market-get_entry_by get_entry_by
+     * 
+     * @code bx_srv('bx_market', 'get_entry_by', [...]); @endcode
+     * 
+     * Get entry (product) which meets requirements.
+     *
+     * @param $sType string with search type.  
+     * @param $mixedValue mixed value to be used for searching.
+     * @return an array describing a product which meets requirements.
+     * 
+     * @see BxMarketModule::serviceGetEntryBy
+     */
+    /** 
+     * @ref bx_market-get_entry_by "get_entry_by"
+     */
+    public function serviceGetEntryBy($sType, $mixedValue)
+    {
+    	return $this->_oDb->getContentInfoBy(array('type' => $sType, 'value' => $mixedValue));
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-browsing Browsing
+     * @subsubsection bx_market-get_entries_by get_entries_by
+     * 
+     * @code bx_srv('bx_market', 'get_entries_by', [...]); @endcode
+     * 
+     * Get an array of entries (products) which meet requirements.
+     * 
+     * @param $aParams an array of requirements which will be used for searching.
+     * @return an array of products which meets requirements.
+     * 
+     * @see BxMarketModule::serviceGetEntriesBy
+     */
+    /** 
+     * @ref bx_market-get_entries_by "get_entries_by"
+     */
+    public function serviceGetEntriesBy($aParams)
+    {
+    	return $this->_oDb->getContentInfoBy($aParams);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-browsing Browsing
+     * @subsubsection bx_market-get_products_names get_products_names
+     * 
+     * @code bx_srv('bx_market', 'get_products_names', [...]); @endcode
+     * 
+     * Get an array of products names.
+     * 
+     * @param $iVendorId return products of 1 vendor only
+     * @param $iLimit limit the result artay
+     * @return an array of products names
+     * 
+     * @see BxMarketModule::serviceGetProductsNames
+     */
+    /** 
+     * @ref bx_market-get_products_names "get_products_names"
+     */
+    public function serviceGetProductsNames($iVendorId, $iLimit = 1000)
+    {
+    	return $this->_oDb->getProductsNames($iVendorId, $iLimit);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-entry Entry
+     * @subsubsection bx_market-get_thumbnail get_thumbnail
+     * 
+     * @code bx_srv('bx_market', 'get_thumbnail', [...]); @endcode
+     * 
+     * Get an array of image URLs for the product's thumbnail. Returned images have different dimensions ('small' and 'big').
+     * 
+     * @param $iPhotoId photo ID which is attached to a product as thumbnail.
+     * @return an array of image URLs.
+     * 
+     * @see BxMarketModule::serviceGetThumbnail
+     */
+    /** 
+     * @ref bx_market-get_thumbnail "get_thumbnail"
+     */
+    public function serviceGetThumbnail($iPhotoId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+    	$oImagesTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_GALLERY']);
+
+    	return array(
+    		'small' => $oImagesTranscoder ? $oImagesTranscoder->getFileUrl($iPhotoId) : '',
+    		'big' => $oStorage ? $oStorage->getFileUrlById($iPhotoId) : ''
+    	);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-entry Entry
+     * @subsubsection bx_market-get_cover get_cover
+     * 
+     * @code bx_srv('bx_market', 'get_cover', [...]); @endcode
+     * 
+     * Get an array of image URLs for the product's cover. Returned images have different dimensions ('small', 'large' and 'big').
+     * 
+     * @param $iPhotoId photo ID which is attached to a product as cover.
+     * @return an array of image URLs.
+     * 
+     * @see BxMarketModule::serviceGetCover
+     */
+    /** 
+     * @ref bx_market-get_cover "get_cover"
+     */
+    public function serviceGetCover($iPhotoId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $oTiGallery = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_GALLERY']);
+        $oTiPage = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_COVER']);
+        $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+
+        return array(
+            'medium' => $oTiGallery ? $oTiGallery->getFileUrl($iPhotoId) : '',
+            'large' => $oTiPage ? $oTiPage->getFileUrl($iPhotoId) : '',
+            'big' => $oStorage ? $oStorage->getFileUrlById($iPhotoId) : ''
+        );
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-entry Entry
+     * @subsubsection bx_market-get_file get_file
+     * 
+     * @code bx_srv('bx_market', 'get_file', [...]); @endcode
+     * 
+     * Get an array with file's description.
+     * 
+     * @param $iFileId file ID which is attached to a product as package (version, update, etc).
+     * @return an array with file's description.
+     * 
+     * @see BxMarketModule::serviceGetFile
+     */
+    /** 
+     * @ref bx_market-get_file "get_file"
+     */
+    public function serviceGetFile($iFileId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$aFile = $this->_oDb->getFile(array(
+            'type' => 'file_id_ext', 
+            'file_id' => $iFileId
+    	));
+
+    	if(!empty($aFile) && is_array($aFile)) {
+            $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE_FILES']);
+            $aFile['file_url'] = $oStorage ? $oStorage->getFileUrlById($iFileId) : '';
+    	}
+
+    	return $aFile;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-entry Entry
+     * @subsubsection bx_market-get_files get_files
+     * 
+     * @code bx_srv('bx_market', 'get_files', [...]); @endcode
+     * 
+     * Get an array with files attached to the entry.
+     * 
+     * @param $iEntryId product ID.
+     * @param $sType file type ('version', 'update')
+     * @return an array with files.
+     * 
+     * @see BxMarketModule::serviceGetFiles
+     */
+    /** 
+     * @ref bx_market-get_files "get_files"
+     */
+    public function serviceGetFiles($iEntryId, $sType)
+    {
+    	return $this->_oDb->getFile(array(
+            'type' => 'content_id_and_type', 
+            'content_id' => $iEntryId,
+            'file_type' => $sType,
+            'ordered' => true
+    	));
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-entry Entry
+     * @subsubsection bx_market-get_updates get_updates
+     * 
+     * @code bx_srv('bx_market', 'get_updates', [...]); @endcode
+     * 
+     * Get an array with update(s)' description(s).
+     * 
+     * @param $iContentId product's ID.
+     * @param $sVersion (optional) if version is specified then it will be used for searching as 'Version From'.
+     * @return an array with update(s)' description(s).
+     * 
+     * @see BxMarketModule::serviceGetUpdates
+     */
+    /** 
+     * @ref bx_market-get_updates "get_updates"
+     */
+    public function serviceGetUpdates($iContentId, $sVersion = '')
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$aFile = $this->_oDb->getFile(array(
+            'type' => 'content_id_and_type', 
+            'content_id' => $iContentId, 
+            'file_type' => 'update',
+            'version' => $sVersion
+    	));
+
+    	if(!empty($aFile) && is_array($aFile)) {
+            $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE_FILES']);
+            $aFile['file_url'] = $oStorage ? $oStorage->getFileUrlById($aFile['file_id']) : '';
+    	}
+
+    	return $aFile;
+    }
+
+	/**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-entry Entry
+     * @subsubsection bx_market-get_screenshots get_screenshots
+     * 
+     * @code bx_srv('bx_market', 'get_screenshots', [...]); @endcode
+     * 
+     * Get an array with screenshots' descriptions.
+     * 
+     * @param $iItemId product's ID.
+     * @return an array with screenshots' descriptions.
+     * 
+     * @see BxMarketModule::serviceGetScreenshots
+     */
+    /** 
+     * @ref bx_market-get_screenshots "get_screenshots"
+     */
+    public function serviceGetScreenshots($iItemId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        if(!$iItemId)
+            return [];
+
+    	$aData = $this->_oDb->getContentInfoById($iItemId);
+
+    	$aPhotos = $this->_oDb->getPhoto(array('type' => 'content_id', 'content_id' => $aData[$CNF['FIELD_ID']], 'except' => array($aData[$CNF['FIELD_THUMB']], $aData[$CNF['FIELD_COVER']])));
+    	if(empty($aPhotos) || !is_array($aPhotos))
+            return [];
+
+        $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+        if(!$oStorage)
+            return [];
+
+    	$oImagesTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_SCREENSHOT']);
+
+    	$aResult = [];
+    	foreach($aPhotos as $aPhoto) {
+            $sUrlSm = '';
+            $sUrlBg = $oStorage->getFileUrlById($aPhoto['file_id']);
+
+            $aPhotoInfo = $oStorage->getFile($aPhoto['file_id']);
+            if(strpos($aPhotoInfo['mime_type'], 'svg') !== false)
+                $sUrlSm = $sUrlBg;
+            else
+                $sUrlSm = $oImagesTranscoder ? $oImagesTranscoder->getFileUrl($aPhoto['file_id']) : '';
+
+            $aResult[] = [
+                'id' => $aPhoto['file_id'],
+                'url_sm' => $sUrlSm,
+                'url_bg' => $sUrlBg
+            ];
+        }
+
+        return $aResult;
+    }
+
+	/**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-get_payment_data get_payment_data
+     * 
+     * @code bx_srv('bx_market', 'get_payment_data', [...]); @endcode
+     * 
+     * Get an array with module's description. Is needed for payments processing module.
+     * 
+     * @return an array with module's description.
+     * 
+     * @see BxMarketModule::serviceGetPaymentData
+     */
+    /** 
+     * @ref bx_market-get_payment_data "get_payment_data"
+     */
+    public function serviceGetPaymentData()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $oPermalink = BxDolPermalinks::getInstance();
+
+        $aResult = $this->_aModule;
+        $aResult['url_browse_order_common'] = bx_absolute_url($oPermalink->permalink($CNF['URL_LICENSES_COMMON'], array('filter' => '{order}')));
+        $aResult['url_browse_order_administration'] = bx_absolute_url($oPermalink->permalink($CNF['URL_LICENSES_ADMINISTRATION'], array('filter' => '{order}')));
+
+        return $aResult;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-get_cart_item get_cart_item
+     * 
+     * @code bx_srv('bx_market', 'get_cart_item', [...]); @endcode
+     * 
+     * Get an array with prodict's description. Is used in Shopping Cart in payments processing module.
+     * 
+     * @param $mixedItemId product's ID or Unique Name.
+     * @return an array with prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxMarketModule::serviceGetCartItem
+     */
+    /** 
+     * @ref bx_market-get_cart_item "get_cart_item"
+     */
+    public function serviceGetCartItem($mixedItemId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        if(!$mixedItemId)
+            return array();
+
+        if(is_numeric($mixedItemId))
+            $aItem = $this->_oDb->getContentInfoById((int)$mixedItemId);
+        else
+            $aItem = $this->_oDb->getContentInfoByName($mixedItemId);
+
+        if(empty($aItem) || !is_array($aItem))
+            return array();
+
+        return array (
+            'id' => $aItem[$CNF['FIELD_ID']],
+            'author_id' => $aItem[$CNF['FIELD_AUTHOR']],
+            'name' => $aItem[$CNF['FIELD_NAME']],
+            'title' => $aItem[$CNF['FIELD_TITLE']],
+            'description' => $aItem[$CNF['FIELD_TEXT']],
+            'url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=view-product&id=' . $aItem[$CNF['FIELD_ID']])),
+            'price_single' => $aItem[$CNF['FIELD_PRICE_SINGLE']],
+            'price_recurring' => $aItem[$CNF['FIELD_PRICE_RECURRING']],
+            'period_recurring' => 1,
+            'period_unit_recurring' => $aItem[$CNF['FIELD_DURATION_RECURRING']],
+            'trial_recurring' => $aItem[$CNF['FIELD_TRIAL_RECURRING']]
+        );
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-get_cart_items get_cart_items
+     * 
+     * @code bx_srv('bx_market', 'get_cart_items', [...]); @endcode
+     * 
+     * Get an array with prodicts' descriptions by seller. Is used in Manual Order Processing in payments processing module.
+     * 
+     * @param $iSellerId seller ID.
+     * @return an array with prodicts' descriptions. Empty array is returned if something is wrong or seller doesn't have any products.
+     * 
+     * @see BxMarketModule::serviceGetCartItems
+     */
+    /** 
+     * @ref bx_market-get_cart_items "get_cart_items"
+     */
+    public function serviceGetCartItems($iSellerId)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+        $iSellerId = (int)$iSellerId;
+        if(empty($iSellerId))
+            return array();
+
+        $aItems = $this->_oDb->getEntriesByAuthor($iSellerId);
+
+        $aResult = array();
+        foreach($aItems as $aItem)
+            $aResult[] = array(
+                'id' => $aItem[$CNF['FIELD_ID']],
+                'author_id' => $aItem[$CNF['FIELD_AUTHOR']],
+                'name' => $aItem[$CNF['FIELD_NAME']],
+                'title' => $aItem[$CNF['FIELD_TITLE']],
+                'description' => $aItem[$CNF['FIELD_TEXT']],
+                'url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=view-product&id=' . $aItem[$CNF['FIELD_ID']])),
+                'price_single' => $aItem[$CNF['FIELD_PRICE_SINGLE']],
+                'price_recurring' => $aItem[$CNF['FIELD_PRICE_RECURRING']],
+                'period_recurring' => 1,
+                'period_unit_recurring' => $aItem[$CNF['FIELD_DURATION_RECURRING']],
+                'trial_recurring' => $aItem[$CNF['FIELD_TRIAL_RECURRING']]
+            );
+
+        return $aResult;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-register_cart_item register_cart_item
+     * 
+     * @code bx_srv('bx_market', 'register_cart_item', [...]); @endcode
+     * 
+     * Register a processed single time payment inside the Market module. Is called with payment processing module after the payment was registered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return an array with purchased prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxMarketModule::serviceRegisterCartItem
+     */
+    /** 
+     * @ref bx_market-register_cart_item "register_cart_item"
+     */
+    public function serviceRegisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_MARKET_LICENSE_TYPE_SINGLE);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-register_subscription_item register_subscription_item
+     * 
+     * @code bx_srv('bx_market', 'register_subscription_item', [...]); @endcode
+     * 
+     * Register a processed subscription (recurring payment) inside the Market module. Is called with payment processing module after the subscription was registered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return an array with subscribed prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxMarketModule::serviceRegisterSubscriptionItem
+     */
+    /** 
+     * @ref bx_market-register_subscription_item "register_subscription_item"
+     */
+    public function serviceRegisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+		return $this->_serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_MARKET_LICENSE_TYPE_RECURRING);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-reregister_cart_item reregister_cart_item
+     * 
+     * @code bx_srv('bx_market', 'reregister_cart_item', [...]); @endcode
+     * 
+     * Reregister a single time payment inside the Market module. Is called with payment processing module after the payment was reregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemIdOld old item ID.
+     * @param $iItemIdNew new item ID.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return an array with purchased prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxMarketModule::serviceReregisterCartItem
+     */
+    /** 
+     * @ref bx_market-reregister_cart_item "reregister_cart_item"
+     */
+    public function serviceReregisterCartItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
+    {
+        return $this->_serviceReregisterItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder, BX_MARKET_LICENSE_TYPE_SINGLE);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-reregister_subscription_item reregister_subscription_item
+     * 
+     * @code bx_srv('bx_market', 'reregister_subscription_item', [...]); @endcode
+     * 
+     * Reregister a subscription (recurring payment) inside the Market module. Is called with payment processing module after the subscription was reregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemIdOld old item ID.
+     * @param $iItemIdNew new item ID.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return an array with subscribed prodict's description. Empty array is returned if something is wrong.
+     * 
+     * @see BxMarketModule::serviceReregisterSubscriptionItem
+     */
+    /** 
+     * @ref bx_market-reregister_subscription_item "reregister_subscription_item"
+     */
+    public function serviceReregisterSubscriptionItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder)
+    {
+		return $this->_serviceReregisterItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder, BX_MARKET_LICENSE_TYPE_RECURRING);
+    }
+
+	/**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-unregister_cart_item unregister_cart_item
+     * 
+     * @code bx_srv('bx_market', 'unregister_cart_item', [...]); @endcode
+     * 
+     * Unregister an earlier processed single time payment inside the Market module. Is called with payment processing module after the payment was unregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return boolean value determining where the payment was unregistered or not.
+     * 
+     * @see BxMarketModule::serviceUnregisterCartItem
+     */
+    /** 
+     * @ref bx_market-unregister_cart_item "unregister_cart_item"
+     */
+    public function serviceUnregisterCartItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+        return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_MARKET_LICENSE_TYPE_SINGLE);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-unregister_subscription_item unregister_subscription_item
+     * 
+     * @code bx_srv('bx_market', 'unregister_subscription_item', [...]); @endcode
+     * 
+     * Unregister an earlier processed subscription (recurring payment) inside the Market module. Is called with payment processing module after the subscription was unregistered there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @param $sLicense license number genereted with payment processing module for internal usage
+     * @return boolean value determining where the subscription was unregistered or not.
+     * 
+     * @see BxMarketModule::serviceUnregisterSubscriptionItem
+     */
+    /** 
+     * @ref bx_market-unregister_subscription_item "unregister_subscription_item"
+     */
+    public function serviceUnregisterSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense)
+    {
+    	return $this->_serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, BX_MARKET_LICENSE_TYPE_RECURRING); 
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_market Market
+     * @subsection bx_market-payments Payments
+     * @subsubsection bx_market-cancel_subscription_item cancel_subscription_item
+     * 
+     * @code bx_srv('bx_market', 'cancel_subscription_item', [...]); @endcode
+     * 
+     * Cancel an earlier processed subscription (recurring payment) inside the Market module. Is called with payment processing module after the subscription was canceled there.
+     * 
+     * @param $iClientId client ID.
+     * @param $iSellerId seller ID
+     * @param $iItemId item ID.
+     * @param $iItemCount the number of purchased items.
+     * @param $sOrder order number received from payment provider (PayPal, Stripe, etc)
+     * @return boolean value determining where the subscription was canceled or not.
+     * 
+     * @see BxMarketModule::serviceCancelSubscriptionItem
+     */
+    /** 
+     * @ref bx_market-cancel_subscription_item "cancel_subscription_item"
+     */
+	public function serviceCancelSubscriptionItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder)
+    {
+    	//TODO: Do something if it's necessary.
+    	return true;
+    }
+
+    protected function _serviceRegisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$aItem = $this->serviceGetCartItem($iItemId);
+        if(empty($aItem) || !is_array($aItem))
+            return array();
+
+        $aProduct = $this->_oDb->getContentInfoById($iItemId);
+        if(empty($aProduct) || !is_array($aProduct))
+            return array();
+
+        if(!BxDolPrivacy::getObjectInstance($CNF['OBJECT_PRIVACY_PURCHASE'])->check((int)$iItemId, $iClientId))
+            return array();
+
+        $iTrial = 0;
+        $sDuration = '';
+        $sAction = 'register';
+        if($sType == BX_MARKET_LICENSE_TYPE_RECURRING) {
+            $iTrial = $aProduct[$this->_oConfig->CNF['FIELD_TRIAL_RECURRING']];
+            $sDuration = $aProduct[$this->_oConfig->CNF['FIELD_DURATION_RECURRING']];
+            
+            if($this->_oDb->hasLicenseByOrder($iClientId, $iItemId, $sOrder))
+                $sAction = 'prolong';
+        }
+
+        if(!$this->_oDb->{$sAction . 'License'}($iClientId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType, $sDuration, $iTrial))
+            return array();
+
+        bx_alert($this->getName(), 'license_' . $sAction, 0, false, array(
+            'product_id' => $iItemId,
+            'profile_id' => $iClientId,
+            'order' => $sOrder,
+            'license' => $sLicense,
+            'type' => $sType,
+            'count' => $iItemCount,
+            'duration' => $sDuration,
+            'trial' => $iTrial
+        ));
+
+        $oClient = BxDolProfile::getInstanceMagic($iClientId);
+        $oSeller = BxDolProfile::getInstanceMagic($iSellerId);
+        $sSellerUrl = $oSeller->getUrl();
+        $sSellerName = $oSeller->getDisplayName();
+
+        $sNote = $aProduct[$CNF['FIELD_NOTES_PURCHASED']];
+        if(empty($sNote))
+            $sNote = _t('_bx_market_txt_purchased_note', $sSellerUrl, $sSellerName);
+
+        sendMailTemplate($CNF['ETEMPLATE_PURCHASED'], 0, $iClientId, array(
+            'client_name' => $oClient->getDisplayName(),
+            'product_name' => $aProduct[$CNF['FIELD_TITLE']],
+            'product_url' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=view-product&id=' . $aProduct[$CNF['FIELD_ID']])),
+            'vendor_url' => $sSellerUrl,
+            'vendor_name' => $sSellerName,
+            'license' => $sLicense,
+            'notes' => $sNote,
+        ));
+
+        return $aItem;
+    }
+
+    protected function _serviceReregisterItem($iClientId, $iSellerId, $iItemIdOld, $iItemIdNew, $sOrder, $sType)
+    {
+        $aItemNew = $this->serviceGetCartItem($iItemIdNew);
+        if(empty($aItemNew) || !is_array($aItemNew))
+            return array();
+
+        if(!$this->_oDb->hasLicenseByOrder($iClientId, $iItemIdOld, $sOrder))
+            return array();
+
+        if(!$this->_oDb->updateLicense(array('product_id' => $iItemIdNew), array('profile_id' => $iClientId, 'product_id' => $iItemIdOld, 'order' => $sOrder)))
+            return array();
+
+        bx_alert($this->getName(), 'license_reregister', 0, false, array(
+            'product_id_old' => $iItemIdOld,
+            'product_id_new' => $iItemIdNew,
+            'profile_id' => $iClientId,
+            'order' => $sOrder,
+            'type' => $sType
+        ));
+
+        return $aItemNew;
+    }
+
+    protected function _serviceUnregisterItem($iClientId, $iSellerId, $iItemId, $iItemCount, $sOrder, $sLicense, $sType)
+    {
+    	if(!$this->_oDb->unregisterLicense($iClientId, $iItemId, $sOrder, $sLicense, $sType))
+    		return false;
+
+        bx_alert($this->getName(), 'license_unregister', 0, false, array(
+            'product_id' => $iItemId,
+            'profile_id' => $iClientId,
+            'order' => $sOrder,
+            'license' => $sLicense,
+            'type' => $sType,
+            'count' => $iItemCount
+        ));
+
+    	return true;
+    }
+
+    public function getGhostTemplateVars($aFile, $iProfileId, $iContentId, $oStorage, $oImagesTranscoder)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	$sMethod = '';
+    	$sStorage = $oStorage->getObject();
+        switch($sStorage) {
+            case $CNF['OBJECT_STORAGE']:
+                $sMethod = 'getPhoto';
+                break;
+
+            case $CNF['OBJECT_STORAGE_FILES']:
+                $sMethod = 'getFile';
+                break;
+        }
+
+        $aFileInfo = $this->_oDb->$sMethod(['type' => 'file_id', 'file_id' => $aFile['id']]);
+        $bFileInfo = !empty($aFileInfo) && is_array($aFileInfo);
+
+        $bFileInfoTitle = $bFileInfo && isset($aFileInfo['title']);
+
+        $aTmplVars = [
+            'js_object' => $this->_oConfig->getJsObject('form'),
+
+            'file_title' => $bFileInfoTitle ? $aFileInfo['title'] : '',
+            'file_title_attr' => $bFileInfoTitle ? bx_html_attribute($aFileInfo['title']) : '',
+        ];
+
+        if($sStorage == $CNF['OBJECT_STORAGE'])
+            return $aTmplVars;
+
+        $bFileInfoVersion = $bFileInfo && isset($aFileInfo['version']);
+
+        $bFileInfoTypeVersion = !$bFileInfo || ($bFileInfo && isset($aFileInfo['type']) && $aFileInfo['type'] == BX_MARKET_FILE_TYPE_VERSION);
+        $bFileInfoTypeUpdate = $bFileInfo && isset($aFileInfo['type']) && $aFileInfo['type'] == BX_MARKET_FILE_TYPE_UPDATE;
+
+        $aVersions = $this->_oDb->$sMethod(array('type' => 'content_id', 'content_id' => $iContentId));
+
+        $aTmplVars = array_merge($aTmplVars, [
+            'file_version' => $bFileInfoVersion ? $aFileInfo['version'] : '',
+            'file_version_attr' => $bFileInfoVersion ? bx_html_attribute($aFileInfo['version']) : '',
+
+            'file_type_version_selected' => $bFileInfoTypeVersion ? ' selected="selected"' : '',
+            'file_type_update_selected' => $bFileInfoTypeUpdate ? ' selected="selected"' : '',
+
+            'file_type_version_elements' => !$bFileInfoTypeVersion ? ' style="display:none;"' : '',
+            'file_type_update_elements' => !$bFileInfoTypeUpdate ? ' style="display:none;"' : '',
+
+            'file_version_from_options' => $this->_oTemplate->getGhostTemplateFileOptions('version', $aFileInfo, $aVersions),
+            'file_version_to_options' => $this->_oTemplate->getGhostTemplateFileOptions('version_to', $aFileInfo, $aVersions)
+        ]);
+
+        return $aTmplVars;
+    }
+
+    public function checkAllowedDelete (&$aDataEntry, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if($aDataEntry[$CNF['FIELD_AUTHOR']] == $this->_iProfileId) {
+            $aLicenses = $this->_oDb->getLicense(array('type' => 'product_id', 'product_id' => $aDataEntry[$CNF['FIELD_ID']]));
+            if(is_array($aLicenses) && count($aLicenses) > 0)
+                return _t('_sys_txt_access_denied');
+        }
+
+        return parent::checkAllowedDelete($aDataEntry, $isPerformAction);
+    }
+
+    public function checkAllowedSetCover ()
+    {
+        $aCheck = checkActionModule($this->_iProfileId, 'set cover', $this->getName(), false);
+        if($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+            return $aCheck[CHECK_ACTION_MESSAGE];
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function checkAllowedSetSubentries ()
+    {
+        $aCheck = checkActionModule($this->_iProfileId, 'set subentries', $this->getName(), false);
+        if($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+            return $aCheck[CHECK_ACTION_MESSAGE];
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+	public function checkAllowedDownload($aDataEntry, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        // moderator and owner always have access
+        if ($aDataEntry[$CNF['FIELD_AUTHOR']] == $this->_iProfileId || $this->_isModerator($isPerformAction))
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        // check ACL
+        $aCheck = checkActionModule($this->_iProfileId, 'download entry', $this->getName(), $isPerformAction);
+        if($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+			return $aCheck[CHECK_ACTION_MESSAGE];
+
+        if((float)$aDataEntry[$CNF['FIELD_PRICE_SINGLE']] == 0 && (float)$aDataEntry[$CNF['FIELD_PRICE_RECURRING']] == 0)
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+		if(!$this->_oDb->hasLicense($this->_iProfileId, $aDataEntry[$CNF['FIELD_ID']]))
+            return CHECK_ACTION_RESULT_NOT_ALLOWED;
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function checkAllowedHide($aDataEntry, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if($aDataEntry[$CNF['FIELD_STATUS']] != 'active')
+            return CHECK_ACTION_RESULT_NOT_ALLOWED;
+
+        // moderator and owner always have access
+        if ($aDataEntry[$CNF['FIELD_AUTHOR']] == $this->_iProfileId || $this->_isModerator($isPerformAction))
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        return CHECK_ACTION_RESULT_NOT_ALLOWED;
+    }
+
+    public function checkAllowedUnhide($aDataEntry, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if($aDataEntry[$CNF['FIELD_STATUS']] == 'active')
+            return CHECK_ACTION_RESULT_NOT_ALLOWED;
+
+        // moderator and owner always have access
+        if ($aDataEntry[$CNF['FIELD_AUTHOR']] == $this->_iProfileId || $this->_isModerator($isPerformAction))
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        return CHECK_ACTION_RESULT_NOT_ALLOWED;
+    }
+
+    public function checkAllowedViewMoreMenu (&$aDataEntry, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $oMenu = BxTemplMenu::getObjectInstance($CNF['OBJECT_MENU_ACTIONS_VIEW_ENTRY_MORE']);
+        $oMenu->setContentId($aDataEntry[$CNF['FIELD_ID']]);
+        if(!$oMenu || !$oMenu->isVisible())
+            return CHECK_ACTION_RESULT_NOT_ALLOWED;
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    protected function _getBlockLicenses($sType = '') 
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sGrid = $this->_oConfig->getGridObject('licenses' . (!empty($sType) ? '_' . $sType : ''));
+        $oGrid = BxDolGrid::getObjectInstance($sGrid);
+        if(!$oGrid)
+            return '';
+
+        $this->_oDb->updateLicense(array('new' => 0), array('profile_id' => bx_get_logged_profile_id(), 'new' => 1));
+
+        $this->_oTemplate->addJs(array('licenses.js'));
+        return array(
+            'content' => $this->_oTemplate->getJsCode('licenses', array('sObjNameGrid' => $sGrid)) . $oGrid->getCode(),
+            'menu' => $CNF['OBJECT_MENU_LICENSES']
+        );
+    }
+
+    protected function _getContentInfo($iContentId = 0)
+    {
+    	if(!$iContentId)
+            $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        if(!$iContentId)
+            return false;
+
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(!$aContentInfo)
+            return false;
+
+		return $aContentInfo;
+    }
+
+    protected function _performHideProduct($aDataEntry) {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oDb->updateContentInfoBy(array($CNF['FIELD_STATUS'] => 'hidden'), array($CNF['FIELD_ID'] => $aDataEntry[$CNF['FIELD_ID']]))) 
+            return echoJson(array());
+
+        $this->checkAllowedHide($aDataEntry, true);
+        return echoJson(array('reload' => 1));
+    }
+
+    protected function _performUnhideProduct($aDataEntry) {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_oDb->updateContentInfoBy(array($CNF['FIELD_STATUS'] => 'active'), array($CNF['FIELD_ID'] => $aDataEntry[$CNF['FIELD_ID']]))) 
+            return echoJson(array());
+
+        $this->checkAllowedHide($aDataEntry, true);
+        return echoJson(array('reload' => 1));
+    }
+
+    protected function _getContentForTimelinePost($aEvent, $aContentInfo, $aBrowseParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aResult = parent::_getContentForTimelinePost($aEvent, $aContentInfo, $aBrowseParams);
+
+        if($this->_bIsApi) 
+            foreach(['PRICE_SINGLE', 'PRICE_RECURRING', 'DURATION_RECURRING', 'TRIAL_RECURRING'] as $sKey)
+                $aResult[$CNF['FIELD_' . $sKey]] = $aContentInfo[$CNF['FIELD_' . $sKey]];
+
+        return $aResult;
+    }
+}
+
+/** @} */

+ 532 - 0
modules/boonex/market/updates/14.0.4_14.0.5/source/classes/BxMarketTemplate.php

@@ -0,0 +1,532 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Market Market
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/*
+ * Module representation.
+ */
+class BxMarketTemplate extends BxBaseModTextTemplate
+{
+    public function __construct(&$oConfig, &$oDb)
+    {
+        $this->MODULE = 'bx_market';
+
+        parent::__construct($oConfig, $oDb);
+    }
+
+    public function entryRating($aData)
+    {
+        $CNF = &$this->getModule()->_oConfig->CNF;
+
+        $sVotes = '';
+        $oVotes = BxDolVote::getObjectInstance($CNF['OBJECT_VOTES'], $aData['id']);
+        if($oVotes) {
+            $sVotes = $oVotes->getElementBlock(array('show_counter' => true, 'show_legend' => true));
+            if(!empty($sVotes))
+                $sVotes = $this->parseHtmlByName('entry-rating.html', array(
+                'content' => $sVotes,
+            ));
+        }
+
+        return $sVotes; 
+    }
+
+    public function getTmplVarsText($aData)
+    {
+        $oModule = $this->getModule();
+        $CNF = &$oModule->_oConfig->CNF;
+
+        $aVars = parent::getTmplVarsText($aData);
+        
+        $bIcon = getParam($CNF['PARAM_USE_ICON']) == 'on';
+        if($this->_bIsApi) {
+            if($bIcon)
+                $aVars['icon'] = bx_api_get_image($CNF['OBJECT_STORAGE'], (int)$aData[$CNF['FIELD_THUMB']]);
+
+            return $aVars;
+        }
+
+        //--- Process RAW Cover
+        $bCoverRaw = !empty($CNF['FIELD_COVER_RAW']) && !empty($aData[$CNF['FIELD_COVER_RAW']]);
+
+        $aVars['bx_if:show_image_raw'] = [
+            'condition' => $bCoverRaw,
+            'content' => [
+                'image_raw' => $aData[$CNF['FIELD_COVER_RAW']]
+            ]
+        ];
+
+        //--- Process Cover
+        $bIsAllowEditCover = $oModule->checkAllowedEdit($aData) === CHECK_ACTION_RESULT_ALLOWED;
+        $bShowImage = $aVars['bx_if:show_image']['condition'];
+
+        $aVars['bx_if:show_entry_cover'] = [
+            'condition' => !$bCoverRaw && ($bShowImage || $bIsAllowEditCover),
+            'content' => array_merge([
+                'bx_if:show_image' => [
+                    'condition' => $bShowImage,
+                    'content' => [true]
+                ],
+                'bx_if:show_image_empty' => [
+                    'condition' => !$bShowImage,
+                    'content' => [true]
+                ]
+            ], $aVars['bx_if:show_image']['content'])
+        ];
+
+        //--- Process Icon (Thumb)
+        $bIsAllowEditIcon = $bIsAllowEditCover;
+
+        $sIcon = '';
+        if($bIcon) {
+            $mixedIcon = $oModule->getEntryImageData($aData, 'FIELD_THUMB', ['OBJECT_IMAGES_TRANSCODER_THUMB', 'OBJECT_IMAGES_TRANSCODER_GALLERY']);
+            if($mixedIcon !== false) {
+                if(!empty($mixedIcon['object']))
+                    $o = BxDolStorage::getObjectInstance($mixedIcon['object']);
+                else if(!empty($mixedIcon['transcoder']))
+                    $o = BxDolTranscoder::getObjectInstance($mixedIcon['transcoder']);
+
+                if($o)
+                    $sIcon = $o->getFileUrlById($mixedIcon['id']);
+            }
+        }
+
+        $sIconUniqId = genRndPwd(8, false);
+        $sIconTweak = '';
+        if($bIsAllowEditIcon)
+            $sIconTweak = $this->_prepareImage($aData, $sIconUniqId, $CNF['OBJECT_UPLOADERS'], $CNF['OBJECT_STORAGE'], $CNF['FIELD_THUMB'], false);
+
+        $aVars = array_merge($aVars, [
+            'bx_if:show_entry_icon' => [
+                'condition' => $bIcon,
+                'content' => [
+                    'bx_if:show_icon' => [
+                        'condition' => !empty($sIcon),
+                        'content' => [true]
+                    ],
+                    'bx_if:show_icon_empty' => [
+                        'condition' => empty($sIcon),
+                        'content' => [true]
+                    ],
+                    'unique_id' => $sIconUniqId,
+                    'entry_icon' => $sIcon,
+                    'icon' => $CNF['ICON'],
+                    'icon_tweak' => $sIconTweak
+                ]
+            ]
+        ]);
+
+        //--- Process Screenshots
+        $sScreenshots = $this->getScreenshots($aData);
+        $bScreenshots = !empty($sScreenshots);
+        if($bScreenshots) {
+            $this->addCss(BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'flickity/|flickity.css');
+            $this->addJs('flickity/flickity.pkgd.min.js');
+        }
+
+        $aVars['bx_if:show_screenshots'] = array(
+            'condition' => $bScreenshots,
+            'content' => array(
+                'screenshots' => $sScreenshots
+            )
+        );
+        
+        return $aVars;
+    }
+
+    public function getScreenshots($aData)
+    {
+    	$oModule = $this->getModule();
+
+    	$CNF = &$oModule->_oConfig->CNF;
+    	$aPhotos = $oModule->serviceGetScreenshots($aData[$CNF['FIELD_ID']]);
+        
+        if (bx_is_api()){
+            return $aPhotos;
+        }
+        
+        if(empty($aPhotos) || !is_array($aPhotos))
+            return '';
+
+        return $this->parseHtmlByName('entry-screenshots.html', array(
+            'bx_repeat:items' => $aPhotos
+    	));
+    }
+    
+    public function getAuthorAddon ($aData, $oProfile)
+    {
+        $aAccount = $oProfile->getAccountObject()->getInfo();
+
+        $sContent = $this->parseHtmlByName('entry-author.html', array(
+    		'joined' => bx_time_js($aAccount['added']),
+	    	'last_active' => bx_time_js($aAccount['logged']),
+	    	'installs' => '?',
+    	));
+
+    	return $sContent . parent::getAuthorAddon ($aData, $oProfile);
+    }
+
+    public function getGhostTemplateFileOptions($sField, $aFile, $aVersions)
+    {
+    	$bField = !empty($aFile) && is_array($aFile) && isset($aFile[$sField]);
+    	
+    	$aTmplVarsVersions = array();
+    	foreach ($aVersions as $aVersion) {
+    		if($aVersion['type'] != 'version' || empty($aVersion['version']))
+    			continue;
+
+			$aTmplVarsVersions[] = array(
+				'value' => $aVersion['version'],
+				'title' =>  $aVersion['version'],
+				'bx_if:selected' => array(
+					'condition' => $bField && $aFile[$sField] == $aVersion['version'],
+					'content' => array()
+				)
+			);
+    	}
+
+    	return $this->parseHtmlByName('form_ghost_template_file_options.html', array(
+    		'bx_repeat:versions' => $aTmplVarsVersions
+    	));    	
+    }
+
+    protected function getUnit ($aData, $aParams = array())
+    {
+        $bTmplVarsSectionAuthor = true;
+        $bTmplVarsSectionPricing = false;
+        $bTmplVarsSectionVoting = true;
+
+        $oModule = $this->getModule();
+    	$CNF = &$oModule->_oConfig->CNF;
+
+    	$aUnit = parent::getUnit($aData, $aParams);
+        $oPayment = BxDolPayments::getInstance();
+        $oPermalinks = BxDolPermalinks::getInstance();
+
+        $iAuthorId = (int)$aData[$CNF['FIELD_AUTHOR']];
+        list($sAuthorName, $sAuthorUrl, $sAuthorIcon, $sAuthorUnit, $sAuthorUnitShort) = $oModule->getUserInfo($iAuthorId);
+        $sAuthorCurrencySign = $oPayment->getCurrencySign($iAuthorId);
+
+        $aTmplVarsSectionAuthor = array();
+        if($bTmplVarsSectionAuthor) {
+            //--- Author Info
+            $bAuthorIcon = !empty($sAuthorIcon);
+            $sAuthorName = $this->parseHtmlByName('author_link.html', array(
+                'href' => $sAuthorUrl,
+                'title' => bx_html_attribute($sAuthorName),
+                'content' => $sAuthorName,
+                'bx_if:show_account' => array(
+                    'condition' => false,
+                    'content' => array()
+                )
+            ));
+
+            //--- Actions
+            $sActions = '';
+            $oActions = BxDolMenu::getObjectInstance($CNF['OBJECT_MENU_ACTIONS_SNIPPET']);
+            if($oActions) {
+                $oActions->setContentId($aData[$CNF['FIELD_ID']]);
+                $sActions = $oActions->getCode();
+            }
+
+            $sDate = $this->parseHtmlByName('snippet-date.html', array(
+                'date' => bx_time_js($aData[$CNF['FIELD_ADDED']])
+            ));
+
+            $aTmplVarsSectionAuthor = array(
+                'actions' => $sActions,
+                'author_unit' => $sAuthorUnitShort,
+                'author_width_date' => _t('_bx_market_txt_author_added_product', $sAuthorName, $sDate),
+            );
+        }
+
+        //--- Main Info
+        $sUrl = bx_absolute_url($oPermalinks->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $aData[$CNF['FIELD_ID']]));
+        $sLinkMore = ' <a title="' . bx_html_attribute(_t('_sys_read_more', $aData[$CNF['FIELD_TITLE']])) . '" href="' . $sUrl . '"><i class="sys-icon ellipsis-h"></i></a>';
+        $sSummary = strmaxtextlen($aData[$CNF['FIELD_TEXT']], (int)getParam($CNF['PARAM_CHARS_SUMMARY']), $sLinkMore);
+        $sSummaryPlain = BxTemplFunctions::getInstance()->getStringWithLimitedLength(strip_tags($sSummary), (int)getParam($CNF['PARAM_CHARS_SUMMARY_PLAIN']));
+        
+
+        //--- Icon Info
+        $sIconUrl = '';
+        $bIcon = getParam($CNF['PARAM_USE_ICON']) == 'on';
+        if($bIcon && !empty($CNF['FIELD_THUMB']) && $aData[$CNF['FIELD_THUMB']]) {
+            $iThumb = (int)$aData[$CNF['FIELD_THUMB']];
+
+            $oStorage = BxDolStorage::getObjectInstance($CNF['OBJECT_STORAGE']);
+            $aThumb = $oStorage->getFile($iThumb);
+
+            if(!empty($aThumb) && is_array($aThumb)) {
+                if(strpos($aThumb['mime_type'], 'svg') !== false)
+                    $sIconUrl = $oStorage->getFileUrlById($iThumb);
+
+                if(empty($sIconUrl) && ($oImagesTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_ICON'])) !== false)
+                    $sIconUrl = $oImagesTranscoder->getFileUrl($iThumb);
+
+                if(empty($sIconUrl) && ($oImagesTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_THUMB'])) !== false)
+                    $sIconUrl = $oImagesTranscoder->getFileUrl($iThumb);
+            }
+        }
+
+        //--- Cover Info
+        $bCoverRaw = !empty($CNF['FIELD_COVER_RAW']) && !empty($aData[$CNF['FIELD_COVER_RAW']]);
+
+        $sCoverUrl = '';
+        if(!$bCoverRaw && !empty($CNF['FIELD_COVER']) && $aData[$CNF['FIELD_COVER']]) {
+            $oImagesTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_GALLERY']);
+            if($oImagesTranscoder)
+                $sCoverUrl = $oImagesTranscoder->getFileUrl($aData[$CNF['FIELD_COVER']]);
+
+            if(empty($sCoverUrl)) {
+                $oImagesTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_COVER']);
+                if($oImagesTranscoder)
+                    $sCoverUrl = $oImagesTranscoder->getFileUrl($aData[$CNF['FIELD_COVER']]);
+            }
+        }
+
+        $aTmplVarsSectionPricing = array();
+        if($bTmplVarsSectionPricing) {
+            //--- Price Single
+            $bTmplVarsSingle = (float)$aData[$CNF['FIELD_PRICE_SINGLE']] != 0;
+            $aTmplVarsSingle = array();
+            if($bTmplVarsSingle) {
+                $aJsSingle = $oPayment->getAddToCartJs($iAuthorId, $this->_oConfig->getName(), $aData[$CNF['FIELD_ID']], 1, true);
+                if(!empty($aJsSingle) && is_array($aJsSingle)) {
+                    list($sJsCode, $sSingleOnclick) = $aJsSingle;
+
+                    $aTmplVarsSingle = array(
+                        'price_single_onclick' => $sSingleOnclick,
+                        'price_single' => _t('_bx_market_txt_price_single', $sAuthorCurrencySign, $aData[$CNF['FIELD_PRICE_SINGLE']])
+                    );
+                }
+                else 
+                    $bTmplVarsSingle = false;
+            }
+
+            //--- Price Recurring
+            $bTmplVarsRecurring = !$oPayment->isCreditsOnly() && (float)$aData[$CNF['FIELD_PRICE_RECURRING']] != 0;
+            $aTmplVarsRecurring = array();
+            if($bTmplVarsRecurring) {
+                $aJsRecurring = $oPayment->getSubscribeJs($iAuthorId, '', $this->_oConfig->getName(), $aData[$CNF['FIELD_ID']], 1);
+                if(!empty($aJsRecurring) && is_array($aJsRecurring)) {
+                    list($sJsCode, $sRecurringOnclick) = $aJsRecurring;
+
+                    $aTmplVarsRecurring = array(
+                        'price_recurring_onclick' => $sRecurringOnclick,
+                        'price_recurring' => _t('_bx_market_txt_price_recurring', $sAuthorCurrencySign, $aData[$CNF['FIELD_PRICE_RECURRING']], _t($CNF['T']['txt_per_' . $aData[$CNF['FIELD_DURATION_RECURRING']] . '_short']))
+                    );
+                }
+                else 
+                    $bTmplVarsRecurring = false;
+            }
+
+            $aTmplVarsSectionPricing = array(
+                'bx_if:show_single' => array(
+                    'condition' => $bTmplVarsSingle,
+                    'content' => $aTmplVarsSingle
+                ),
+                'bx_if:show_recurring' => array(
+                    'condition' => $bTmplVarsRecurring,
+                    'content' => $aTmplVarsRecurring
+                ),
+                'bx_if:show_free' => array(
+                    'condition' => !$bTmplVarsSingle && !$bTmplVarsRecurring,
+                    'content' => array(
+                        'price_free_href' => $oPermalinks->permalink('page.php?i=' . $CNF['URI_DOWNLOAD_ENTRY'] . '&id=' . $aData[$CNF['FIELD_ID']])
+                    )
+                )
+            );
+        }
+
+        $aTmplVarsSectionVoting = array();
+        if($bTmplVarsSectionVoting) {
+            $sVoting = '';
+            $oVoting = BxDolVote::getObjectInstance($CNF['OBJECT_VOTES'], $aData[$CNF['FIELD_ID']]);
+            if($oVoting)
+                $sVoting = $oVoting->getElementBlock(array('show_counter' => false));
+
+            $sPricing = "";
+            if((float)$aData[$CNF['FIELD_PRICE_RECURRING']] != 0) {
+                $sDuration = 'txt_per_' . $aData[$CNF['FIELD_DURATION_RECURRING']] . '_short';
+                $sDuration = _t(!empty($CNF['T'][$sDuration]) ? $CNF['T'][$sDuration] : '_bx_market_txt_per_' . $aData[$CNF['FIELD_DURATION_RECURRING']] . '_short');
+
+                $sPricing = _t('_bx_market_txt_price_recurring_short', $sAuthorCurrencySign, $aData[$CNF['FIELD_PRICE_RECURRING']], $sDuration);
+            }
+
+            if(empty($sPricing) && (float)$aData[$CNF['FIELD_PRICE_SINGLE']] != 0)
+                $sPricing = _t('_bx_market_txt_price_single_short', $sAuthorCurrencySign, $aData[$CNF['FIELD_PRICE_SINGLE']]);
+
+            if(empty($sPricing))
+                 $sPricing = _t('_bx_market_txt_price_free');
+
+            $aTmplVarsSectionVoting = array(
+                'voting' => $sVoting,
+                'pricing' => $sPricing
+            );
+        }
+
+    	$aUnit = array_merge($aUnit, array(
+    	    'bx_if:show_section_author' => array(
+    	        'condition' => $bTmplVarsSectionAuthor,
+    	        'content' => $aTmplVarsSectionAuthor
+    	    ),
+            'bx_if:show_icon' => array (
+                'condition' => $sIconUrl,
+                'content' => array(
+                    'content_url' => $sUrl,
+                    'icon_url' => $sIconUrl,
+                ),
+            ),
+            'bx_if:show_icon_empty' => array (
+                'condition' => $bIcon && !$sIconUrl,
+                'content' => array(
+                    'content_url' => $sUrl,
+                    'icon' => $CNF['ICON']
+                ),
+            ),
+            'bx_if:show_cover_raw' => array (
+                'condition' => $bCoverRaw,
+                'content' => array (
+                    'summary_attr' => bx_html_attribute($sSummaryPlain),
+                    'content_url' => $sUrl,
+                    'cover_raw' => $aData[$CNF['FIELD_COVER_RAW']],
+                ),
+            ),
+            'bx_if:show_cover' => array (
+                'condition' => $sCoverUrl,
+                'content' => array (
+                    'summary_attr' => bx_html_attribute($sSummaryPlain),
+                    'content_url' => $sUrl,
+                    'cover_url' => $sCoverUrl,
+                    'strecher' => str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ', 40),
+                ),
+            ),
+            'bx_if:show_cover_empty' => array (
+                'condition' => !$bCoverRaw && !$sCoverUrl,
+                'content' => array (
+                    'summary_plain' => $sSummaryPlain,
+                    'strecher' => mb_strlen($sSummaryPlain) > 240 ? '' : str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ', round((240 - mb_strlen($sSummaryPlain)) / 6)),
+                ),
+            ),
+            'author_name' => $sAuthorName,
+            'content_url' => $sUrl,
+    		'bx_if:show_section_pricing' => array(
+                'condition' => $bTmplVarsSectionPricing,
+                'content' => $aTmplVarsSectionPricing
+            ),
+            'bx_if:show_section_voting' => array(
+            	'condition' => $bTmplVarsSectionVoting,
+                'content' => $aTmplVarsSectionVoting
+            )
+    	));
+
+    	return $aUnit;
+    }
+
+    protected function getAttachments($sStorage, $aData, $aParams = array())
+    {
+        $mixedAttachments = parent::getAttachments($sStorage, $aData, $aParams);
+        if(empty($mixedAttachments) || !is_array($mixedAttachments))
+            return $mixedAttachments;
+
+        $aAttachments = [];
+        foreach($mixedAttachments as $aAttachment)
+            $aAttachments[(int)$aAttachment['id']] = $aAttachment;       
+
+        $aFiles = $this->_oDb->getFile(['type' => 'content_id_key_file_id', 'content_id' => $aData['id']]);
+
+        $aResults = [];
+        $sCaptionType = "";
+        foreach($aFiles as $iAttachmentId => $aFile) {
+            $aAttachments[$iAttachmentId]['class'] = (int)$aData['package'] == $iAttachmentId ? ' bx-market-attachment-main' : '';
+            $aAttachments[$iAttachmentId]['bx_if:main'] = [
+                'condition' => (int)$aData['package'] == $iAttachmentId,
+                'content' => []
+            ];
+
+            $bSeparatorNeeded = false;
+            if ($aFiles[$iAttachmentId]['type'] != $sCaptionType){
+                $bSeparatorNeeded = true;
+                $sCaptionType = $aFiles[$iAttachmentId]['type'];
+            }
+
+            if ((int)$aData['package'] == $iAttachmentId){
+                $bSeparatorNeeded = true;
+                $sCaptionType = 'latest';
+            }
+
+            $bAttachment = !empty($aFiles[$iAttachmentId]);
+            $aAttachments[$iAttachmentId]['bx_if:not_image']['content'] = array_merge($aAttachments[$iAttachmentId]['bx_if:not_image']['content'], [
+                'file_type' => $bAttachment ? _t('_bx_market_form_entry_input_files_type_' . $aFiles[$iAttachmentId]['type']) : '',
+                'file_name_attr' => bx_html_attribute($aAttachments[$iAttachmentId]['file_name']),
+                'bx_if:show_version' => [
+                    'condition' => $bAttachment && $aFiles[$iAttachmentId]['type'] == BX_MARKET_FILE_TYPE_VERSION,
+                    'content' => [
+                        'file_version' => $bAttachment ? $aFiles[$iAttachmentId]['version'] : ''
+                    ]
+                ],
+                'bx_if:show_update' => [
+                    'condition' => $bAttachment && $aFiles[$iAttachmentId]['type'] == BX_MARKET_FILE_TYPE_UPDATE,
+                    'content' => [
+                        'file_version_from_to' => $bAttachment ? _t('_bx_market_form_entry_input_files_version_from_x_to_y', $aFiles[$iAttachmentId]['version'], $aFiles[$iAttachmentId]['version_to']) : ''
+                    ]
+                ]
+            ]);
+            $aAttachments[$iAttachmentId]['bx_if:show_separator'] = [
+                'condition' => $bSeparatorNeeded, 
+                'content' => [
+                    'type' => _t('_bx_market_form_entry_input_files_version_caption_' . $sCaptionType)
+                ]
+            ];
+
+            $aFiles[$iAttachmentId] = $aAttachments[$iAttachmentId];
+            $aResults[$iAttachmentId] = $aAttachments[$iAttachmentId];
+        }
+
+        return array_intersect_key($aFiles, $aResults);
+    }
+
+    protected function getAttachmentsImagesTranscoders ($sStorage = '')
+    {
+        $CNF = &$this->getModule()->_oConfig->CNF;
+
+        $oTranscoder = BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_PREVIEW']);
+        $oTranscoderPreview = isset($CNF['OBJECT_IMAGES_TRANSCODER_PICTURE']) && $CNF['OBJECT_IMAGES_TRANSCODER_PICTURE'] ? BxDolTranscoderImage::getObjectInstance($CNF['OBJECT_IMAGES_TRANSCODER_PICTURE']) : null;
+
+        return array($oTranscoder, $oTranscoderPreview);
+    }
+
+    protected function _getHeaderImageParams()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return array_merge(parent::_getHeaderImageParams(), [
+            'field' => isset($CNF['FIELD_COVER']) ? $CNF['FIELD_COVER'] : false,
+            'field_position' => isset($CNF['FIELD_COVER_POSITION']) ? $CNF['FIELD_COVER_POSITION'] : false,
+        ]);
+    }
+
+    protected function _getHeaderImage($aData)
+    {
+        return $this->getModule()->getEntryImageData($aData, 'FIELD_COVER');
+    }
+    
+    function _prepareImage($aData, $sUniqId, $sUploader, $sStorage, $sField, $bAllowTweak, $bAllowMultiple = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(in_array($sField, [$CNF['FIELD_THUMB'], $CNF['FIELD_COVER']]))
+            $bAllowMultiple = true;
+
+        return parent::_prepareImage($aData, $sUniqId, $sUploader, $sStorage, $sField, $bAllowTweak, $bAllowMultiple);
+    }
+}
+
+/** @} */

+ 140 - 0
modules/boonex/market/updates/14.0.4_14.0.5/source/install/config.php

@@ -0,0 +1,140 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Market Market
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_market',
+    'title' => 'Market',
+    'note' => 'Market module.',
+    'version' => '14.0.5',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/market/',
+    'home_uri' => 'market',
+
+    'db_prefix' => 'bx_market_',
+    'class_prefix' => 'BxMarket',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Market',
+
+	/**
+     * List of page triggers.
+     */
+    'page_triggers' => array (
+    	'trigger_page_profile_view_entry',
+    ),
+
+    /**
+     * Menu triggers.
+     */
+    'menu_triggers' => array(
+    	'trigger_profile_view_submenu',
+        'trigger_group_view_submenu'
+    ),
+
+	/**
+     * Storage objects to automatically delete files from upon module uninstallation.
+     * Note. Don't add storage objects used in transcoder objects.
+     */
+    'storages' => array(
+    	'bx_market_files',
+        'bx_market_photos'
+    ),
+
+    /**
+     * Transcoders.
+     */
+    'transcoders' => array(
+        'bx_market_icon',
+        'bx_market_thumb',
+        'bx_market_cover',
+        'bx_market_screenshot',
+		'bx_market_preview',
+        'bx_market_gallery',
+    ),
+
+    /**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_market',
+    	'bx_market_cmts',
+    ),
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+    	'process_esearches' => 1,
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable_success' => array(
+    	'process_page_triggers' => 1,
+    	'process_menu_triggers' => 1,
+    	'process_esearches' => 1,
+    	'register_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+    	'update_relations' => 1,
+    	'unregister_transcoders' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+    	'register_transcoders' => 1,
+    	'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Connections Section
+     */
+    'relations' => array(
+    	'bx_timeline',
+    	'bx_notifications'
+    ),
+
+);
+
+/** @} */

+ 51 - 0
modules/boonex/notifications/updates/14.0.4_14.0.5/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Notifications',
+    'version_from' => '14.0.4',
+    'version_to' => '14.0.5',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/notifications/updates/update_14.0.4_14.0.5/',
+    'home_uri' => 'notifications_update_1404_1405',
+
+    'module_dir' => 'boonex/notifications/',
+    'module_uri' => 'notifications',
+
+    'db_prefix' => 'bx_notifications_',
+    'class_prefix' => 'BxNtfs',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Notifications',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/notifications/updates/14.0.4_14.0.5/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxNtfsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 855 - 0
modules/boonex/notifications/updates/14.0.4_14.0.5/source/classes/BxNtfsModule.php

@@ -0,0 +1,855 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Notifications Notifications
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+bx_import('BxBaseModNotificationsModule');
+
+define('BX_NTFS_TYPE_OBJECT_OWNER_AND_CONNECTIONS', 'obj_own_and_con');
+define('BX_NTFS_TYPE_DEFAULT', BX_NTFS_TYPE_OBJECT_OWNER_AND_CONNECTIONS);
+
+/**
+ * STYPE - Setting Type:
+ * 1. personal - related to you;
+ * 2. follow_member - related to members you follow;
+ * 3. follow_context - related to contexts you follow;
+ * 4. other - others.
+ */
+define('BX_NTFS_STYPE_PERSONAL', 'personal');
+define('BX_NTFS_STYPE_FOLLOW_MEMBER', 'follow_member');
+define('BX_NTFS_STYPE_FOLLOW_CONTEXT', 'follow_context');
+define('BX_NTFS_STYPE_OTHER', 'other');
+
+/**
+ * DTYPE - Delivery Type
+ */
+define('BX_NTFS_DTYPE_SITE', 'site');
+define('BX_NTFS_DTYPE_EMAIL', 'email');
+define('BX_NTFS_DTYPE_PUSH', 'push');
+
+/**
+ * SLTMODE - Silent mode:
+ * It is needed for alert sending module to tell that the alert should be ignored 
+ * with Notifications module completely or partially. Available values: 
+ * 1. disabled (global, value = 0) - all notifications are available;
+ * 2. absolute (global, value = 1) - alert isn't registered which means that there is no notifications at all;
+ * 3. absolute (for Notifications only, value = 11) - the same as global absolute.
+ * 3. on-site only (value = 12) - alert is registered. It means that on-site notification is available while the others are disabled.
+ * 4. on-site + email (value = 13) - alert is registered (on-site) and notification via 'email' is enabled.
+ * 5. on-site + push (value = 14) - alert is registered (on-site) and notification via 'push' is enabled.
+ * 
+ * @see BxNtfsResponse::response - 'silent_mode' parameter in Alerts Extras array.
+ */
+define('BX_NTFS_SLTMODE_ABSOLUTE', 11);
+define('BX_NTFS_SLTMODE_SITE', 12);
+define('BX_NTFS_SLTMODE_SITE_EMAIL', 13);
+define('BX_NTFS_SLTMODE_SITE_PUSH', 14);
+
+class BxNtfsModule extends BxBaseModNotificationsModule
+{
+    /**
+     * Constructor
+     */
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_iOwnerId = $this->getUserId();
+    }
+
+    /**
+     * ACTION METHODS
+     */
+    function actionMarkAsClicked()
+    {
+        $iId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        $aEvent = $this->_oDb->getEvents(['browse' => 'id', 'value' => $iId]);
+        if(empty($aEvent) || !is_array($aEvent))
+            return echoJson(['code' => 1]);
+
+        $aParams = $this->_prepareParamsGet();
+        if($this->_oDb->markAsClicked($aParams['viewer_id'], $iId) === false)
+            return echoJson(['code' => 2]);
+
+        echoJson([
+            'code' => 0, 
+            'id' => $iId
+        ]);
+    }
+
+    function actionGetPosts()
+    {
+        $aParams = $this->_prepareParamsGet();
+        if(empty($aParams['owner_id']) || (int)$aParams['owner_id'] != $this->_iOwnerId)
+            return echoJson([]);
+
+        echoJson([
+            'events' => $this->_oTemplate->getPosts($aParams)
+        ]);
+    }
+
+    /**
+     * SERVICE METHODS
+     */
+
+    public function serviceGetSafeServices()
+    {
+        return [
+            'GetBlockView' => '',
+            'GetData' => '',
+            'GetUnreadNotificationsNum' => '',
+            'GetUnreadNotificationsNumEx' => '',
+            'MarkAsRead' => '',
+            'EnableSetting' => '',
+            'ChangeSetting' => '',
+        ];
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_notifications Invitations
+     * @subsection bx_notifications-other Other
+     * @subsubsection bx_notifications-get_include get_include
+     * 
+     * @code bx_srv('bx_notifications', 'get_include', [...]); @endcode
+     * 
+     * Get all necessary CSS and JS files to include in a page.
+     *
+     * @return string with all necessary CSS and JS files.
+     * 
+     * @see BxInvModule::serviceGetInclude
+     */
+    /** 
+     * @ref bx_notifications-get_include "get_include"
+     */
+    public function serviceGetInclude($bIncludeCss = true, $mixedIncludeJs = false)
+    {
+        return $this->_oTemplate->getInclude($bIncludeCss, $mixedIncludeJs);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_notifications Notifications
+     * @subsection bx_notifications-page_blocks Page Blocks
+     * @subsubsection bx_notifications-get_block_settings get_block_settings
+     * 
+     * @code bx_srv('bx_notifications', 'get_block_settings', [...]); @endcode
+     * 
+     * Get Settings block for a separate page.
+     *
+     * @return a string with block content to display on the site. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxNtfsModule::serviceGetBlockSettings
+     */
+    /** 
+     * @ref bx_notifications-get_block_settings "get_block_settings"
+     */
+    public function serviceGetBlockSettings($sDeliveryType = '', $aParams = array())
+    {
+        if(!isLogged())
+            return '';
+
+        if(empty($sDeliveryType) && bx_get('delivery') !== false)
+            $sDeliveryType = bx_process_input(bx_get('delivery'));
+
+        if(empty($sDeliveryType))
+            $sDeliveryType = BX_BASE_MOD_NTFS_DTYPE_SITE;
+
+        $iUserId = bx_get_logged_profile_id();
+        if(!empty($aParams['user_id']))
+            $iUserId = (int)$aParams['user_id'];
+        else
+            $aParams['user_id'] = $iUserId;
+
+        $this->_oDb->initSettingUser($iUserId);
+
+        return array(
+            'content' => $this->getBlockSettings($sDeliveryType, $aParams),
+        );
+    }
+
+    
+    /**
+     * @page service Service Calls
+     * @section bx_notifications Notifications
+     * @subsection bx_notifications-page_blocks Page Blocks
+     * @subsubsection bx_notifications-get_data get_data
+     * 
+     * @code bx_srv('bx_notifications', 'get_data', [...]); @endcode
+     * 
+     * Get data for app
+     *
+     * @return an array .
+     * 
+     * @see BxNtfsModule::serviceGetData
+     */
+     /** 
+     * @ref bx_notifications-get_data "get_data"
+     * @api @ref bx_notifications-get_data "get_data"
+     */
+    public function serviceGetData($aParams)
+    {
+        if(is_string($aParams))
+            $aParams = bx_api_get_browse_params($aParams, true);
+
+        return call_user_func_array([$this, 'serviceGetBlockView'], [
+            isset($aParams['type']) ? $aParams['type'] : '',
+            isset($aParams['start']) ? (int)$aParams['start'] : -1,
+            isset($aParams['per_page']) ? (int)$aParams['per_page'] : -1,
+            isset($aParams['modules']) ? $aParams['modules'] : [],
+        ]);
+    }
+    
+    /**
+     * @page service Service Calls
+     * @section bx_notifications Notifications
+     * @subsection bx_notifications-page_blocks Page Blocks
+     * @subsubsection bx_notifications-get_block_view get_block_view
+     * 
+     * @code bx_srv('bx_notifications', 'get_block_view', [...]); @endcode
+     * 
+     * Get View block for a separate page. Will return a block with "Empty" message if nothing found.
+     *
+     * @return an array describing a block to display on the site. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxNtfsModule::serviceGetBlockView
+     */
+    /** 
+     * @ref bx_notifications-get_block_view "get_block_view"
+     */
+    public function serviceGetBlockView($sType = '', $iStart = -1, $iPerPage = -1, $aModules = [])
+    {
+    	$aBrowseTypes = [BX_NTFS_TYPE_OBJECT_OWNER_AND_CONNECTIONS, BX_BASE_MOD_NTFS_TYPE_CONNECTIONS, BX_BASE_MOD_NTFS_TYPE_OBJECT_OWNER];
+
+    	if(empty($sType)) {
+            $mixedType = bx_get('type');
+            if($mixedType !== false && in_array($mixedType, $aBrowseTypes))
+                $sType = $mixedType;
+
+            if(empty($sType))
+                $sType = BX_NTFS_TYPE_DEFAULT;
+    	}
+
+    	$iOwnerId = $this->getUserId();
+        if(!$iOwnerId) {
+            $sResult = _t('_bx_ntfs_txt_msg_no_results');
+            return $this->_bIsApi ? [bx_api_get_msg($sResult)] : ['content' => MsgBox($sResult)];
+        }
+
+        $aParams = $this->_prepareParams($sType, $iOwnerId, $iStart, $iPerPage, $aModules);
+
+        if($this->_bIsApi) {
+            $aParams['start_from_item'] = true;
+
+            $aContent = defined('BX_API_PAGE') ? [] : $this->_oTemplate->getPosts($aParams);
+            if(!empty($aContent) && is_array($aContent)) {
+                $iContent = count($aContent);
+                if($iContent > $aParams['per_page'])
+                    $aContent = array_slice($aContent, 0, -($iContent - $aParams['per_page']));
+
+                $aParams['start'] = end($aContent)['id'];
+            }
+
+            if($aParams['start'] == 0)
+                $this->markAsRead($iOwnerId, $aParams);
+
+            return [bx_api_get_block('browse', [
+                'unit' => 'notifications',  
+                'request_url' => '/api.php?r=bx_notifications/get_data/&params[]=',
+                'params' => $aParams,
+                'id' => $iOwnerId,
+                'data' => $aContent]),
+            ];
+        }
+
+        $sContent = $this->_oTemplate->getViewBlock($aParams);
+
+        $this->markAsRead($iOwnerId, $aParams);
+
+        return ['content' => $sContent]; 
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_notifications Notifications
+     * @subsection bx_notifications-browsing Browsing
+     * @subsubsection bx_notifications-get_event_by_id get_event_by_id
+     * 
+     * @code bx_srv('bx_notifications', 'get_event_by_id', [...]); @endcode
+     * 
+     * Get event's data by event ID.
+     *
+     * @return an array with event's data.
+     * 
+     * @see BxNtfsModule::serviceGetEventById
+     */
+    /** 
+     * @ref bx_notifications-get_event_by_id "get_event_by_id"
+     */
+    public function serviceGetEventById($iId)
+    {
+        $aParams = array(
+            'browse' => 'id',
+            'value' => $iId,
+        );
+    	return $this->_oDb->getEvents($aParams);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_notifications Notifications
+     * @subsection bx_notifications-other Other
+     * @subsubsection bx_notifications-get_notifications get_notifications
+     * 
+     * @code bx_srv('bx_notifications', 'get_notifications', [...]); @endcode
+     * 
+     * Get notifications by params.
+     *
+     * @return array with notifications.
+     * 
+     * @see BxNtfsModule::serviceGetNotifications
+     */
+    /** 
+     * @ref bx_notifications-get_notifications "get_notifications"
+     */
+    public function serviceGetNotifications($iOwnerId = 0, $aBrowseParams = array())
+    {
+        if(!$iOwnerId)
+            $iOwnerId = $this->getUserId();
+
+        if(!$iOwnerId)
+            return 0;
+
+        $aParams = $this->_prepareParams(BX_NTFS_TYPE_DEFAULT, $iOwnerId);
+        if(!empty($aBrowseParams) && is_array($aBrowseParams))
+            $aParams = array_merge($aParams, $aBrowseParams);
+
+        $aEvents = $this->_oDb->getEvents($aParams);
+
+        if($this->_oConfig->isEventsGrouped())
+            $this->groupEvents($aEvents);
+
+        // returns parsed content for React Jot
+        if (!is_array($aBrowseParams) && $aBrowseParams === 'return_parsed_content')
+            foreach ($aEvents as &$aContent)
+                $this->_oTemplate->getPost($aContent, array('return_parsed_content'));
+
+        return $aEvents;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_notifications Notifications
+     * @subsection bx_notifications-other Other
+     * @subsubsection bx_notifications-get_unread_notifications get_unread_notifications
+     * 
+     * @code bx_srv('bx_notifications', 'get_unread_notifications', [...]); @endcode
+     * 
+     * Get unread notifications.
+     *
+     * @return array with unread notifications.
+     * 
+     * @see BxNtfsModule::serviceGetUnreadNotifications
+     */
+    /** 
+     * @ref bx_notifications-get_unread_notifications "get_unread_notifications"
+     */
+    public function serviceGetUnreadNotifications($iOwnerId = 0)
+    {
+        return $this->serviceGetNotifications($iOwnerId, array('new' => 1));
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_notifications Notifications
+     * @subsection bx_notifications-other Other
+     * @subsubsection bx_notifications-get_unread_notifications_num get_unread_notifications_num
+     * 
+     * @code bx_srv('bx_notifications', 'get_unread_notifications_num', [...]); @endcode
+     * 
+     * Get number of unread notifications.
+     *
+     * @return integer value with number of unread notifications.
+     * 
+     * @see BxNtfsModule::serviceGetUnreadNotificationsNum
+     */
+    /** 
+     * @ref bx_notifications-get_unread_notifications_num "get_unread_notifications_num"
+     */
+    public function serviceGetUnreadNotificationsNum($iOwnerId = 0)
+    {
+        if(!$iOwnerId)
+            $iOwnerId = $this->getUserId();
+
+        if(!$iOwnerId)
+            return 0;
+
+        $sModule = $this->_oConfig->getName();
+        $aBrowseParams = $this->_prepareParams(BX_NTFS_TYPE_DEFAULT, $iOwnerId, 0, PHP_INT_MAX);
+
+        $sParamCheck = 'perform_privacy_check';
+        $sParamCheckFor = 'perform_privacy_check_for';
+
+        $iEvents = 0;
+        if($this->_oConfig->isEventsGrouped()) {
+            $aEvents = $this->_oDb->getEvents($aBrowseParams);
+
+            $this->groupEvents($aEvents);
+
+            $iCount = 0;
+            $iLastRead = $this->_oDb->getLastRead((int)$aBrowseParams['owner_id']);
+            foreach($aEvents as $aEvent)
+                if($aEvent['id'] > $iLastRead)
+                    $iCount++;
+
+            $aEvents = array_slice($aEvents, 0, $iCount);
+        }
+        else {
+            $aBrowseParams = array_merge($aBrowseParams, [
+                'new' => 1,
+            ]);
+
+            $aEvents = $this->_oDb->getEvents($aBrowseParams);
+        }
+
+        foreach($aEvents as $aEvent) {
+            if(isset($aBrowseParams[$sParamCheck]) && $aBrowseParams[$sParamCheck] !== true) 
+                continue;
+
+            $iViewerId = !empty($aBrowseParams[$sParamCheckFor]) ? (int)$aBrowseParams[$sParamCheckFor] : 0;
+
+            $oPrivacyInt = BxDolPrivacy::getObjectInstance($this->_oConfig->getObject('privacy_view'));
+            if(!$oPrivacyInt->check($aEvent['id'], $iViewerId))
+                continue;
+
+            $oPrivacyExt = $this->_oConfig->getPrivacyObject($aEvent['type'] . '_' . $aEvent['action']);
+            if($oPrivacyExt !== false && !$oPrivacyExt->check($aEvent['id'], $iViewerId))
+                continue;
+
+            $iContentObjectId = $this->_oConfig->getContentObjectId($aEvent);
+            if($iContentObjectId === false) {
+                $this->_oTemplate->getPost($aEvent, $aBrowseParams);
+                $iContentObjectId = $this->_oConfig->getContentObjectId($aEvent);
+            }
+
+            $sSrvModule = $this->_oConfig->getContentModule($aEvent);
+            $sSrvMethod = 'check_allowed_with_content_for_profile';
+            if($sSrvModule && bx_is_srv($sSrvModule, $sSrvMethod) && bx_srv($sSrvModule, $sSrvMethod, ['view', $iContentObjectId, $iViewerId]) !== CHECK_ACTION_RESULT_ALLOWED)
+                continue;
+
+            $bEventCanceled = false;
+            
+            /**
+             * @hooks
+             * @hookdef hook-bx_notifications-is_notification 'bx_notifications', 'is_notification' - hook to override checking if an event is available or not
+             * - $unit_name - equals `bx_notifications`
+             * - $action - equals `is_notification`
+             * - $object_id - not used
+             * - $sender_id - not used
+             * - $extra_params - array of additional params with the following array keys:
+             *      - `browse_params` - [array] browse params array as key&value pairs
+             *      - `event` - [array] by ref, array with event data as key&value pairs, can be overridden in hook processing
+             *      - `event_canceled` - [boolean] by ref, if event is canceled (not available) or not, can be overridden in hook processing
+             * @hook @ref hook-bx_notifications-is_notification
+             */
+            bx_alert($sModule, 'is_notification', 0, 0, [
+                'browse_params' => $aBrowseParams,
+                'event' => &$aEvent, 
+                'event_canceled' => &$bEventCanceled
+            ]);
+
+            if($bEventCanceled)
+                continue;
+
+            $iEvents++;
+        }
+
+        return $iEvents;
+    }
+    
+    public function serviceGetUnreadNotificationsNumEx($iOwnerId, $iCount)
+    {
+         header('Content-Type: text/event-stream');
+         header('Cache-Control: no-cache');
+         $iTmp = $this->serviceGetUnreadNotificationsNum($iOwnerId);
+         if ($iCount != $iTmp){
+             $time = date('r');
+             echo "id: " . time() . "\n";
+             echo "data: $iTmp\n\n";
+             flush();
+         }
+         exit();
+            
+     }
+
+    /**
+     * @page service Service Calls
+     * @section bx_notifications Notifications
+     * @subsection bx_notifications-other Other
+     * @subsubsection bx_notifications-get_live_updates get_live_updates
+     * 
+     * @code bx_srv('bx_notifications', 'get_live_updates', [...]); @endcode
+     * 
+     * Get data for Live Updates system.
+     *
+     * @return an array with special format.
+     * 
+     * @see BxNtfsModule::serviceGetLiveUpdates
+     */
+    /** 
+     * @ref bx_notifications-get_live_updates "get_live_updates"
+     */
+    public function serviceGetLiveUpdates($aMenuItemParent, $aMenuItemChild, $iCount = 0)
+    {
+        $iOwnerId = $this->getUserId();
+        $iCountNew = $this->serviceGetUnreadNotificationsNum($iOwnerId);
+        if($iCountNew == $iCount)
+			return false;
+
+        return array(
+    		'count' => $iCountNew, // required
+    		'method' => 'bx_menu_show_live_update(oData)', // required
+    		'data' => array(
+    			'code' => BxDolTemplate::getInstance()->parseHtmlByTemplateName('menu_item_addon', array(
+    				'content' => '{count}'
+                )),
+                'mi_parent' => $aMenuItemParent,
+                'mi_child' => $aMenuItemChild
+    		),  // optional, may have some additional data to be passed in JS method provided using 'method' param above.
+    	);
+    }
+
+    /*
+     * COMMON METHODS
+     */
+    public function getBlockSettings($sDeliveryType, $aParams = array())
+    {
+        return $this->_oTemplate->getSettingsBlock($sDeliveryType, $aParams);
+    }
+
+    public function setSubmenu($sSelected)
+    {
+    	$oSubmenuSystem = BxDolMenu::getObjectInstance('sys_site_submenu');
+        if(!$oSubmenuSystem)
+            return;
+
+        $CNF = &$this->_oConfig->CNF;
+
+        $oSubmenuSystem->setObjectSubmenu($CNF['OBJECT_MENU_SUBMENU'], array (
+            'title' => _t('_bx_ntfs'),
+            'link' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink($CNF['URL_HOME'])),
+            'icon' => '',
+        ));
+
+        $oSubmenuModule = BxDolMenu::getObjectInstance($CNF['OBJECT_MENU_SUBMENU']);
+        if($oSubmenuModule)
+            $oSubmenuModule->setSelected($this->_oConfig->getName(), $sSelected);
+    }
+
+    public function onPost($iId)
+    {
+    	//--- Event -> Post for Alerts Engine ---//
+        $oAlert = new BxDolAlerts($this->_oConfig->getObject('alert'), 'post', $iId);
+        $oAlert->alert();
+        //--- Event -> Post for Alerts Engine ---//
+    }
+
+    public function enableSettingsLike($iId, $bValue, $bAdministration = false)
+    {
+        $aSetting = $this->_oDb->getSetting(array(
+            'by' => $bAdministration ? 'id' : 'tsu_id', 
+            'id' => (int)$iId
+        ));
+        if(empty($aSetting) || !is_array($aSetting))
+            return false;
+
+        $aSettingsIds = $this->_oDb->getSetting(array(
+            'by' => 'group_type_delivery', 
+            'group' => $aSetting['group'], 
+            'delivery' => $aSetting['delivery'], 
+            'type' => $aSetting['type'], 
+            'active' => !$bAdministration
+        ));
+        if(empty($aSettingsIds) || !is_array($aSettingsIds))
+            return false;
+
+        $iUserId = bx_get_logged_profile_id();
+
+        $mixedResult = false;
+        if($bAdministration)
+            $mixedResult =  $this->_oDb->activateSettingById($bValue, $aSettingsIds);
+        else
+            $mixedResult = $this->_oDb->activateSettingByIdUser($bValue, $iUserId, $aSettingsIds);
+
+        return $mixedResult;
+    }
+
+    public function changeSettingsValueLike($iId, $sField, $mixedValue, $bAdministration = false)
+    {
+        $aSetting = $this->_oDb->getSetting(array(
+            'by' => $bAdministration ? 'id' : 'tsu_id', 
+            'id' => (int)$iId
+        ));
+        if(empty($aSetting) || !is_array($aSetting))
+            return false;
+
+        $aSettingsIds = $this->_oDb->getSetting(array(
+            'by' => 'group_type_delivery', 
+            'group' => $aSetting['group'], 
+            'delivery' => $aSetting['delivery'], 
+            'type' => $aSetting['type'], 
+            'active' => !$bAdministration
+        ));
+        if(empty($aSettingsIds) || !is_array($aSettingsIds))
+            return false;
+
+        $iUserId = bx_get_logged_profile_id();
+
+        $mixedResult = false;
+        if($bAdministration)
+            $mixedResult =  $this->_oDb->changeSettingById($sField, $mixedValue, $aSettingsIds);
+        else
+            $mixedResult = $this->_oDb->changeSettingByIdUser($sField, $mixedValue, $iUserId, $aSettingsIds);
+
+        return $mixedResult;
+    }
+
+    public function sendNotificationEmail($iProfile, $aNotification)
+    {
+        if(!$iProfile)
+            return false;
+
+        $aSettings = &$aNotification['settings'];
+
+        $sTemplate = !empty($aSettings['template']) ? $aSettings['template'] : 'bx_notifications_new_event';
+        $aTemplateMarkers = ['subject' => $aNotification['subject'], 'content' => $aNotification['content']];
+        if(!empty($aSettings['markers']) && is_array($aSettings['markers']))
+            $aTemplateMarkers = array_merge($aTemplateMarkers, $aSettings['markers']);              
+
+        $aTemplate = null;
+        /**
+         * @hooks
+         * @hookdef hook-bx_notifications-before_parse_email_template 'bx_notifications', 'before_parse_email_template' - hook to override email template
+         * - $unit_name - equals `bx_notifications`
+         * - $action - equals `before_parse_email_template`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `profile_id` - [int] recipient profile id
+         *      - `template` - [string] email template name
+         *      - `markers` - [array] parsable variables array as key&value pairs
+         *      - `notification` - [array] notification data array as key&value pairs
+         *      - `override_result` - [array] by ref, email template (subject and body), if null, then default one will be used, can be overridden in hook processing
+         * @hook @ref hook-bx_notifications-before_parse_email_template
+         */
+        bx_alert($this->_aModule['name'], 'before_parse_email_template', 0, 0, [
+            'profile_id' => $iProfile, 
+            'template' => $sTemplate, 
+            'markers' => $aTemplateMarkers, 
+            'notification' => $aNotification, 
+            'override_result' => &$aTemplate
+        ]);
+        if(is_null($aTemplate))
+            $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate($sTemplate, $aTemplateMarkers, 0, $iProfile);
+        if(!$aTemplate)
+            return false;
+
+        $sEmail = BxDolProfileQuery::getInstance()->getEmailById($iProfile);
+        $sSubject = !empty($aSettings['subject']) ? $aSettings['subject'] : $aTemplate['Subject'];
+        return sendMail($sEmail, $sSubject, $aTemplate['Body'], 0, array(), BX_EMAIL_NOTIFY, 'html', false, array(), true);
+    }
+
+    public function sendNotificationPush($iProfile, $aNotification)
+    {
+        if(!$iProfile)
+            return false;
+
+        $aContent = &$aNotification['content'];
+        $aSettings = &$aNotification['settings'];
+        
+        /**
+         * @hooks
+         * @hookdef hook-bx_notifications-before_send_notification_push 'bx_notifications', 'before_send_notification_push' - hook to override push message
+         * - $unit_name - equals `bx_notifications`
+         * - $action - equals `before_send_notification_push`
+         * - $object_id - not used
+         * - $sender_id - not used
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `profile_id` - [int] recipient profile id
+         *      - `content` - [array] by ref, push message params array as key&value pairs, can be overridden in hook processing
+         *      - `setting` - [array] by ref, push settings array as key&value pairs, can be overridden in hook processing
+         *      - `subject` - [string] by ref, push message subject, can be overridden in hook processing
+         * @hook @ref hook-bx_notifications-before_send_notification_push
+         */
+        bx_alert($this->_aModule['name'], 'before_send_notification_push', 0, 0, [
+            'profile_id' => $iProfile, 
+            'content' => &$aContent, 
+            'setting' => &$aSettings, 
+            'subject' => &$sSubject
+        ]);
+        
+        if (!$aContent)
+            return false;
+
+        $sSubject = !empty($aSettings['subject']) ? $aSettings['subject'] : _t('_bx_ntfs_push_new_event_subject', getParam('site_title'));
+
+        return BxDolPush::getInstance()->send($iProfile, array(
+            'contents' => array(
+                'en' => $aContent['message']
+            ),
+            'headings' => array(
+                'en' => $sSubject
+            ),
+            'url' => $aContent['url'],
+            'icon' => $aContent['icon']
+        ), true);
+    }
+
+    public function groupEvents(&$aEvents)
+    {
+        //--- Check for Visual Grouping
+        $aGroups = [];
+        foreach($aEvents as $iIndex => $aEvent) {
+            if(empty($aEvent['source']))
+                continue;
+
+            $sSource = $aEvent['source'];
+            if(!isset($aGroups[$sSource]))
+               $aGroups[$sSource] = [];
+
+            $aGroups[$sSource][$iIndex] = $aEvent['priority'];
+        }
+
+        //--- Perform Visual Grouping
+        foreach($aGroups as $sSource => $aGroup) {
+            if(!is_array($aGroup) || count($aGroup) < 2)
+                continue;
+
+            arsort($aGroup);
+            $iPriorityMax = reset($aGroup);
+
+            foreach($aGroup as $iIndex => $iPriority)
+                if($iPriority != $iPriorityMax)
+                    unset($aEvents[$iIndex]);
+        }
+    }
+    
+    public function serviceMarkAsRead()
+    {
+        $iOwnerId = bx_get_logged_profile_id();
+        $aParams = $this->_prepareParams(BX_NTFS_TYPE_OBJECT_OWNER_AND_CONNECTIONS, $iOwnerId);
+        $this->markAsRead($iOwnerId, $aParams);
+    }
+
+    public function markAsRead($iOwnerId, $aParams)
+    {
+        $aParams['browse'] = 'first';
+    	$aEvent = $this->_oDb->getEvents($aParams);
+    	if(empty($aEvent) || !is_array($aEvent))
+            return false;
+
+        return $this->_oDb->markAsRead($iOwnerId, $aEvent['id']) !== false;
+    }
+
+    public function serviceEnableSetting($mixedParams)
+    {
+        if(!is_array($mixedParams))
+            $mixedParams = json_decode($mixedParams, true);
+
+        if(!isset($mixedParams['id'], $mixedParams['value']))
+            return false;
+
+        $iId = (int)$mixedParams['id'];
+        $iValue = (int)$mixedParams['value'];
+        $bAdministration = isset($mixedParams['admin']) && $mixedParams['admin'];
+
+        if(!$this->_oConfig->isSettingsGrouped())
+            $mixedResult = $this->_oDb->updateSetting(['active' => $iValue], ['id' => $iId]);
+        else 
+            $mixedResult = $this->enableSettingsLike($iId, $iValue, $bAdministration);
+
+        return $mixedResult !== false;
+    }
+            
+    public function serviceChangeSetting($mixedParams)
+    {
+        if(!is_array($mixedParams))
+            $mixedParams = json_decode($mixedParams, true);
+
+        if(!isset($mixedParams['id'], $mixedParams['value']))
+            return false;
+
+        $iId = (int)$mixedParams['id'];
+        $iValue = (int)$mixedParams['value'];
+
+        if(!$this->_oConfig->isSettingsGrouped())
+            $mixedResult = $this->_oDb->updateSetting(['value' => $iValue], ['id' => $iId]);
+        else 
+            $mixedResult = $this->changeSettingsValueLike($iId, 'value', $iValue, true);
+
+        return $mixedResult !== false;
+    }
+
+    /*
+     * INTERNAL METHODS
+     */
+    protected function _prepareParams($sType = '', $iOwnerId = 0, $iStart = -1, $iPerPage = -1, $aModules = [])
+    {
+        $iUserId = $this->getUserId();
+        $iOwnerId = (int)$iOwnerId != 0 ? $iOwnerId : $iUserId;
+
+        $aParams = [
+            'browse' => 'list',
+            'type' => !empty($sType) ? $sType : BX_NTFS_TYPE_DEFAULT,
+            'owner_id' => $iOwnerId,
+            'start' => (int)$iStart > 0 ? $iStart : 0,
+            'start_from_item' => false,
+            'per_page' => (int)$iPerPage > 0 ? $iPerPage : $this->_oConfig->getPerPage(),
+            'modules' => is_array($aModules) && !empty($aModules) ? $aModules : [],
+            'last_read' => $this->_oDb->getLastRead($iOwnerId),
+            'viewer_id' => $iUserId,
+            'active' => 1
+        ];
+
+        return $aParams;
+    }
+
+    protected function _prepareParamsGet()
+    {
+        $aParams = [];
+        $aParams['browse'] = 'list';
+
+        $sType = bx_get('type');
+        $aParams['type'] = $sType !== false ? bx_process_input($sType, BX_DATA_TEXT) : BX_NTFS_TYPE_DEFAULT;
+
+        $aParams['owner_id'] = $sType !== false ? bx_process_input(bx_get('owner_id'), BX_DATA_INT) : $this->getUserId();
+
+        $iStart = bx_get('start');
+        $aParams['start'] = $iStart !== false ? bx_process_input($iStart, BX_DATA_INT) : 0;
+        $aParams['start_from_item'] = false;
+
+        $iPerPage = bx_get('per_page');
+        $aParams['per_page'] = $iPerPage !== false ? bx_process_input($iPerPage, BX_DATA_INT) : $this->_oConfig->getPerPage();
+
+        $aModules = bx_get('modules');
+        $aParams['modules'] = $aModules !== false ? bx_process_input($aModules, BX_DATA_TEXT) : [];
+
+        $iViewerId = $this->getUserId();
+        if(($aParams['viewer_id'] = bx_get('viewer_id')) !== false)
+            $aParams['viewer_id'] = bx_process_input($aParams['viewer_id'], BX_DATA_INT);
+        if(!$aParams['viewer_id'] || $aParams['viewer_id'] != $iViewerId)
+            $aParams['viewer_id'] = $iViewerId;
+
+        $aParams['active'] = 1;
+
+        return $aParams;
+    }
+}
+
+/** @} */

+ 82 - 0
modules/boonex/notifications/updates/14.0.4_14.0.5/source/install/config.php

@@ -0,0 +1,82 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Notifications Notifications
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_notifications',
+    'title' => 'Notifications',
+    'note' => 'Notifications module.',
+    'version' => '14.0.5',
+    'vendor' => 'UNA INC',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/notifications/',
+    'home_uri' => 'notifications',
+
+    'db_prefix' => 'bx_notifications_',
+    'class_prefix' => 'BxNtfs',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Notifications',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+    	'update_relations_for_all' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+    	'update_relations_for_all' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+
+    /**
+     * Relations Section
+     */
+    'relation_handlers' => array(
+    	'on_install' => '',
+    	'on_uninstall' => 'delete_module_events',
+	    'on_enable' => 'add_handlers',
+	    'on_disable' => 'delete_handlers',
+    ),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/organizations/updates/14.0.5_14.0.6/install/config.php

@@ -0,0 +1,51 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'title' => 'Organizations',
+    'version_from' => '14.0.5',
+    'version_to' => '14.0.6',
+    'vendor' => 'UNA INC',
+
+    'compatible_with' => array(
+        '14.0.0-RC2'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/organizations/updates/update_14.0.5_14.0.6/',
+    'home_uri' => 'orgs_update_1405_1406',
+
+    'module_dir' => 'boonex/organizations/',
+    'module_uri' => 'orgs',
+
+    'db_prefix' => 'bx_organizations_',
+    'class_prefix' => 'BxOrgs',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Organizations',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/organizations/updates/14.0.5_14.0.6/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxOrgsUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

部分文件因为文件数量过多而无法显示