소스 검색

Modules updates.

AntonLV 3 년 전
부모
커밋
8424b38e59
85개의 변경된 파일과 16127개의 추가작업 그리고 0개의 파일을 삭제
  1. 51 0
      modules/boonex/artificer/updates/13.0.13_13.0.14/install/config.php
  2. 26 0
      modules/boonex/artificer/updates/13.0.13_13.0.14/install/installer.php
  3. 344 0
      modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/css/common.css
  4. 210 0
      modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/css/menu.css
  5. 25 0
      modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/menu_profile_followings.html
  6. 17 0
      modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/menu_profile_followings_subitems.html
  7. 116 0
      modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/pt_application.html
  8. 68 0
      modules/boonex/artificer/updates/13.0.13_13.0.14/source/install/config.php
  9. 133 0
      modules/boonex/artificer/updates/13.0.13_13.0.14/source/js/sidebar.js
  10. 51 0
      modules/boonex/attendant/updates/13.0.4_13.0.5/install/config.php
  11. 13 0
      modules/boonex/attendant/updates/13.0.4_13.0.5/install/installer.php
  12. 93 0
      modules/boonex/attendant/updates/13.0.4_13.0.5/source/classes/BxAttendantDb.php
  13. 77 0
      modules/boonex/attendant/updates/13.0.4_13.0.5/source/install/config.php
  14. 51 0
      modules/boonex/developer/updates/13.0.5_13.0.6/install/config.php
  15. 13 0
      modules/boonex/developer/updates/13.0.5_13.0.6/install/installer.php
  16. 11 0
      modules/boonex/developer/updates/13.0.5_13.0.6/install/langs/en.xml
  17. 29 0
      modules/boonex/developer/updates/13.0.5_13.0.6/install/sql/install.sql
  18. 69 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/install/config.php
  19. 529 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/install/langs/en.xml
  20. 724 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/install/sql/install.sql
  21. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/bp_add_block.html
  22. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/bp_add_page.html
  23. 3 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/bp_export.html
  24. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_display.html
  25. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_form.html
  26. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_list.html
  27. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_search_field.html
  28. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_search_form.html
  29. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_value.html
  30. 3 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_export.html
  31. 22 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/nav_add_item.html
  32. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/nav_add_menu.html
  33. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/nav_add_set.html
  34. 3 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/nav_export.html
  35. 19 0
      modules/boonex/developer/updates/13.0.5_13.0.6/source/template/pgt_add_keys.html
  36. 51 0
      modules/boonex/events/updates/13.0.10_13.0.11/install/config.php
  37. 13 0
      modules/boonex/events/updates/13.0.10_13.0.11/install/installer.php
  38. 634 0
      modules/boonex/events/updates/13.0.10_13.0.11/source/classes/BxEventsModule.php
  39. 152 0
      modules/boonex/events/updates/13.0.10_13.0.11/source/install/config.php
  40. 51 0
      modules/boonex/files/updates/13.0.9_13.0.10/install/config.php
  41. 13 0
      modules/boonex/files/updates/13.0.9_13.0.10/install/installer.php
  42. 546 0
      modules/boonex/files/updates/13.0.9_13.0.10/source/classes/BxFilesSearchResult.php
  43. 135 0
      modules/boonex/files/updates/13.0.9_13.0.10/source/install/config.php
  44. 51 0
      modules/boonex/forum/updates/13.0.12_13.0.13/install/config.php
  45. 13 0
      modules/boonex/forum/updates/13.0.12_13.0.13/install/installer.php
  46. 244 0
      modules/boonex/forum/updates/13.0.12_13.0.13/source/classes/BxForumMenuCategories.php
  47. 1068 0
      modules/boonex/forum/updates/13.0.12_13.0.13/source/classes/BxForumModule.php
  48. 151 0
      modules/boonex/forum/updates/13.0.12_13.0.13/source/install/config.php
  49. 51 0
      modules/boonex/market/updates/13.0.9_13.0.10/install/config.php
  50. 13 0
      modules/boonex/market/updates/13.0.9_13.0.10/install/installer.php
  51. 2 0
      modules/boonex/market/updates/13.0.9_13.0.10/install/sql/install.sql
  52. 1376 0
      modules/boonex/market/updates/13.0.9_13.0.10/source/classes/BxMarketModule.php
  53. 165 0
      modules/boonex/market/updates/13.0.9_13.0.10/source/classes/BxMarketSearchResult.php
  54. 140 0
      modules/boonex/market/updates/13.0.9_13.0.10/source/install/config.php
  55. 633 0
      modules/boonex/market/updates/13.0.9_13.0.10/source/install/sql/install.sql
  56. 51 0
      modules/boonex/massmailer/updates/13.0.5_13.0.6/install/config.php
  57. 13 0
      modules/boonex/massmailer/updates/13.0.5_13.0.6/install/installer.php
  58. 9 0
      modules/boonex/massmailer/updates/13.0.5_13.0.6/install/sql/install.sql
  59. 219 0
      modules/boonex/massmailer/updates/13.0.5_13.0.6/source/classes/BxMassMailerGridCampaigns.php
  60. 23 0
      modules/boonex/massmailer/updates/13.0.5_13.0.6/source/classes/BxMassMailerPageEntry.php
  61. 77 0
      modules/boonex/massmailer/updates/13.0.5_13.0.6/source/install/config.php
  62. 137 0
      modules/boonex/massmailer/updates/13.0.5_13.0.6/source/install/sql/install.sql
  63. 51 0
      modules/boonex/notifications/updates/13.0.11_13.0.12/install/config.php
  64. 13 0
      modules/boonex/notifications/updates/13.0.11_13.0.12/install/installer.php
  65. 440 0
      modules/boonex/notifications/updates/13.0.11_13.0.12/source/classes/BxNtfsDb.php
  66. 82 0
      modules/boonex/notifications/updates/13.0.11_13.0.12/source/install/config.php
  67. 51 0
      modules/boonex/payment/updates/13.0.10_13.0.11/install/config.php
  68. 13 0
      modules/boonex/payment/updates/13.0.10_13.0.11/install/installer.php
  69. 338 0
      modules/boonex/payment/updates/13.0.10_13.0.11/source/classes/BxPaymentConfig.php
  70. 632 0
      modules/boonex/payment/updates/13.0.10_13.0.11/source/classes/BxPaymentProviderStripeBasic.php
  71. 70 0
      modules/boonex/payment/updates/13.0.10_13.0.11/source/install/config.php
  72. 51 0
      modules/boonex/profiler/updates/13.0.3_13.0.4/install/config.php
  73. 13 0
      modules/boonex/profiler/updates/13.0.3_13.0.4/install/installer.php
  74. 680 0
      modules/boonex/profiler/updates/13.0.3_13.0.4/source/classes/BxProfiler.php
  75. 68 0
      modules/boonex/profiler/updates/13.0.3_13.0.4/source/install/config.php
  76. 51 0
      modules/boonex/tasks/updates/13.0.7_13.0.8/install/config.php
  77. 13 0
      modules/boonex/tasks/updates/13.0.7_13.0.8/install/installer.php
  78. 22 0
      modules/boonex/tasks/updates/13.0.7_13.0.8/source/classes/BxTasksFormListEntry.php
  79. 149 0
      modules/boonex/tasks/updates/13.0.7_13.0.8/source/install/config.php
  80. 51 0
      modules/boonex/timeline/updates/13.0.14_13.0.15/install/config.php
  81. 13 0
      modules/boonex/timeline/updates/13.0.14_13.0.15/install/installer.php
  82. 983 0
      modules/boonex/timeline/updates/13.0.14_13.0.15/source/classes/BxTimelineConfig.php
  83. 31 0
      modules/boonex/timeline/updates/13.0.14_13.0.15/source/classes/BxTimelineMenuView.php
  84. 3248 0
      modules/boonex/timeline/updates/13.0.14_13.0.15/source/classes/BxTimelineTemplate.php
  85. 143 0
      modules/boonex/timeline/updates/13.0.14_13.0.15/source/install/config.php

+ 51 - 0
modules/boonex/artificer/updates/13.0.13_13.0.14/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' => 'Artificer',
+    'version_from' => '13.0.13',
+    'version_to' => '13.0.14',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/artificer/updates/update_13.0.13_13.0.14/',
+    'home_uri' => 'artificer_update_13013_13014',
+
+    'module_dir' => 'boonex/artificer/',
+    'module_uri' => 'artificer',
+
+    'db_prefix' => 'bx_artificer_',
+    'class_prefix' => 'BxArtificer',
+
+    /**
+     * 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 Artificer Template',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 26 - 0
modules/boonex/artificer/updates/13.0.13_13.0.14/install/installer.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ */
+
+class BxArtificerUpdater 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;
+    }
+}

+ 344 - 0
modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/css/common.css

@@ -0,0 +1,344 @@
+/*
+import url(../../../../../../../template/css/common.css);
+*/
+
+/*--- General ---*/
+.bx-clear {
+    clear:both;
+    float:none;
+    display:block;
+    width: 0px;
+    height: 0px;
+    font-size:1px;
+}
+.bx-clearfix:before,
+.bx-clearfix:after {
+    content:"";
+    display:table;
+}
+.bx-clearfix:after {
+    clear:both;
+}
+
+.bx-tag b,
+.bx-tag s {
+    text-decoration: none;
+    font-weight: normal;
+}
+
+.sys-icon-emoji {
+    font-weight: normal !important;
+}
+
+.sys-icon-image,
+.sys-icon-svg {
+    display: inline-block;
+    width: 1.5rem;
+    height: 1.5rem;
+    background-size: contain;
+    background-position: center;
+    background-repeat: no-repeat;
+}
+
+/*--- Application ---*/
+.bx-main.bx-pt-application .bx-menu-toolbar-padding {
+    padding-top: 0;
+}
+.bx-main.bx-pt-application .bx-main-column.narrow.bx-mc-narrowed .bx-mc-cnt {
+    padding: 0;
+}
+
+/*--- Design Boxes ---*/
+.bx-db-container {
+    position:relative;
+}
+.bx-db-header {
+    display: -webkit-box;
+    display: -webkit-flex;
+    display: -ms-flexbox;
+    display: flex;
+
+    -webkit-box-align: center;
+    -webkit-align-items: center;
+    -ms-flex-align: center;
+    align-items: center;
+
+    -webkit-flex-wrap: nowrap;
+    -ms-flex-wrap: nowrap;
+    flex-wrap: nowrap;
+}
+.bx-db-container.bx-dbc-nopadding > .bx-db-header {
+    padding-left: 0;
+    padding-right: 0;
+}
+.bx-db-container.bx-dbc-nobg .bx-db-divider {
+    display: none;
+}
+.bx-db-title {
+    -webkit-flex: 1 1 auto;
+    -ms-flex: 1 1 auto;
+    flex: 1 1 auto;
+
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+}
+
+.bx-db-menu {
+    -webkit-flex: 1 2 auto;
+    -ms-flex: 1 2 auto;
+    flex: 1 2 auto;
+
+    max-width: 75%;
+}
+
+.bx-media-phone .bx-db-menu {
+    max-width: 65%;
+}
+
+.bx-db-menu-tab {
+    margin-left: 5px;
+    text-align: right;
+    overflow-x: auto;
+}
+.bx-db-menu-tab.bx-db-menu-tab-more-auto {
+    overflow-x: visible;
+}
+
+.bx-db-menu-tab .bx-menu-custom-hor:not(.bx-menu-more-auto) {
+    width: max-content;
+}
+
+.bx-db-menu-tab-active {
+    text-decoration:underline;
+}
+
+.bx-db-menu-tab-btn a {
+    text-decoration:none;
+}
+
+.bx-db-menu-tab-btn > ul > li {
+    display: flex;
+}
+
+.bx-db-menu .sys-icon-a {
+    vertical-align:middle;
+}
+
+.bx-db-menu-popup {
+    text-align: left;
+}
+
+.bx-db-content {
+    position: relative;
+}
+
+.bx-content-padding-block {
+    padding: 1rem;
+}
+
+.bx-db-content .bx-db-help {
+    position: absolute;
+    top: 0;
+    right: 0;
+    width: 2.0rem;
+    height: 2.0rem;
+    border-radius: 2rem;
+    background-color: rgba(0, 0, 0, 0.1);
+    line-height: 2rem;
+    text-align: center;
+}
+
+.bx-db-help-popup {
+    max-width: 25rem;
+    max-height: 25rem;
+    overflow: auto;
+}
+
+/*--- Design Boxes Menu ---*/
+.bx-db-menu ul li {
+    white-space: nowrap;
+}
+.bx-db-menu ul li:not(.bx-stl-mil-sm) {
+    border-radius: 0;
+}
+.bx-db-menu ul.bx-menu-hor,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor.bx-menu-more-auto:not(.bx-menu-submenu-more-auto) {
+    display: -webkit-inline-flex;
+    display: inline-flex;
+    -webkit-justify-content: flex-end;
+    justify-content: flex-end;
+}
+.bx-db-menu ul.bx-menu-hor > li:not(.bx-stl-mil-sm),
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor > li:not(.bx-stl-mil-sm) {
+    display: block;
+
+    -webkit-flex: 0 1 auto; 
+    -ms-flex:  0 1 auto; 
+    flex:  0 1 auto;
+
+    padding: 0 0.25rem;
+
+    border-bottom: 1px solid transparent;
+}
+.bx-db-menu .bx-menu-custom.bx-menu-custom-hor > li:not(.bx-stl-mil-sm) > span {
+    display: block;
+
+    margin: 0;
+    padding: 0;
+}
+.bx-db-menu ul.bx-menu-hor > li a.bx-btn,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor > li a.bx-btn {
+    display: inline-block;
+    float: none;
+}
+.bx-db-menu ul.bx-menu-hor > li a > span,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor > li a > span {
+    vertical-align: middle;
+}
+.bx-db-menu ul > li:not(.bx-menu-item-more-auto) a .bx-mi-icon {
+    display: none;
+}
+.bx-db-menu ul.bx-menu-submenu-more-auto {
+    line-height: normal;
+}
+.bx-db-menu ul li:not(.bx-menu-item-more-auto) a span,
+.bx-db-menu .bx-menu-item-more-popup ul.bx-menu-custom.bx-menu-submenu-more-auto li:not(.bx-menu-item-more-auto) a span,
+.bx-db-menu .bx-popup .bx-db-menu-popup ul.bx-menu-custom.bx-menu-custom-ver li a > span,
+.bx-db-menu .bx-popup .bx-db-menu-popup ul.bx-menu-ver li a > div > span {
+    margin: 0;
+}
+.bx-db-menu ul.bx-menu-hor li.bx-menu-tab-active,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor li.bx-menu-tab-active {
+    border-bottom: 0;
+}
+.bx-db-menu ul.bx-menu-custom-ver li.bx-menu-item-more-auto {
+    display: none;
+}
+
+
+/*--- Popup boxes ---*/
+html.bx-popup-lock,
+body.bx-popup-lock {
+    overflow: hidden;
+}
+
+.bx-popup-transitions {
+    -webkit-transition: opacity 0.2s ease-in-out, -webkit-transform 0.2s ease-in-out;
+    transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
+}
+
+.bx-popup-inactive {
+    -webkit-opacity: 0;
+    opacity: 0;
+
+    -webkit-transform: scale(0.75);
+    transform: scale(0.75);
+}
+
+.bx-popup-active {
+    max-height:100%;
+    overflow-y:auto;
+
+    -webkit-opacity: 1;
+    opacity: 1;
+
+    -webkit-transform: scale(1);
+    transform: scale(1);
+
+    -webkit-transition: opacity 0.2s ease-in-out, -webkit-transform 0.2s ease-in-out;
+    transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
+}
+.bx-popup-fog {
+    position: fixed;
+    top: 0;
+    left: 0;
+    -webkit-backface-visibility: hidden;
+}
+.bx-popup-menu .bx-popup-close-wrapper {
+    display: none;
+}
+.bx-popup-menu .bx-popup-content-indent {
+    padding: 0px !important;
+}
+.bx-popup-menu ul.bx-menu-ver,
+.bx-popup-menu ul.bx-menu-verlit,
+.bx-popup-menu ul.bx-menu-custom-ver,
+.bx-popup-menu.bx-popup-menu-main.sys-add-content ul {
+    margin-top: 0.5rem;
+    margin-bottom: 0.5rem;
+}
+
+
+/*--- Loading indicator ---*/
+.bx-loading-ajax {
+    position: absolute;
+    width: 6rem;
+    height: 6rem;
+    margin-left: auto;
+    margin-right: auto;
+
+    background-color: transparent;
+
+    border-radius: 0.5rem;
+    -moz-border-radius: 0.5rem;
+    -webkit-border-radius: 0.5rem;
+
+    text-align: center;
+    color: #CCCCCC;
+}
+.bx-loading-ajax.static {
+    position: static;
+}
+.bx-loading-ajax-btn {
+    position: relative;
+    display: inline-block;
+    vertical-align: top;
+
+    min-width: 2.0rem;
+
+    margin-left: 0.25rem;
+}
+
+button.bx-btn:focus,
+button.bx-btn:focus .bx-loading-ajax-btn {
+    outline: 0;	
+}
+
+/*-- Show More --*/
+.bx-overflow-ready {
+    max-height: 16rem;
+    overflow: hidden;
+}
+.bx-overflow-ready.bx-or-cmt {
+    max-height: 5rem;
+}
+.bx-overflow:before {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+    height: 20%;
+
+    content: '';
+}
+
+.bx-show-more {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+
+    text-align: center
+}
+
+/*-- Flickity: Tailwind overwrite --*/
+.flickity-enabled .flickity-viewport .flickity-slider img,
+.flickity-enabled .flickity-viewport .flickity-slider video {
+    display: inline;
+    max-width: none;
+}
+
+.bx-popup-element-close:hover,
+.bx-popup-element-close {
+    text-decoration: none;
+    font-size: 1.5rem !important;
+}

+ 210 - 0
modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/css/menu.css

@@ -0,0 +1,210 @@
+/*--
+import url(../../../../../../../template/css/menu.css);
+--*/
+
+ul.bx-menu-hor,
+ul.bx-menu-custom-hor,
+ul.bx-menu-notif,
+ul.bx-menu-verlit,
+ul.bx-menu-ver,
+ul.bx-menu-custom-ver,
+ul.bx-menu-main,
+ul.bx-menu-main-submenu,
+ul.bx-menu-toolbar,
+ul.bx-menu-floating-blocks-wide,
+ul.bx-menu-floating-blocks-big,
+ul.bx-menu-floating-blocks-dash,
+ul.bx-menu-floating-blocks,
+ul.bx-menu-inter,
+ul.bx-menu-inter-vert,
+ul.bx-menu-btns-ver,
+ul.bx-menu-btns-hor,
+ul.bx-menu-btns-icon-hor {
+    padding: 0;
+    margin: 0;
+    list-style: none;
+}
+
+li.bx-mi-aux {}
+li.bx-mi-hidden {
+    display: none;
+}
+
+/* --- Vertical, Vertical lite, Custom Vertical Menus ---*/
+.bx-menu-ver li,
+.bx-menu-verlit li,
+.bx-menu-custom-ver li {
+    margin-top: 1px;
+}
+.bx-menu-ver li:first-child,
+.bx-menu-verlit li:first-child,
+.bx-menu-custom-ver li:first-child {
+    margin-top: 0px;
+}
+
+/*--- Vertical, Vertical lite, Custom Vertical Menus (in Popup) ---*/
+.bx-popup .bx-menu-custom-ver li > span {
+    -webkit-flex: 1 1 auto;
+    -ms-flex: 1 1 auto;
+    flex: 1 1 auto;
+
+    display: -webkit-flex;
+    display: flex;
+    -webkit-align-items: center;
+    align-items: center;
+    -webkit-justify-content: space-between;
+    justify-content: space-between;
+}
+.bx-popup .bx-menu-custom-ver li > span .bx-menu-addon {
+    padding-right: 1.0rem;
+}
+
+/* --- Horizontal, Custom Horizontal Menus --- */
+.bx-menu-custom .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-custom .bx-menu-item,
+.bx-menu-custom .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-custom .bx-menu-item a,
+.bx-menu-custom .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-custom .bx-menu-item > span {
+    border-radius: 0;
+}
+
+/*--- Interactive Menu ---*/
+.bx-menu-inter li.bx-menu-inter-hidden,
+.bx-menu-inter-vert li.bx-menu-inter-hidden {
+    display: none;
+}
+
+/*--- Custom Menu with More Auto ---*/
+.bx-menu-main-submenu.bx-menu-more-auto .bx-menu-item.bx-menu-item-more-auto,
+.bx-menu-custom.bx-menu-custom-hor.bx-menu-more-auto .bx-menu-item.bx-menu-item-more-auto {
+    display: none;
+}
+.bx-menu-more-auto .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-item a.p-2,
+.bx-menu-more-auto .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-item a.px-2 {
+    padding-left: 1rem;
+    padding-right: 1rem;
+}
+
+/*--- Page Main menu ---*/
+.bx-menu-main .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-custom .bx-menu-item,
+.bx-menu-main .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-custom .bx-menu-item a {
+    border-radius: 0;
+}
+
+/*--- Page Submenu ---*/
+.bx-menu-main-submenu {
+    -webkit-justify-content: center;
+    justify-content: center;
+}
+.bx-menu-main-submenu .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-custom .bx-menu-item > a {
+    width: 100%;
+    padding: 0.5rem 1.0rem; 
+    text-align: left;
+}
+.bx-menu-main-submenu .bx-menu-item.bx-menu-item-more-auto .bx-popup .bx-menu-custom .bx-menu-item .bx-mi-underline {
+    display: none;
+}
+
+/*--- Toolbar Menu ---*/
+.bx-menu-toolbar .bx-menu-toolbar-item .bx-menu-item-addon {
+    position: absolute;
+    top: -0.375rem;
+    right: -0.375rem;
+    padding: 0 0.25rem;
+}
+.bx-menu-toolbar .bx-menu-toolbar-item .bx-menu-toolbar-item-unit .bx-base-pofile-unit-online {
+    display: none;
+}
+
+/*--- Account Popup ---*/
+#bx-sliding-menu-account .bx-menu-account-popup .bx-menu-item-addon.bx-def-unit-alert {
+    position: relative;
+    padding: 0 0.25rem;
+}
+#bx-sliding-menu-account .bx-menu-account-popup-profile-create {
+    display: -webkit-flex;
+    display: flex;
+    -webkit-justify-content: center;
+    justify-content: center;
+}
+
+/*--- Add Popup ---*/
+div.bx-popup-menu-main.sys-add-content ul {
+    flex-direction: column;
+}
+
+/*--- Pannel Menu ---*/
+.bx-menu-panel {}
+
+.bx-main-column.bx-mc-narrowed .bx-menu-panel .bx-menu-panel-item .bx-mpi-toggle-hidden {
+    position: absolute;
+    display: none;
+}
+
+.bx-mc-narrowed .bx-menu-panel li .bx-stl-mil {
+    -webkit-justify-content: center;
+    justify-content: center;
+
+    padding-left: 0;
+    padding-right: 0;
+}
+.bx-mc-narrowed .bx-menu-panel li .bx-stl-mii {
+    width: 100%;
+    text-align: center;
+}
+.bx-mc-narrowed .bx-menu-panel li .bx-stl-mii,
+.bx-mc-narrowed .bx-menu-panel li .bx-stl-mii > * {
+    margin: 0;
+}
+.bx-mc-narrowed .bx-menu-panel .bx-def-thumb-size {
+    width: 2rem;
+    height: 2rem;
+}
+.bx-mc-narrowed .bx-menu-panel .bx-profile-avatar-cnt {
+    -webkit-justify-content: center;
+    justify-content: center;
+}
+
+/*--- Footer Menu ---*/
+#bx-menu-footer.bx-menu-hor > li.bx-menu-item-powered_by .sys-icon-svg {
+    width: auto;
+}
+
+/*--- Profile Stats/Followings Menu ---*/
+.bx-db-content .bx-profile-stats .bx-profile-avatar-cnt,
+.bx-db-content .bx-profile-stats .bx-menu-vertical-more-less,
+.bx-db-container:not(.bx-dbc-nopadding) .bx-menu-vertical-more-less,
+.bx-db-container:not(.bx-dbc-nopadding) .bx-menu-profile-followings {
+    margin: -0.75rem;
+}
+.bx-db-container:not(.bx-dbc-notitle) .bx-menu-vertical-more-less,
+.bx-db-container:not(.bx-dbc-notitle) .bx-menu-profile-followings {
+    margin-top: 0;
+}
+
+/*--- Profile Followings Menu ---*/
+.bx-menu-ver.bx-menu-object-sys_profile_followings li .bx-menu-show {
+    display: none;
+}
+.bx-menu-ver.bx-menu-object-sys_profile_followings li .bx-menu-hide {
+    display: inline;
+}
+.bx-menu-ver.bx-menu-object-sys_profile_followings li.bx-mi-collapsed .bx-menu-show {
+    display: inline;
+}
+.bx-menu-ver.bx-menu-object-sys_profile_followings li.bx-mi-collapsed .bx-menu-hide {
+    display: none;
+}
+.bx-menu-ver.bx-menu-object-sys_profile_followings li.bx-mi-collapsed .bx-menu-item-subitems {
+    display: none;
+}
+
+/*--- Profiles meta menu ---*/
+.sys-action-counter-icon .sys-icon{
+    width: 1rem;
+    height: 1rem;
+    font-size: 0.75rem;
+    line-height: 1.1rem;
+    text-align: center;
+}
+.sys-action-counter-icon .sys-icon.sys-icon-emoji{
+    line-height: 1rem;
+}

+ 25 - 0
modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/menu_profile_followings.html

@@ -0,0 +1,25 @@
+<ul class="bx-menu-profile-followings bx-menu-ver bx-menu-object-__object__ space-y-2">
+    <bx_repeat:menu_items>
+        <li class="bx-menu-item bx-menu-item-__name__ __class_add__">
+            <div class="bx-menu-item-cnt">
+                <div class="bx-menu-item-link bx-stl-mil-wo-gaps">
+                    <a class="bx-stl-mil-gaps flex flex-auto items-center" href="__link__" <bx_if:onclick>onclick="__onclick__"</bx_if:onclick> __attrs__>
+                        <span class="bx-menu-item-icon bx-stl-mii flex-none group-hover:text-gray-700 dark:group-hover:text-gray-300">
+                            <bx_include_auto:menu_icon.html />
+                        </span>
+                        <span class="bx-menu-item-title bx-stl-mit flex-1 uppercase" sidebar-toggle-item>__title__</span>
+                        <span class="bx-menu-collapse flex-none pl-2" sidebar-toggle-item>
+                            <i class="sys-icon chevron-up bx-menu-hide"></i>
+                            <i class="sys-icon chevron-down bx-menu-show"></i>
+                        </span>
+                    </a>
+                </div>
+                <bx_if:show_subitems>
+                    <div class="bx-menu-item-subitems pt-2">
+                        <ul class="bx-menu-item-subitems bx-menu-pf-subitems bx-menu-ver space-y-2">__subitems__</ul>
+                    </div>
+                </bx_if:show_subitems>
+            </div>
+        </li>
+    </bx_repeat:menu_items>
+</ul>

+ 17 - 0
modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/menu_profile_followings_subitems.html

@@ -0,0 +1,17 @@
+<bx_repeat:menu_items>
+    <li class="bx-menu-subitem bx-menu-subitem-__name__ __class_add__">
+        <div class="bx-menu-item-cnt bx-stl-mil-wo-gaps">
+            <a class="bx-stl-mil-gaps group flex flex-auto items-center truncate" href="__link__" <bx_if:onclick>onclick="__onclick__"</bx_if:onclick> __attrs__>
+                <span class="bx-menu-item-icon bx-stl-mii flex-none group-hover:text-gray-700 dark:group-hover:text-gray-300">
+                    <bx_include_auto:menu_icon.html />
+                </span>
+                <span class="bx-menu-item-title bx-stl-mit flex-1" sidebar-toggle-item>__title__</span>
+                <bx_if:addon>
+                    <div class="bx-menu-addon bx-stl-mia flex-none group-hover:text-gray-800 dark:group-hover:text-gray-100">
+                        <div class="bx-menu-addon-cnt bx-def-label menu">__addon__</div>
+                    </div>
+                </bx_if:addon>
+            </a>
+        </div>
+    </li>
+</bx_repeat:menu_items>

+ 116 - 0
modules/boonex/artificer/updates/13.0.13_13.0.14/source/data/template/system/pt_application.html

@@ -0,0 +1,116 @@
+<bx_include_auto:_header.html />
+<div class="bx-main bx-pt-application <bx_injection:injection_main_class />">
+    <bx_injection:top />
+    <!-- BEG: Navigation -->
+    <nav class="fixed z-30 w-full bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
+        <div class="px-3 py-3 lg:px-5 lg:pl-3">
+            <div class="flex items-center justify-between">
+                <div class="flex flex-1 items-center justify-start">
+                    <!-- Sidebar Toggler -->
+                    <button id="toggleSidebar" aria-expanded="true" aria-controls="sidebar" class="hidden p-2 mr-6 text-gray-600 rounded cursor-pointer xl:inline hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-700">
+                      <svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h6a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path></svg>
+                    </button>
+                    <button id="toggleSidebarMobile" aria-expanded="true" aria-controls="sidebar" class="p-2 mr-2 text-gray-600 rounded cursor-pointer xl:hidden hover:text-gray-900 hover:bg-gray-100 focus:bg-gray-100 dark:focus:bg-gray-700 focus:ring-2 focus:ring-gray-100 dark:focus:ring-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
+                      <svg id="toggleSidebarMobileHamburger" class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h6a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path></svg>
+                      <svg id="toggleSidebarMobileClose" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
+                    </button>
+                    <div class="bx-toolbar-items-group relative flex flex-1 items-center justify-start">
+                        <!-- Logo -->
+                        <div id="bx-logo-container" class="bx-toolbar-item bx-ti-left bx-ti-logo flex-none items-center">
+                            <bx_injection:injection_logo_before />
+                            __main_logo__
+                            <bx_injection:injection_logo_after />
+                        </div>
+                        <!-- Menu -->
+                        <div class="bx-toolbar-item bx-ti-menu flex-2 hidden xl:block w-64 ml-4">
+                            <div id="bx-menu-toolbar-1-container" class="bx-toolbar-item bx-ti-menu w-full">
+                                <bx_menu:sys_application />
+                            </div>
+                        </div>
+                        <!-- Search -->
+                        <div class="bx-toolbar-item bx-ti-search flex flex-1 items-center justify-end">
+                            <div class="bx-ti-search-box flex items-start md:min-w-88 mr-2 bg-white dark:bg-gray-800 rounded-md shadow-lg xl:shadow-none hidden xl:block">
+                                <div class="relative w-full">__sys_site_search__</div>
+                            </div>
+                            <div class="bx-ti-search-button group relative inline-flex xl:hidden items-center mx-2 text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 text-base font-medium leading-6 border border-transparent hover:border-gray-200/50 active:border-gray-300 dark:hover:border-gray-700/50 dark:active:border-gray-700 bg-gray-100 dark:bg-gray-700/50 active:bg-gray-300 dark:active:bg-black/50 hover:bg-gray-200/50 dark:hover:bg-gray-700/50 rounded-full">
+                                <a href="javascript:void(0)" onclick="javascript:bx_site_search_show(this)">
+                                    <span class="relative flex items-center justify-center h-8 w-8 md:h-10 md:w-10">
+                                        <bx_image_auto:search />
+                                    </span>
+                                </a>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="flex items-center">
+                    <!-- Members Toolbar -->
+                    <bx_menu:sys_toolbar_member />
+                </div>
+            </div>
+        </div>
+    </nav>
+    <!-- END: Navigation -->
+
+    <div class="bx-main-content flex pt-16 overflow-hidden">
+        <!-- BEG: Sidebar -->
+        <aside id="sidebar" class="bx-main-column narrow fixed top-0 left-0 z-20 hidden xl:flex flex-col flex-shrink-0 w-64 h-full pt-16 duration-75 transition-width" aria-label="Sidebar">
+            <div class="bx-mc-border relative flex flex-col flex-1 min-h-0 max-h-full h-full pt-0 bg-white border-r border-gray-200 dark:bg-gray-800 dark:border-gray-700">
+                <div class="flex flex-col flex-1 py-2 overflow-y-auto">
+                    <div class="bx-mc-cnt flex-1 px-4 space-y-1 bg-white divide-y divide-gray-200 dark:bg-gray-800 dark:divide-gray-700">
+                        <bx_menu:sys_site_panel />
+                    </div>
+                </div>
+                <div class="absolute bottom-0 left-0 justify-center hidden w-full p-4 space-x-4 xl:flex" sidebar-bottom-menu>
+                    <!-- Some button based menu may be here -->
+                </div>
+            </div>
+        </aside>
+        <div class="fixed inset-0 z-10 hidden bg-gray-900/50 dark:bg-gray-900/90" id="sidebarBackdrop"></div>
+        <!-- END: Sidebar -->    
+
+        <!-- BEG: Main Content -->
+        <div id="main-content" class="bx-main-column wide relative w-full h-full overflow-y-auto xl:ml-64">
+            <main>
+                <bx_injection:injection_between_cover_logo />
+                __cover__
+                <bx_injection:injection_between_cover_top_menu />
+                <div id="bx-menu-main-bar-wrapper" class="bx-menu-main-bar-wrapper bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 __site_submenu_hidden__">
+                    <div id="bx-menu-main-bar" class="bx-menu-main-bar __site_submenu_class__ mx-auto px-4 sm:px-6 lg:px-8 box-border">
+                        <div class="bx-menu-main-bar-cnt relative -mb-px">
+                            <div class="absolute w-full text-center">
+                                <bx_injection:injection_top_menu_before />
+                                <bx_menu:sys_site_submenu />
+                                <bx_injection:injection_top_menu_after />
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <bx_injection:injection_between_content_menu />
+                <div id="bx-content-wrapper" class="bx-def-page-width mx-auto">
+                    __informer__
+                    <div class="bx-page-wrapper mx-auto px-3 md:px-4 lg:px-6 box-border">
+                        <div id="bx-content-container" class="bx-content-container">
+                            <div id="bx-content-main" class="bx-content-main my-2 lg:my-3">
+                                <bx_injection:injection_content_before />
+                                __page_main_code__
+                                <bx_injection:injection_content_after />
+                                <bx_injection:banner_bottom />
+                            </div> <!-- #bx-content-main -->
+                            <bx_injection:injection_between_content_footer />
+                        </div> <!-- #bx-content-container -->
+                    </div> <!-- .bx-page-wrapper -->
+                </div> <!-- #bx-content-wrapper -->
+            </main>
+            <footer class="p-4 m-4 bg-white rounded-lg shadow md:flex md:items-center md:justify-between md:p-6 xl:p-8 dark:bg-gray-800">
+                <bx_injection:injection_footer_before />
+                <div id="bx-menu-bottom">
+                    <bx_menu:sys_footer />
+                </div>
+                <bx_injection:injection_footer_after />
+            </footer>
+        </div>
+        <!-- END: Main Content -->
+    </div>
+    <bx_menu:sys_site_action />
+</div> <!-- .bx-main -->
+<bx_include_auto:_footer.html />

+ 68 - 0
modules/boonex/artificer/updates/13.0.13_13.0.14/source/install/config.php

@@ -0,0 +1,68 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Artificer Artificer template
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_TEMPLATE,
+    'name' => 'bx_artificer',
+    'title' => 'Artificer',
+    'note' => 'Design template',
+    'version' => '13.0.14',
+    'vendor' => 'Boonex',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/artificer/',
+    'home_uri' => 'artificer',
+
+    'db_prefix' => 'bx_artificer_',
+    'class_prefix' => 'BxArtificer',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Boonex Artificer 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(),
+);
+
+/** @} */

+ 133 - 0
modules/boonex/artificer/updates/13.0.13_13.0.14/source/js/sidebar.js

@@ -0,0 +1,133 @@
+document.addEventListener("DOMContentLoaded", function(event) {
+    const isSidebarExpanded = toggleSidebarEl => {
+        return toggleSidebarEl.getAttribute('aria-expanded') === 'true' ? true : false;
+    }
+
+    const toggleSidebar = (sidebarEl, expand, setExpanded = false) => {
+        const bottomMenuEl = document.querySelector('[sidebar-bottom-menu]');
+        const mainContentEl = document.getElementById('main-content');
+        if (expand) {
+            sidebarEl.classList.remove('bx-mc-narrowed');
+            sidebarEl.classList.add('bx-mc-expanded');
+            sidebarEl.classList.add('xl:w-64');
+            sidebarEl.classList.remove('xl:w-16');
+            mainContentEl.classList.add('xl:ml-64');
+            mainContentEl.classList.remove('xl:ml-16');
+
+            document.querySelectorAll('#' + sidebarEl.getAttribute('id') + ' [sidebar-toggle-item]').forEach(sidebarToggleEl => {
+                sidebarToggleEl.classList.remove('xl:hidden');
+                sidebarToggleEl.classList.remove('xl:absolute');
+                sidebarToggleEl.classList.remove('bx-mpi-toggle-hidden');
+            });
+
+            // toggle multi level menu item initial and full text
+            document.querySelectorAll('#' + sidebar.getAttribute('id') + ' ul > li > ul > li > a').forEach(e => {
+                e.classList.add('pl-11');
+                e.classList.remove('px-4');
+                e.childNodes[0].classList.remove('hidden');
+                e.childNodes[1].classList.add('hidden');
+            });
+
+            bottomMenuEl.classList.remove('flex-col', 'space-y-4', 'p-2');
+            bottomMenuEl.classList.add('space-x-4', 'p-4');
+            setExpanded ? toggleSidebarEl.setAttribute('aria-expanded', 'true') : null;
+        } 
+        else {
+            sidebarEl.classList.add('bx-mc-narrowed');
+            sidebarEl.classList.remove('bx-mc-expanded');
+            sidebarEl.classList.remove('xl:w-64');
+            sidebarEl.classList.add('xl:w-16');
+            mainContentEl.classList.remove('xl:ml-64');
+            mainContentEl.classList.add('xl:ml-16');
+            document.querySelectorAll('#' + sidebarEl.getAttribute('id') + ' [sidebar-toggle-item]').forEach(sidebarToggleEl => {
+                sidebarToggleEl.classList.add('xl:hidden');
+                sidebarToggleEl.classList.add('xl:absolute');
+                sidebarToggleEl.classList.add('bx-mpi-toggle-hidden');
+            });
+
+            // toggle multi level menu item initial and full text
+            document.querySelectorAll('#' + sidebar.getAttribute('id') + ' ul > li > ul > li > a').forEach(e => {
+                e.classList.remove('pl-11');
+                e.classList.add('px-4');
+                e.childNodes[0].classList.add('hidden');
+                e.childNodes[1].classList.remove('hidden');
+            });
+
+            bottomMenuEl.classList.add('flex-col', 'space-y-4', 'p-2');
+            bottomMenuEl.classList.remove('space-x-4', 'p-4');
+            setExpanded ? toggleSidebarEl.setAttribute('aria-expanded', 'false') : null;
+        }
+    }
+
+    const toggleSidebarMobile = (sidebar, sidebarBackdrop, toggleSidebarMobileHamburger, toggleSidebarMobileClose) => {
+        sidebar.classList.remove('bx-mc-narrowed');
+        sidebar.classList.add('bx-mc-expanded');
+        sidebar.classList.toggle('hidden');
+        sidebarBackdrop.classList.toggle('hidden');
+        toggleSidebarMobileHamburger.classList.toggle('hidden');
+        toggleSidebarMobileClose.classList.toggle('hidden');
+    }
+
+
+    const sidebar = document.getElementById('sidebar');
+    const toggleSidebarEl = document.getElementById('toggleSidebar');
+    if(sidebar) {
+        document.querySelectorAll('#' + sidebar.getAttribute('id') + ' ul > li > ul > li > a').forEach(e => {
+            var fullText = e.textContent;
+            var firstLetter = fullText.substring(0, 1);
+
+            var fullTextEl = document.createElement('span');
+            var firstLetterEl = document.createElement('span');
+            firstLetterEl.classList.add('hidden');
+            fullTextEl.textContent = fullText;
+            firstLetterEl.textContent = firstLetter;
+
+            e.textContent = '';
+            e.appendChild(fullTextEl);
+            e.appendChild(firstLetterEl);
+        });
+
+        // initialize sidebar
+        if (localStorage.getItem('sidebarExpanded') !== null) {
+            if (localStorage.getItem('sidebarExpanded') === 'true') {
+                toggleSidebar(sidebar, true, false);
+            } else {
+                toggleSidebar(sidebar, false, true);
+            }
+        }
+
+        if(toggleSidebarEl) {
+            toggleSidebarEl.addEventListener('click', () => {
+                localStorage.setItem('sidebarExpanded', !isSidebarExpanded(toggleSidebarEl));
+                toggleSidebar(sidebar, !isSidebarExpanded(toggleSidebarEl), true);
+            });
+
+            sidebar.addEventListener('mouseenter', () => {
+                if (!isSidebarExpanded(toggleSidebarEl)) {
+                    toggleSidebar(sidebar, true);
+                }
+            });
+
+            sidebar.addEventListener('mouseleave', () => {
+                if (!isSidebarExpanded(toggleSidebarEl)) {
+                    toggleSidebar(sidebar, false);
+                }
+            });
+        }    
+    }
+
+    const toggleSidebarMobileEl = document.getElementById('toggleSidebarMobile');
+    if(toggleSidebarMobileEl) {
+        const sidebarBackdrop = document.getElementById('sidebarBackdrop');
+        const toggleSidebarMobileHamburger = document.getElementById('toggleSidebarMobileHamburger');
+        const toggleSidebarMobileClose = document.getElementById('toggleSidebarMobileClose');
+
+        toggleSidebarMobileEl.addEventListener('click', () => {
+            toggleSidebarMobile(sidebar, sidebarBackdrop, toggleSidebarMobileHamburger, toggleSidebarMobileClose);
+        });
+
+        sidebarBackdrop.addEventListener('click', () => {
+            toggleSidebarMobile(sidebar, sidebarBackdrop, toggleSidebarMobileHamburger, toggleSidebarMobileClose);
+        });
+    }
+});

+ 51 - 0
modules/boonex/attendant/updates/13.0.4_13.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' => 'Attendant',
+    'version_from' => '13.0.4',
+    'version_to' => '13.0.5',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/attendant/updates/update_13.0.4_13.0.5/',
+    'home_uri' => 'attendant_update_1304_1305',
+
+    'module_dir' => 'boonex/attendant/',
+    'module_uri' => 'attendant',
+
+    'db_prefix' => 'bx_attendant_',
+    'class_prefix' => 'BxAttendant',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Attendant',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/attendant/updates/13.0.4_13.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 BxAttendantUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 93 - 0
modules/boonex/attendant/updates/13.0.4_13.0.5/source/classes/BxAttendantDb.php

@@ -0,0 +1,93 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Attendant Attendant
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+bx_import('BxDolModuleDb');
+
+class BxAttendantDb extends BxBaseModGeneralDb
+{
+    protected $_sTableEntries;
+
+    public function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+
+        $this->_oConfig = $oConfig;
+        $CNF = $oConfig->CNF;
+        $this->_sTableEntries = $CNF['TABLE_ENTRIES'];
+    }
+    
+    public function getEvents($aParams, $bReturnCount = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        
+        $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
+        $sSelectClause = $sJoinClause = $sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        $sSelectClause = "`{$this->_sTableEntries}`.*";
+
+        switch($aParams['type']) {
+            case 'active_by_action_and_profile_id':
+                $aMethod['name'] = 'getAll';
+                $aMethod['params'][1] = array(
+                    'action' => $aParams['action'],
+                    'profile_id' => $aParams['profile_id']
+                );
+                $sWhereClause = "AND `{$this->_sTableEntries}`.`" . $CNF['FIELD_ACTION'] . "` =:action  AND `{$this->_sTableEntries}`.`" . $CNF['FIELD_PROFILE_ID'] . "` =:profile_id AND `" . $CNF['FIELD_PROCESSED'] . "` IS NULL";
+                break;
+            case 'all':
+                $aMethod['name'] = 'getAll';
+                $aMethod['params'][1] = array(
+                    'request_id' => $aParams['value']
+                );
+                break;
+        }
+
+        $sSql = "SELECT {select} FROM `{$this->_sTableEntries}` " . $sJoinClause . " WHERE 1 " . $sWhereClause . " {order} {limit}";
+        
+        $aMethod['params'][0] = str_replace(array('{select}', '{order}', '{limit}'), array($sSelectClause, $sOrderClause, $sLimitClause), $sSql);
+        $aEntries = call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+        
+        if(!$bReturnCount)
+            return $aEntries;
+
+        $aMethod['name'] = 'getOne';
+        $aMethod['params'][0] = str_replace(array('{select}', '{order}', '{limit}'), array("COUNT(*)", "", ""), $sSql);
+
+        return array($aEntries, (int)call_user_func_array(array($this, $aMethod['name']), $aMethod['params']));
+    }
+    
+    public function addEvent($sAction, $iObjectId, $sModule, $sEvent, $iProfileId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $aBindings = array(
+            'module' => $sModule,
+            'event' => $sEvent,
+            'action' => $sAction,
+            'added' => time(),
+            'object_id' => $iObjectId,
+            'profile_id' => $iProfileId,
+            'event' => $sEvent
+        );
+        
+        $this->query("INSERT INTO `" . $CNF['TABLE_ENTRIES'] . "` (`" . $CNF['FIELD_MODULE'] . "`, `" . $CNF['FIELD_EVENT'] . "`, `" . $CNF['FIELD_ACTION'] . "`, `" . $CNF['FIELD_ADDED'] . "`, `" . $CNF['FIELD_OBJECT_ID'] . "`, `" . $CNF['FIELD_PROFILE_ID'] . "`) values (:module, :event, :action, :added, :object_id, :profile_id)", $aBindings);
+    }
+    
+    public function setEventProcessed($id)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $aBindings = array(
+            'processed' => time()
+        );
+       $this->query("UPDATE `" . $CNF['TABLE_ENTRIES'] . "` SET   `" . $CNF['FIELD_PROCESSED'] . "` = :processed", $aBindings);
+    }
+}
+
+/** @} */

+ 77 - 0
modules/boonex/attendant/updates/13.0.4_13.0.5/source/install/config.php

@@ -0,0 +1,77 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Attendant Attendant
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_attendant',
+    'title' => 'Attendant',
+    'note' => 'Attendant module.',
+    'version' => '13.0.5',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/attendant/',
+    'home_uri' => 'attendant',
+
+    'db_prefix' => 'bx_attendant_',
+    'class_prefix' => 'BxAttendant',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Attendant',
+
+    /**
+     * 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,
+    ),
+    'enable_success' => array(
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/developer/updates/13.0.5_13.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' => 'Developer',
+    'version_from' => '13.0.5',
+    'version_to' => '13.0.6',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/developer/updates/update_13.0.5_13.0.6/',
+    'home_uri' => 'developer_update_1305_1306',
+
+    'module_dir' => 'boonex/developer/',
+    'module_uri' => 'developer',
+
+    'db_prefix' => 'bx_dev_',
+    'class_prefix' => 'BxDev',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'BoonEx Developer',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/developer/updates/13.0.5_13.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 BxDevUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 11 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/install/langs/en.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string_add name="_bx_dev_nav_txt_sys_items_primary"><![CDATA[Primary]]></string_add>
+    <string_add name="_bx_dev_nav_txt_items_primary"><![CDATA[Primary]]></string_add>
+    <string_add name="_bx_dev_nav_txt_sys_items_collapsed"><![CDATA[Collapsed]]></string_add>
+    <string_add name="_bx_dev_nav_txt_items_collapsed"><![CDATA[Collapsed]]></string_add>
+	<string_add name="_bx_dev_nav_txt_sys_items_active_api"><![CDATA[Active (for App)]]></string_add>
+    <string_add name="_bx_dev_nav_txt_items_active_api"><![CDATA[Active (for App)]]></string_add>
+	<string_add name="_bx_dev_bp_txt_sys_block_active_api"><![CDATA[Active (for App)]]></string_add>
+    <string_add name="_bx_dev_bp_txt_block_active_api"><![CDATA[Active (for App)]]></string_add>
+</resources>

+ 29 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/install/sql/install.sql

@@ -0,0 +1,29 @@
+SET @sName = 'bx_developer';
+
+
+-- FORMS
+DELETE FROM `sys_form_inputs` WHERE `object`='bx_developer_nav_item' AND `name`IN ('primary', 'collapsed', 'active_api');
+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_developer_nav_item', @sName, 'primary', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_primary', '_bx_dev_nav_txt_items_primary', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'collapsed', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_collapsed', '_bx_dev_nav_txt_items_collapsed', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'active_api', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_active_api', '_bx_dev_nav_txt_items_active_api', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0);
+
+DELETE FROM `sys_form_display_inputs` WHERE `display_name`='bx_developer_nav_item_add' AND input_name IN ('primary', 'collapsed', 'active_api');
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_nav_item_add', 'primary', 2147483647, 1, 17),
+('bx_developer_nav_item_add', 'collapsed', 2147483647, 1, 17),
+('bx_developer_nav_item_add', 'active_api', 2147483647, 1, 18);
+
+DELETE FROM `sys_form_display_inputs` WHERE `display_name`='bx_developer_nav_item_edit' AND input_name IN ('primary', 'collapsed', 'active_api');
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_nav_item_edit', 'primary', 2147483647, 1, 18),
+('bx_developer_nav_item_edit', 'collapsed', 2147483647, 1, 18),
+('bx_developer_nav_item_edit', 'active_api', 2147483647, 1, 19);
+
+DELETE FROM `sys_form_inputs` WHERE `object`='bx_developer_bp_block' AND `name`='active_api';
+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_developer_bp_block', @sName, 'active_api', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_active_api', '_bx_dev_bp_txt_block_active_api', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0);
+
+DELETE FROM `sys_form_display_inputs` WHERE `display_name`='bx_developer_bp_block_edit' AND `input_name`='active_api';
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_bp_block_edit', 'active_api', 2147483647, 1, 16);

+ 69 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/install/config.php

@@ -0,0 +1,69 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Developer Developer
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_developer',
+    'title' => 'Developer',
+    'note' => 'Developer tools...',
+    'version' => '13.0.6',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/developer/',
+    'home_uri' => 'developer',
+
+    'db_prefix' => 'bx_dev_',
+    'class_prefix' => 'BxDev',
+
+	/**
+     * Category for language keys.
+     */
+    'language_category' => 'BoonEx Developer',
+
+    /**
+     * 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(),
+
+);
+
+/** @} */

+ 529 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/install/langs/en.xml

@@ -0,0 +1,529 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+    <string name="_bx_developer"><![CDATA[Developer]]></string>
+
+    <string name="_bx_dev_adm_wgt_cpt"><![CDATA[Developer]]></string>
+    <string name="_bx_dev_adm_page_cpt"><![CDATA[Developer]]></string>
+    
+    <!-- Forms Builder -->
+    <string name="_bx_dev_frm_txt_forms_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_forms_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_forms_gl_displays"><![CDATA[Displays]]></string>
+    <string name="_bx_dev_frm_btn_forms_gl_export"><![CDATA[Export Form]]></string>
+    <string name="_bx_dev_frm_txt_displays_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_displays_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_displays_gl_form"><![CDATA[Form]]></string>
+    <string name="_bx_dev_frm_txt_displays_gl_fields"><![CDATA[Fields]]></string>
+    <string name="_bx_dev_frm_txt_fields_gl_type"><![CDATA[Type]]></string>
+    <string name="_bx_dev_frm_txt_fields_gl_caption_system"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_fields_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_fields_gl_visible"><![CDATA[Visible For]]></string>
+    <string name="_bx_dev_frm_txt_pre_lists_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_pre_lists_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_pre_lists_gl_values"><![CDATA[Values]]></string>
+    <string name="_bx_dev_frm_txt_pre_lists_gl_use_for_sets"><![CDATA[Use For Sets]]></string>
+    <string name="_bx_dev_frm_txt_pre_values_gl_lkey"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_gl_fields"><![CDATA[Fields]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_fields_gl_type"><![CDATA[Type]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_fields_gl_caption"><![CDATA[Caption]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_fields_gl_search_type"><![CDATA[Search Type]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_fields_gl_search_operator"><![CDATA[Search Operator]]></string>
+    <string name="_bx_dev_frm_btn_forms_gl_create"><![CDATA[Add New Form]]></string>
+    <string name="_bx_dev_frm_btn_displays_gl_create"><![CDATA[Add New Display]]></string>
+    <string name="_bx_dev_frm_btn_fields_create"><![CDATA[Add New Field]]></string>
+    <string name="_bx_dev_frm_btn_pre_lists_create"><![CDATA[Add New List]]></string>
+    <string name="_bx_dev_frm_btn_pre_values_create"><![CDATA[Add New Item]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_create"><![CDATA[Add New Search]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_edit"><![CDATA[Edit search form]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_fields_edit"><![CDATA[Edit search field]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_fields_reset"><![CDATA[Reset]]></string>
+    <string name="_bx_dev_frm_txt_select_module"><![CDATA[Select Module...]]></string>
+    <string name="_bx_dev_frm_txt_select_object"><![CDATA[Select Object...]]></string>
+    <string name="_bx_dev_frm_txt_select_object_content_info"><![CDATA[Select Content Info Object...]]></string>
+    <string name="_bx_dev_frm_txt_forms_create_popup"><![CDATA[Add New Form]]></string>
+    <string name="_bx_dev_frm_txt_forms_edit_popup"><![CDATA[Edit "{0}" Form]]></string>
+    <string name="_bx_dev_frm_txt_forms_export_popup"><![CDATA[Export "{0}" Form]]></string>
+    <string name="_bx_dev_frm_err_forms_create"><![CDATA[Cannot add form]]></string>
+    <string name="_bx_dev_frm_err_forms_edit"><![CDATA[Cannot update form]]></string>
+    <string name="_bx_dev_frm_txt_forms_form"><![CDATA[Forms Builder Form]]></string>
+    <string name="_bx_dev_frm_txt_forms_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_forms_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_txt_forms_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_err_forms_object"><![CDATA[You need to enter a unique value here]]></string>    
+    <string name="_bx_dev_frm_txt_sys_forms_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_forms_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_err_forms_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_forms_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_err_forms_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_action"><![CDATA[Action]]></string>
+    <string name="_bx_dev_frm_txt_forms_action"><![CDATA[Action]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_form_attrs"><![CDATA[Form Attrs]]></string>
+    <string name="_bx_dev_frm_txt_forms_form_attrs"><![CDATA[Form Attrs]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_submit_name"><![CDATA[Submit Name]]></string>
+    <string name="_bx_dev_frm_txt_forms_submit_name"><![CDATA[Submit Name]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_table"><![CDATA[Table]]></string>
+    <string name="_bx_dev_frm_txt_forms_table"><![CDATA[Table]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_key"><![CDATA[Key Field]]></string>
+    <string name="_bx_dev_frm_txt_forms_key"><![CDATA[Key Field]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_uri"><![CDATA[URI Field]]></string>
+    <string name="_bx_dev_frm_txt_forms_uri"><![CDATA[URI Field]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_uri_title"><![CDATA[URI Source Field]]></string>
+    <string name="_bx_dev_frm_txt_forms_uri_title"><![CDATA[URI Source Field]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_params"><![CDATA[Params]]></string>
+    <string name="_bx_dev_frm_txt_forms_params"><![CDATA[Params]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_frm_txt_forms_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_frm_txt_forms_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_frm_txt_sys_forms_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_frm_txt_forms_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_frm_txt_forms_export_insert"><![CDATA[Adding to database]]></string>
+    <string name="_bx_dev_frm_txt_forms_export_delete"><![CDATA[Removing from database]]></string>
+    <string name="_bx_dev_frm_btn_sys_forms_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_forms_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_forms_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_forms_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_forms_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_forms_done"><![CDATA[Done]]></string>
+    <string name="_bx_dev_frm_txt_displays_create_popup"><![CDATA[Add New Display]]></string>
+    <string name="_bx_dev_frm_txt_displays_edit_popup"><![CDATA[Edit "{0}" Display]]></string>
+    <string name="_bx_dev_frm_err_displays_create"><![CDATA[Cannot add display]]></string>
+    <string name="_bx_dev_frm_err_displays_edit"><![CDATA[Cannot update display]]></string>
+    <string name="_bx_dev_frm_txt_displays_diplay"><![CDATA[Forms Builder Display]]></string>
+    <string name="_bx_dev_frm_txt_displays_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_displays_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_display_name"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_txt_displays_display_name"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_err_displays_display_name"><![CDATA[You need to enter a unique (in the form) value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_displays_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_err_displays_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_object"><![CDATA[Form Name]]></string>
+    <string name="_bx_dev_frm_txt_displays_object"><![CDATA[Form Name]]></string>
+    <string name="_bx_dev_frm_err_displays_object"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_displays_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_err_displays_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_displays_view_mode"><![CDATA[View Mode]]></string>
+    <string name="_bx_dev_frm_txt_displays_view_mode"><![CDATA[View Mode]]></string>
+    <string name="_bx_dev_frm_btn_sys_displays_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_displays_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_displays_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_displays_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_displays_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_txt_field_title_editable"><![CDATA[Editable]]></string>
+    <string name="_bx_dev_frm_txt_field_info_editable"><![CDATA[Make the field Editable in Studio Forms Builder]]></string>
+    <string name="_bx_dev_frm_txt_field_title_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_frm_txt_field_info_deletable"><![CDATA[Make the field Deletable in Studio Forms Builder]]></string>
+    <string name="_bx_dev_frm_txt_prelists_create_popup"><![CDATA[Add Data List]]></string>
+    <string name="_bx_dev_frm_err_prelists_create"><![CDATA[Cannot add data list]]></string>
+    <string name="_bx_dev_frm_txt_prelists_edit_popup"><![CDATA[Edit "{0}" Data List]]></string>
+    <string name="_bx_dev_frm_err_prelists_edit"><![CDATA[Cannot update data list]]></string>
+    <string name="_bx_dev_frm_txt_prelists_export_popup"><![CDATA[Export "{0}" Data List]]></string>
+    <string name="_bx_dev_frm_txt_prelists_prelist"><![CDATA[Forms Builder Data List]]></string>
+    <string name="_bx_dev_frm_txt_prelists_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_prelists_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_id"><![CDATA[id]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_key"><![CDATA[Key]]></string>
+    <string name="_bx_dev_frm_txt_prelists_key"><![CDATA[Key]]></string>
+    <string name="_bx_dev_frm_err_prelists_key"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_prelists_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_err_prelists_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_prelists_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_err_prelists_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_prelists_use_for_sets"><![CDATA[Use For Sets]]></string>
+    <string name="_bx_dev_frm_txt_prelists_use_for_sets"><![CDATA[Use For Sets]]></string>
+    <string name="_bx_dev_frm_btn_sys_prelists_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_prelists_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_prelists_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_prelists_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_prelists_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_prelists_done"><![CDATA[Done]]></string>
+    <string name="_bx_dev_frm_txt_prelists_export_insert"><![CDATA[Adding to database]]></string>
+    <string name="_bx_dev_frm_txt_prelists_export_delete"><![CDATA[Removing from database]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_create_popup"><![CDATA[Add Data Value]]></string>
+    <string name="_bx_dev_frm_err_prevalues_create"><![CDATA[Cannot add data value]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_edit_popup"><![CDATA[Edit "{0}" Data Value]]></string>
+    <string name="_bx_dev_frm_err_prevalues_edit"><![CDATA[Cannot update data value]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_prevalue"><![CDATA[Forms Builder Data Value]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_display_edit"><![CDATA[Edd]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_key"><![CDATA[List]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_key"><![CDATA[List]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_key_select"><![CDATA[Select List...]]></string>
+    <string name="_bx_dev_frm_err_prevalues_key"><![CDATA[You need to select a list]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_value"><![CDATA[Value]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_value"><![CDATA[Value]]></string>
+    <string name="_bx_dev_frm_err_prevalues_value"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_lkey"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_lkey"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_err_prevalues_lkey"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_lkey2"><![CDATA[Title Additional]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_lkey2"><![CDATA[Title Additional]]></string>
+    <string name="_bx_dev_frm_txt_sys_prevalues_data"><![CDATA[Data]]></string>
+    <string name="_bx_dev_frm_txt_prevalues_data"><![CDATA[Data]]></string>
+    <string name="_bx_dev_frm_btn_sys_prevalues_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_prevalues_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_prevalues_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_prevalues_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_prevalues_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_err_prevalues_create_forbidden"><![CDATA[This list is already used. It's impossible to add non-numeric, or less than 1, or more than {0} values in it.]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_create_popup"><![CDATA[Add New Search Form]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_edit_popup"><![CDATA[Edit "{0}" Search Form]]></string>
+    <string name="_bx_dev_frm_err_search_forms_create"><![CDATA[Cannot add search form]]></string>
+    <string name="_bx_dev_frm_err_search_forms_edit"><![CDATA[Cannot update search form]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_form"><![CDATA[Forms Builder Search Form]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_err_forms_object"><![CDATA[You need to enter a unique value here]]></string>    
+    <string name="_bx_dev_frm_txt_sys_search_forms_object_content_info"><![CDATA[Content Info object]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_object_content_info"><![CDATA[Content Info object]]></string>
+    <string name="_bx_dev_frm_err_forms_object_content_info"><![CDATA[You need to select the name of Content Info object here]]></string>    
+    <string name="_bx_dev_frm_txt_sys_search_forms_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_frm_err_forms_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_frm_err_forms_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_forms_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_frm_txt_search_forms_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_frm_btn_sys_search_forms_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_search_forms_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_search_forms_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_edit_popup"><![CDATA[Edit "{0}" Search Field]]></string>
+    <string name="_bx_dev_frm_err_search_fields_edit"><![CDATA[Cannot update search field]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_form"><![CDATA[Forms Builder Search Field]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_object"><![CDATA[Search Form]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_object"><![CDATA[Search Form]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_name"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_name"><![CDATA[Name]]></string>
+    <string name="_bx_dev_frm_err_search_fields_name"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_type"><![CDATA[Type]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_type"><![CDATA[Type]]></string>
+    <string name="_bx_dev_frm_err_search_fields_type"><![CDATA[You need to select a type]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_caption"><![CDATA[Caption]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_caption"><![CDATA[Caption]]></string>
+    <string name="_bx_dev_frm_err_search_fields_caption"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_values"><![CDATA[Values]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_values"><![CDATA[Values]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_search_type"><![CDATA[Search Type]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_search_type"><![CDATA[Search Type]]></string>
+    <string name="_bx_dev_frm_err_search_fields_search_type"><![CDATA[You need to select a search type]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_search_value"><![CDATA[Default Value]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_search_value"><![CDATA[Default Value]]></string>
+    <string name="_bx_dev_frm_txt_sys_search_fields_search_operator"><![CDATA[Operator]]></string>
+    <string name="_bx_dev_frm_txt_search_fields_search_operator"><![CDATA[Operator]]></string>
+    <string name="_bx_dev_frm_err_search_fields_search_operator"><![CDATA[You need to select an operator]]></string>
+    <string name="_bx_dev_frm_btn_search_fields_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_sys_search_fields_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_frm_btn_search_fields_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_frm_btn_sys_search_fields_cancel"><![CDATA[Cancel]]></string>
+    
+    <!-- Navigation Builder -->
+    <string name="_bx_dev_nav_txt_menus_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_nav_txt_menus_gl_set_title"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_menus_gl_items"><![CDATA[Items]]></string>
+    <string name="_bx_dev_nav_txt_menus_gl_template_title"><![CDATA[Style]]></string>
+    <string name="_bx_dev_nav_txt_menus_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_sets_gl_title"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_nav_txt_sets_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_sets_gl_items"><![CDATA[Items]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_title_system"><![CDATA[System Name]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_link"><![CDATA[URL]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_items_gl_visible"><![CDATA[Visibile For]]></string>
+    <string name="_bx_dev_nav_btn_menus_gl_create"><![CDATA[Add New Menu]]></string>
+    <string name="_bx_dev_nav_btn_menus_gl_export"><![CDATA[Export Menu]]></string>
+    <string name="_bx_dev_nav_btn_sets_gl_create"><![CDATA[Add New Set]]></string>
+    <string name="_bx_dev_nav_btn_items_gl_create"><![CDATA[Add New Item]]></string>
+    <string name="_bx_dev_nav_btn_items_gl_import"><![CDATA[Import Items]]></string>
+    <string name="_bx_dev_nav_btn_items_gl_visible"><![CDATA[Visibile For]]></string>
+    <string name="_bx_dev_nav_txt_select_module"><![CDATA[Select Module...]]></string>
+    <string name="_bx_dev_nav_txt_menus_create_popup"><![CDATA[Add New Menu]]></string>
+    <string name="_bx_dev_nav_err_menus_create"><![CDATA[Cannot add menu]]></string>
+    <string name="_bx_dev_nav_txt_menus_edit_popup"><![CDATA[Edit "{0}" Menu]]></string>
+    <string name="_bx_dev_nav_err_menus_edit"><![CDATA[Cannot update menu]]></string>
+    <string name="_bx_dev_nav_txt_menus_export_popup"><![CDATA[Export "{0}" Menu]]></string>
+    <string name="_bx_dev_nav_txt_menus_menu"><![CDATA[Navigation Builder Menu]]></string>
+    <string name="_bx_dev_nav_txt_menus_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_txt_menus_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_nav_txt_menus_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_nav_err_menus_object"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_menus_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_err_menus_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_nav_txt_menus_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_nav_err_menus_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_menus_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_menus_set_name_select"><![CDATA[Select Set...]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_template_id"><![CDATA[Style]]></string>
+    <string name="_bx_dev_nav_txt_menus_template_id"><![CDATA[Style]]></string>
+    <string name="_bx_dev_nav_txt_menus_template_id_select"><![CDATA[Select Style...]]></string>
+    <string name="_bx_dev_nav_err_menus_template_id"><![CDATA[You need to select a style]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_nav_txt_menus_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_nav_txt_menus_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_nav_txt_sys_menus_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_nav_txt_menus_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_nav_txt_menus_export_insert"><![CDATA[Adding to database]]></string>
+    <string name="_bx_dev_nav_txt_menus_export_delete"><![CDATA[Removing from database]]></string>
+    <string name="_bx_dev_nav_btn_sys_menus_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_menus_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_menus_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_nav_btn_sys_menus_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_btn_menus_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_btn_menus_done"><![CDATA[Done]]></string>
+    <string name="_bx_dev_nav_txt_sets_create_popup"><![CDATA[Add New Set]]></string>
+    <string name="_bx_dev_nav_err_sets_create"><![CDATA[Cannot add set]]></string>
+    <string name="_bx_dev_nav_txt_sets_edit_popup"><![CDATA[Edit "{0}" Set]]></string>
+    <string name="_bx_dev_nav_err_sets_edit"><![CDATA[Cannot update set]]></string>
+    <string name="_bx_dev_nav_txt_sets_set"><![CDATA[Navigation Builder Sets]]></string>
+    <string name="_bx_dev_nav_txt_sets_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_txt_sets_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_nav_txt_sys_sets_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_sets_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_err_sets_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_nav_txt_sys_sets_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_sets_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_err_sets_set_name"><![CDATA[You need to enter a set name]]></string>
+    <string name="_bx_dev_nav_txt_sys_sets_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_nav_txt_sets_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_nav_err_sets_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_sets_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_nav_txt_sets_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_nav_btn_sys_sets_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_sets_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_sys_sets_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_btn_sets_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_txt_items_create_popup"><![CDATA[Add New Item]]></string>
+    <string name="_bx_dev_nav_err_items_create"><![CDATA[Cannot add item]]></string>
+    <string name="_bx_dev_nav_txt_items_edit_popup"><![CDATA[Edit "{0}" Item]]></string>
+    <string name="_bx_dev_nav_err_items_edit"><![CDATA[Cannot update item]]></string>
+    <string name="_bx_dev_nav_txt_items_item"><![CDATA[Navigation Builder Items]]></string>
+    <string name="_bx_dev_nav_txt_items_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_txt_items_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_txt_items_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_nav_err_items_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_txt_items_set_name"><![CDATA[Set]]></string>
+    <string name="_bx_dev_nav_err_items_set_name"><![CDATA[You need to select a set]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_name"><![CDATA[Name]]></string>
+    <string name="_bx_dev_nav_txt_items_name"><![CDATA[Name]]></string>
+    <string name="_bx_dev_nav_err_items_name"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_title_system"><![CDATA[System Title]]></string>
+    <string name="_bx_dev_nav_txt_items_title_system"><![CDATA[System Title]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_nav_txt_items_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_nav_err_items_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_link"><![CDATA[Link]]></string>
+    <string name="_bx_dev_nav_txt_items_link"><![CDATA[Link]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_onclick"><![CDATA[OnClick]]></string>
+    <string name="_bx_dev_nav_txt_items_onclick"><![CDATA[OnClick]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_target"><![CDATA[Target]]></string>
+    <string name="_bx_dev_nav_txt_items_target"><![CDATA[Target]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_dev_nav_txt_items_icon"><![CDATA[Icon]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_addon"><![CDATA[Addon]]></string>
+    <string name="_bx_dev_nav_txt_items_addon"><![CDATA[Addon]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_submenu_object"><![CDATA[Submenu Object]]></string>
+    <string name="_bx_dev_nav_txt_items_submenu_object"><![CDATA[Submenu Object]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_submenu_popup"><![CDATA[Submenu Popup]]></string>
+    <string name="_bx_dev_nav_txt_items_submenu_popup"><![CDATA[Submenu Popup]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_visibility_custom"><![CDATA[Custom Visibility]]></string>
+    <string name="_bx_dev_nav_txt_items_visibility_custom"><![CDATA[Custom Visibility]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on"><![CDATA[Hidden on]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on"><![CDATA[Hidden on]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_phone"><![CDATA[phone]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_tablet"><![CDATA[tablet]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_desktop"><![CDATA[desktop]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_mobile"><![CDATA[mobile app]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_pt"><![CDATA[Hidden for page type]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_pt"><![CDATA[Hidden for page type]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_col"><![CDATA[Hidden for column]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col"><![CDATA[Hidden for column]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col_thin"><![CDATA[Thin]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col_half"><![CDATA[Half]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col_wide"><![CDATA[Wide]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_col_full"><![CDATA[Full]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_hidden_on_cxt"><![CDATA[Hidden in contexts]]></string>
+    <string name="_bx_dev_nav_txt_items_hidden_on_cxt"><![CDATA[Hidden in contexts]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_primary"><![CDATA[Primary]]></string>
+    <string name="_bx_dev_nav_txt_items_primary"><![CDATA[Primary]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_collapsed"><![CDATA[Collapsed]]></string>
+    <string name="_bx_dev_nav_txt_items_collapsed"><![CDATA[Collapsed]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_copyable"><![CDATA[Copyable]]></string>
+    <string name="_bx_dev_nav_txt_items_copyable"><![CDATA[Copyable]]></string>
+    <string name="_bx_dev_nav_txt_sys_items_active_api"><![CDATA[Active (for App)]]></string>
+    <string name="_bx_dev_nav_txt_items_active_api"><![CDATA[Active (for App)]]></string>
+    <string name="_bx_dev_nav_btn_sys_items_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_items_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_nav_btn_items_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_nav_btn_sys_items_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_nav_btn_items_cancel"><![CDATA[Cancel]]></string>
+
+    <!-- Page Builder -->
+    <string name="_bx_dev_bp_txt_select_module"><![CDATA[Select Module...]]></string>
+    <string name="_bx_dev_bp_txt_page_create_popup"><![CDATA[Add New Page]]></string>
+    <string name="_bx_dev_bp_err_page_create"><![CDATA[Cannot add page]]></string>
+    <string name="_bx_dev_bp_txt_page_form"><![CDATA[Page in Page Builder]]></string>
+    <string name="_bx_dev_bp_txt_page_display_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_bp_txt_page_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_bp_err_page_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_bp_txt_page_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_bp_err_page_object"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_title_system"><![CDATA[System Title]]></string>
+    <string name="_bx_dev_bp_txt_page_title_system"><![CDATA[System Title]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_bp_txt_page_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_bp_err_page_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_uri"><![CDATA[URI]]></string>
+    <string name="_bx_dev_bp_txt_page_uri"><![CDATA[URI]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_url"><![CDATA[URL]]></string>
+    <string name="_bx_dev_bp_txt_page_url"><![CDATA[URL]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_layout_id"><![CDATA[Layout]]></string>
+    <string name="_bx_dev_bp_txt_page_layout_id"><![CDATA[Layout]]></string>
+    <string name="_bx_dev_bp_txt_page_layout_id_select"><![CDATA[Select Layout...]]></string>
+    <string name="_bx_dev_bp_err_page_layout_id"><![CDATA[You need to select a layout]]></string>
+    <string name="_bx_dev_bp_txt_page_cache_editable"><![CDATA[Editable]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_bp_txt_page_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_bp_txt_page_override_class_name"><![CDATA[Class Name]]></string>
+    <string name="_bx_dev_bp_txt_sys_page_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_bp_txt_page_override_class_file"><![CDATA[Class File]]></string>
+    <string name="_bx_dev_bp_btn_sys_page_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_btn_page_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_btn_sys_page_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_bp_btn_page_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_bp_btn_page_done"><![CDATA[Done]]></string>
+    <string name="_bx_dev_bp_btn_page_export"><![CDATA[Export Page]]></string>
+    <string name="_bx_dev_bp_txt_page_export_popup"><![CDATA[Export "{0}" Page]]></string>
+    <string name="_bx_dev_bp_txt_page_export_insert"><![CDATA[Adding to database]]></string>
+    <string name="_bx_dev_bp_txt_page_export_delete"><![CDATA[Removing from database]]></string>
+    <string name="_bx_dev_bp_txt_block_edit_popup"><![CDATA[Edit "{0}" Block]]></string>
+    <string name="_bx_dev_bp_err_block_edit"><![CDATA[Cannot update page]]></string>
+    <string name="_bx_dev_bp_err_block_not_found"><![CDATA[Block was not found]]></string>
+    <string name="_bx_dev_bp_err_block_delete"><![CDATA[Cannot delete block]]></string>
+    <string name="_bx_dev_bp_txt_block_form"><![CDATA[Block in Page Builder]]></string>
+    <string name="_bx_dev_bp_txt_block_display_edit"><![CDATA[Edit]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_id"><![CDATA[ID]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_bp_txt_block_object"><![CDATA[Name]]></string>
+    <string name="_bx_dev_bp_err_block_object"><![CDATA[You need to enter a unique value here]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_bp_txt_block_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_bp_err_block_module"><![CDATA[You need to select a module]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_title_system"><![CDATA[Title System]]></string>
+    <string name="_bx_dev_bp_txt_block_title_system"><![CDATA[Title System]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_bp_txt_block_title"><![CDATA[Title]]></string>
+    <string name="_bx_dev_bp_err_block_title"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_designbox_id"><![CDATA[Layout]]></string>
+    <string name="_bx_dev_bp_txt_block_designbox_id"><![CDATA[Layout]]></string>
+    <string name="_bx_dev_bp_err_block_designbox_id"><![CDATA[You need to select a layout]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_class"><![CDATA[CSS class]]></string>
+    <string name="_bx_dev_bp_txt_block_class"><![CDATA[CSS class]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_async"><![CDATA[Asynchronous Mode]]></string>
+    <string name="_bx_dev_bp_txt_block_async"><![CDATA[Asynchronous Mode]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_submenu"><![CDATA[Block submenu]]></string>
+    <string name="_bx_dev_bp_txt_block_submenu"><![CDATA[Block submenu]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_tabs"><![CDATA[Use 'tabs']]></string>
+    <string name="_bx_dev_bp_txt_block_tabs"><![CDATA[Use 'tabs' based block submenu]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_hidden_on"><![CDATA[Hidden on]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on"><![CDATA[Hidden on]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on_phone"><![CDATA[phone]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on_tablet"><![CDATA[tablet]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on_desktop"><![CDATA[desktop]]></string>
+    <string name="_bx_dev_bp_txt_block_hidden_on_mobile"><![CDATA[mobile]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_visible_for"><![CDATA[Visible For]]></string>
+    <string name="_bx_dev_bp_txt_block_visible_for"><![CDATA[Visible For]]></string>
+    <string name="_bx_dev_bp_txt_block_visible_for_all"><![CDATA[Anyone]]></string>
+    <string name="_bx_dev_bp_txt_block_visible_for_selected"><![CDATA[Selected levels]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_visible_for_levels"><![CDATA[Select Levels]]></string>
+    <string name="_bx_dev_bp_txt_block_visible_for_levels"><![CDATA[Select Levels]]></string>
+    <string name="_bx_dev_bp_txt_block_content_service"><![CDATA[Code]]></string>
+    <string name="_bx_dev_bp_txt_block_content_lang"><![CDATA[Content]]></string>
+    <string name="_bx_dev_bp_dsc_block_content_lang"><![CDATA[Enter language key]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_bp_txt_block_deletable"><![CDATA[Deletable]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_copyable"><![CDATA[Copyable]]></string>
+    <string name="_bx_dev_bp_txt_block_copyable"><![CDATA[Copyable]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_active"><![CDATA[Active]]></string>
+    <string name="_bx_dev_bp_txt_block_active"><![CDATA[Active]]></string>
+    <string name="_bx_dev_bp_txt_sys_block_active_api"><![CDATA[Active (for App)]]></string>
+    <string name="_bx_dev_bp_txt_block_active_api"><![CDATA[Active (for App)]]></string>
+    <string name="_bx_dev_bp_btn_sys_block_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_btn_block_add"><![CDATA[Add]]></string>
+    <string name="_bx_dev_bp_btn_block_save"><![CDATA[Save]]></string>
+    <string name="_bx_dev_bp_btn_sys_block_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_bp_btn_block_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_bp_btn_sys_block_delete"><![CDATA[Delete]]></string>
+    <string name="_bx_dev_bp_btn_block_delete"><![CDATA[Delete]]></string>
+
+    <!-- Polyglot -->
+    <string name="_bx_dev_pgt_txt_manage_gl_title"><![CDATA[Module]]></string>
+    <string name="_bx_dev_pgt_btn_manage_gl_add_keys"><![CDATA[Add Keys]]></string>
+    <string name="_bx_dev_pgt_btn_manage_gl_recompile"><![CDATA[Recompile All]]></string>
+    <string name="_bx_dev_pgt_btn_manage_gl_restore"><![CDATA[Restore All]]></string>
+    <string name="_bx_dev_pgt_txt_manage_recompile_title_sing"><![CDATA[Recompile %s language for %s module.]]></string>
+    <string name="_bx_dev_pgt_txt_manage_restore_title_sing"><![CDATA[Restore %s language for %s module.]]></string>
+    <string name="_bx_dev_pgt_txt_manage_recompile_title_plur"><![CDATA[Recompile %s language for all modules.]]></string>
+    <string name="_bx_dev_pgt_txt_manage_restore_title_plur"><![CDATA[Restore %s language for all modules.]]></string>
+    <string name="_bx_dev_pgt_txt_manage_modules_all"><![CDATA[All]]></string>
+    <string name="_bx_dev_pgt_txt_keys_add_popup"><![CDATA[Add Keys]]></string>
+
+    <string name="_bx_dev_pgt_txt_keys_form"><![CDATA[Keys]]></string>
+    <string name="_bx_dev_pgt_txt_keys_display_add"><![CDATA[Add Keys]]></string>
+    <string name="_bx_dev_pgt_txt_sys_keys_language"><![CDATA[Language]]></string>
+    <string name="_bx_dev_pgt_txt_keys_language"><![CDATA[Language]]></string>
+    <string name="_bx_dev_pgt_inf_keys_language"><![CDATA[Leave empty to add in all available languages.]]></string>
+    <string name="_bx_dev_pgt_txt_sys_keys_category"><![CDATA[Category]]></string>
+    <string name="_bx_dev_pgt_txt_keys_category"><![CDATA[Category]]></string>
+    <string name="_bx_dev_pgt_err_keys_category"><![CDATA[You need to select a category]]></string>
+    <string name="_bx_dev_pgt_txt_sys_keys_content"><![CDATA[Keys]]></string>
+    <string name="_bx_dev_pgt_txt_keys_content"><![CDATA[Keys]]></string>
+    <string name="_bx_dev_pgt_err_keys_content"><![CDATA[You need to enter a value here]]></string>
+    <string name="_bx_dev_pgt_inf_keys_content"><![CDATA[Use XML format, like the following:<br />&lt;string name="_key_first"&gt;&lt;![CDATA[First Value]]&gt;&lt;/string&gt;<br />&lt;string name="_key_second"&gt;&lt;![CDATA[Second Value]]&gt;&lt;/string&gt;]]></string>
+    <string name="_bx_dev_pgt_txt_sys_keys_replace"><![CDATA[Replace]]></string>
+    <string name="_bx_dev_pgt_txt_keys_replace"><![CDATA[Force replace if exists]]></string>
+    <string name="_bx_dev_pgt_btn_sys_keys_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_dev_pgt_btn_keys_submit"><![CDATA[Submit]]></string>
+    <string name="_bx_dev_pgt_btn_sys_keys_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_pgt_btn_keys_cancel"><![CDATA[Cancel]]></string>
+    <string name="_bx_dev_pgt_msg_keys_added"><![CDATA[{0} keys were added]]></string>
+
+    <!-- Hash -->
+    <string name="_bx_dev_hash_msg_reset_hash_system"><![CDATA[System hash was successfully reset.]]></string>
+    <string name="_bx_dev_hash_err_reset_hash_system"><![CDATA[Cannot reset system hash.]]></string>
+    <string name="_bx_dev_hash_msg_reset_hash_modules"><![CDATA[Hash was successfully reset for the following modules:]]></string>
+    <string name="_bx_dev_hash_txt_reset_hash_module"><![CDATA[Module]]></string>
+    <string name="_bx_dev_hash_txt_reset_hash_files"><![CDATA[Files Hashed]]></string>
+</resources>

+ 724 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/install/sql/install.sql

@@ -0,0 +1,724 @@
+SET @sName = 'bx_developer';
+
+
+--
+-- Studio page and widget.
+--
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, @sName, '_bx_dev_adm_page_cpt', '_bx_dev_adm_page_cpt', 'bx_developer@modules/boonex/developer/|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, @sName, 'extensions', CONCAT('{url_studio}module.php?name=', @sName), '', 'bx_developer@modules/boonex/developer/|std-icon.svg', '_bx_dev_adm_wgt_cpt', '', '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(NOT ISNULL(@iParentPageOrder), @iParentPageOrder + 1, 1));
+
+
+--
+-- Forms Builder -> Grid descriptors
+--
+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_developer_forms', 'Sql', 'SELECT * FROM `sys_objects_form` WHERE 1 ', 'sys_objects_form', 'id', 'module,title', 'active', '', 100, NULL, 'start', '', 'module', 'title', 'like', 'module', 'title', 2147483647, 'BxDevFormsForms', 'modules/boonex/developer/classes/BxDevFormsForms.php'),
+('bx_developer_forms_displays', 'Sql', 'SELECT `td`.`id` AS `id`, `td`.`object` AS `object`, `td`.`display_name` AS `display_name`, `td`.`title` AS `display_title`, `td`.`module` AS `module`, `tf`.`title` AS `form_title` FROM `sys_form_displays` AS `td` LEFT JOIN `sys_objects_form` AS `tf` ON `td`.`object`=`tf`.`object` WHERE 1 ', 'sys_form_displays', 'id', 'module,object,display_title', '', '', 100, NULL, 'start', '', 'td`.`module', 'td`.`title', 'like', 'module', 'display_title,form_title', 2147483647, 'BxDevFormsDisplays', 'modules/boonex/developer/classes/BxDevFormsDisplays.php'),
+('bx_developer_forms_fields', 'Sql', 'SELECT `tdi`.`id` AS `id`, `ti`.`caption_system` AS `caption_system`, `ti`.`caption` AS `caption`, `ti`.`type` AS `type`, `ti`.`module` AS `module`, `tdi`.`visible_for_levels` AS `visible_for_levels`, `tdi`.`active` AS `active`, `ti`.`editable` AS `editable`, `ti`.`deletable` AS `deletable`, `tdi`.`order` AS `order` FROM `sys_form_display_inputs` AS `tdi` LEFT JOIN `sys_form_inputs` AS `ti` ON `tdi`.`input_name`=`ti`.`name` AND `ti`.`object`=? WHERE 1 AND `tdi`.`display_name`=?', 'sys_form_display_inputs', 'id', 'order', 'active', '', 100, NULL, 'start', '', 'ti`.`type', 'ti`.`caption_system', 'like', '', '', 2147483647, 'BxDevFormsFields', 'modules/boonex/developer/classes/BxDevFormsFields.php'),
+
+('bx_developer_forms_pre_lists', 'Sql', 'SELECT * FROM `sys_form_pre_lists` WHERE 1 ', 'sys_form_pre_lists', 'id', '', '', '', 100, NULL, 'start', '', 'module,key', 'title', 'auto', 'module', 'title', 2147483647, 'BxDevFormsPreLists', 'modules/boonex/developer/classes/BxDevFormsPreLists.php'),
+('bx_developer_forms_pre_values', 'Sql', 'SELECT * FROM `sys_form_pre_values` WHERE 1 ', 'sys_form_pre_values', 'id', 'Order', '', '', 1000, NULL, 'start', '', 'Key,Value', 'LKey,LKey2', 'auto', '', '', 2147483647, 'BxDevFormsPreValues', 'modules/boonex/developer/classes/BxDevFormsPreValues.php'),
+
+('bx_developer_search_forms', 'Sql', 'SELECT * FROM `sys_objects_search_extended` WHERE 1 ', 'sys_objects_search_extended', 'id', 'module,title', 'active', '', 100, NULL, 'start', '', 'module', 'title', 'like', 'module', 'title', 2147483647, 'BxDevFormsSearchForms', 'modules/boonex/developer/classes/BxDevFormsSearchForms.php'),
+('bx_developer_search_forms_fields', 'Sql', 'SELECT * FROM `sys_search_extended_fields` WHERE 1 AND `object`=?', 'sys_search_extended_fields', 'id', 'order', 'active', '', 100, NULL, 'start', '', 'type', 'caption', 'like', '', '', 2147483647, 'BxDevFormsSearchFields', 'modules/boonex/developer/classes/BxDevFormsSearchFields.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_developer_forms', 'switcher', '', '10%', 0, '', '', 1),
+('bx_developer_forms', 'title', '_bx_dev_frm_txt_forms_gl_title', '40%', 1, '38', '', 2),
+('bx_developer_forms', 'module', '_bx_dev_frm_txt_forms_gl_module', '15%', 0, '13', '', 3),
+('bx_developer_forms', 'displays', '_bx_dev_frm_txt_forms_gl_displays', '15%', 0, '13', '', 4),
+('bx_developer_forms', 'actions', '', '20%', 0, '', '', 5),
+
+('bx_developer_forms_displays', 'display_title', '_bx_dev_frm_txt_displays_gl_title', '30%', 1, '48', '', 1),
+('bx_developer_forms_displays', 'module', '_bx_dev_frm_txt_displays_gl_module', '13%', 0, '11', '', 2),
+('bx_developer_forms_displays', 'form_title', '_bx_dev_frm_txt_displays_gl_form', '24%', 1, '22', '', 3),
+('bx_developer_forms_displays', 'fields', '_bx_dev_frm_txt_displays_gl_fields', '13%', 0, '11', '', 4),
+('bx_developer_forms_displays', 'actions', '', '20%', 0, '', '', 5),
+
+('bx_developer_forms_fields', 'order', '', '1%', 0, '', '', 1),
+('bx_developer_forms_fields', 'switcher', '', '9%', 0, '', '', 2),
+('bx_developer_forms_fields', 'type', '_bx_dev_frm_txt_fields_gl_type', '5%', 0, '', '', 3),
+('bx_developer_forms_fields', 'caption_system', '_bx_dev_frm_txt_fields_gl_caption_system', '40%', 1, '38', '', 4),
+('bx_developer_forms_fields', 'module', '_bx_dev_frm_txt_fields_gl_module', '15%', 0, '13', '', 5),
+('bx_developer_forms_fields', 'visible_for_levels', '_bx_dev_frm_txt_fields_gl_visible', '10%', 0, '10', '', 6),
+('bx_developer_forms_fields', 'actions', '', '20%', 0, '', '', 7),
+
+('bx_developer_forms_pre_lists', 'title', '_bx_dev_frm_txt_pre_lists_gl_title', '45%', 1, '50', '', 1),
+('bx_developer_forms_pre_lists', 'values', '_bx_dev_frm_txt_pre_lists_gl_values', '12%', 0, '10', '', 2),
+('bx_developer_forms_pre_lists', 'module', '_bx_dev_frm_txt_pre_lists_gl_module', '13%', 0, '11', '', 3),
+('bx_developer_forms_pre_lists', 'use_for_sets', '_bx_dev_frm_txt_pre_lists_gl_use_for_sets', '10%', 0, '8', '', 4),
+('bx_developer_forms_pre_lists', 'actions', '', '20%', 0, '', '', 5),
+
+('bx_developer_forms_pre_values', 'order', '', '1%', 0, '', '', 1),
+('bx_developer_forms_pre_values', 'LKey', '_bx_dev_frm_txt_pre_values_gl_lkey', '79%', 1, '75', '', 2),
+('bx_developer_forms_pre_values', 'actions', '', '20%', 0, '', '', 3),
+
+('bx_developer_search_forms', 'switcher', '', '10%', 0, '', '', 1),
+('bx_developer_search_forms', 'title', '_bx_dev_frm_txt_search_forms_gl_title', '40%', 1, '38', '', 2),
+('bx_developer_search_forms', 'module', '_bx_dev_frm_txt_search_forms_gl_module', '15%', 0, '13', '', 3),
+('bx_developer_search_forms', 'fields', '_bx_dev_frm_txt_search_forms_gl_fields', '15%', 0, '13', '', 4),
+('bx_developer_search_forms', 'actions', '', '20%', 0, '', '', 5),
+
+('bx_developer_search_forms_fields', 'order', '', '1%', 0, '', '', 1),
+('bx_developer_search_forms_fields', 'switcher', '', '9%', 0, '', '', 2),
+('bx_developer_search_forms_fields', 'type', '_bx_dev_frm_txt_search_forms_fields_gl_type', '10%', 0, '', '', 3),
+('bx_developer_search_forms_fields', 'caption', '_bx_dev_frm_txt_search_forms_fields_gl_caption', '40%', 1, '38', '', 4),
+('bx_developer_search_forms_fields', 'search_type', '_bx_dev_frm_txt_search_forms_fields_gl_search_type', '10%', 0, '', '', 5),
+('bx_developer_search_forms_fields', 'search_operator', '_bx_dev_frm_txt_search_forms_fields_gl_search_operator', '10%', 0, '', '', 6),
+('bx_developer_search_forms_fields', 'actions', '', '20%', 0, '', '', 7);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
+('bx_developer_forms', 'independent', 'add', '_bx_dev_frm_btn_forms_gl_create', '', 0, 1),
+('bx_developer_forms', 'single', 'export', '', 'download', 0, 1),
+('bx_developer_forms', 'single', 'edit', '', 'pencil-alt', 0, 2),
+
+('bx_developer_forms_displays', 'independent', 'add', '_bx_dev_frm_btn_displays_gl_create', '', 0, 1),
+('bx_developer_forms_displays', 'single', 'edit', '', 'pencil-alt', 0, 1),
+
+('bx_developer_forms_fields', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_forms_fields', 'single', 'delete', '', 'remove', 1, 2),
+('bx_developer_forms_fields', 'single', 'show_to', '_bx_dev_frm_btn_fields_visible', '', 0, 3),
+('bx_developer_forms_fields', 'independent', 'add', '_bx_dev_frm_btn_fields_create', '', 0, 1),
+
+('bx_developer_forms_pre_lists', 'independent', 'add', '_bx_dev_frm_btn_pre_lists_create', '', 0, 1),
+('bx_developer_forms_pre_lists', 'single', 'export', '', 'download', 0, 1),
+('bx_developer_forms_pre_lists', 'single', 'edit', '', 'pencil-alt', 0, 2),
+('bx_developer_forms_pre_lists', 'single', 'delete', '', 'remove', 1, 3),
+
+('bx_developer_forms_pre_values', 'independent', 'add', '_bx_dev_frm_btn_pre_values_create', '', 0, 1),
+('bx_developer_forms_pre_values', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_forms_pre_values', 'single', 'delete', '', 'remove', 1, 2),
+
+('bx_developer_search_forms', 'independent', 'add', '_bx_dev_frm_btn_search_forms_create', '', 0, 1),
+('bx_developer_search_forms', 'single', 'edit', '', 'pencil-alt', 0, 1),
+
+('bx_developer_search_forms_fields', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_search_forms_fields', 'independent', 'reset', '_bx_dev_frm_btn_search_forms_fields_reset', '', 0, 1);
+
+
+--
+-- Forms Builder -> Forms.
+--
+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_developer_forms_form', @sName, '_bx_dev_frm_txt_forms_form', '', '', 'do_submit', 'sys_objects_form', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_form_add', @sName, 'bx_developer_forms_form', '_bx_dev_frm_txt_forms_display_add', 0),
+('bx_developer_forms_form_edit', @sName, 'bx_developer_forms_form', '_bx_dev_frm_txt_forms_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_developer_forms_form', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_forms_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_form', @sName, 'object', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_object', '_bx_dev_frm_txt_forms_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_object', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'module', '', '', 0, 'select', '_bx_dev_frm_txt_sys_forms_module', '_bx_dev_frm_txt_forms_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_forms_module', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_title', '_bx_dev_frm_txt_forms_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_title', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'action', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_action', '_bx_dev_frm_txt_forms_action', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'form_attrs', '', '', 0, 'textarea', '_bx_dev_frm_txt_sys_forms_form_attrs', '_bx_dev_frm_txt_forms_form_attrs', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'submit_name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_submit_name', '_bx_dev_frm_txt_forms_submit_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'table', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_table', '_bx_dev_frm_txt_forms_table', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'key', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_key', '_bx_dev_frm_txt_forms_key', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'uri', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_uri', '_bx_dev_frm_txt_forms_uri', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'uri_title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_uri_title', '_bx_dev_frm_txt_forms_uri_title', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'params', '', '', 0, 'textarea', '_bx_dev_frm_txt_sys_forms_params', '_bx_dev_frm_txt_forms_params', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_frm_txt_sys_forms_deletable', '_bx_dev_frm_txt_forms_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_form', @sName, 'override_class_name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_override_class_name', '_bx_dev_frm_txt_forms_override_class_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'override_class_file', '', '', 0, 'text', '_bx_dev_frm_txt_sys_forms_override_class_file', '_bx_dev_frm_txt_forms_override_class_file', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_form', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_form', @sName, 'do_submit', '_bx_dev_frm_btn_forms_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_forms_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_form', @sName, 'cancel', '_bx_dev_frm_btn_forms_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_forms_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_form_add', 'object', 2147483647, 1, 1),
+('bx_developer_forms_form_add', 'module', 2147483647, 1, 2),
+('bx_developer_forms_form_add', 'title', 2147483647, 1, 3),
+('bx_developer_forms_form_add', 'action', 2147483647, 1, 4),
+('bx_developer_forms_form_add', 'form_attrs', 2147483647, 1, 5),
+('bx_developer_forms_form_add', 'submit_name', 2147483647, 1, 6),
+('bx_developer_forms_form_add', 'table', 2147483647, 1, 7),
+('bx_developer_forms_form_add', 'key', 2147483647, 1, 8),
+('bx_developer_forms_form_add', 'uri', 2147483647, 1, 9),
+('bx_developer_forms_form_add', 'uri_title', 2147483647, 1, 10),
+('bx_developer_forms_form_add', 'params', 2147483647, 1, 11),
+('bx_developer_forms_form_add', 'deletable', 2147483647, 1, 12),
+('bx_developer_forms_form_add', 'override_class_name', 2147483647, 1, 13),
+('bx_developer_forms_form_add', 'override_class_file', 2147483647, 1, 14),
+('bx_developer_forms_form_add', 'controls', 2147483647, 1, 15),
+('bx_developer_forms_form_add', 'do_submit', 2147483647, 1, 16),
+('bx_developer_forms_form_add', 'cancel', 2147483647, 1, 17),
+('bx_developer_forms_form_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_form_edit', 'object', 2147483647, 1, 2),
+('bx_developer_forms_form_edit', 'module', 2147483647, 1, 3),
+('bx_developer_forms_form_edit', 'title', 2147483647, 1, 4),
+('bx_developer_forms_form_edit', 'action', 2147483647, 1, 5),
+('bx_developer_forms_form_edit', 'form_attrs', 2147483647, 1, 6),
+('bx_developer_forms_form_edit', 'submit_name', 2147483647, 1, 7),
+('bx_developer_forms_form_edit', 'table', 2147483647, 1, 8),
+('bx_developer_forms_form_edit', 'key', 2147483647, 1, 9),
+('bx_developer_forms_form_edit', 'uri', 2147483647, 1, 10),
+('bx_developer_forms_form_edit', 'uri_title', 2147483647, 1, 11),
+('bx_developer_forms_form_edit', 'params', 2147483647, 1, 12),
+('bx_developer_forms_form_edit', 'deletable', 2147483647, 1, 13),
+('bx_developer_forms_form_edit', 'override_class_name', 2147483647, 1, 14),
+('bx_developer_forms_form_edit', 'override_class_file', 2147483647, 1, 15),
+('bx_developer_forms_form_edit', 'controls', 2147483647, 1, 16),
+('bx_developer_forms_form_edit', 'do_submit', 2147483647, 1, 17),
+('bx_developer_forms_form_edit', 'cancel', 2147483647, 1, 18);
+
+--
+-- Forms Builder -> Displays.
+--
+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_developer_forms_display', @sName, '_bx_dev_frm_txt_displays_diplay', '', '', 'do_submit', 'sys_form_displays', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_display_add', @sName, 'bx_developer_forms_display', '_bx_dev_frm_txt_displays_display_add', 0),
+('bx_developer_forms_display_edit', @sName, 'bx_developer_forms_display', '_bx_dev_frm_txt_displays_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_developer_forms_display', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_displays_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_display', @sName, 'display_name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_displays_display_name', '_bx_dev_frm_txt_displays_display_name', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_displays_display_name', 'Xss', '', 0, 0),
+('bx_developer_forms_display', @sName, 'module', '', '', 0, 'select', '_bx_dev_frm_txt_sys_displays_module', '_bx_dev_frm_txt_displays_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_displays_module', 'Xss', '', 0, 0),
+('bx_developer_forms_display', @sName, 'object', '', '', 0, 'select', '_bx_dev_frm_txt_sys_displays_object', '_bx_dev_frm_txt_displays_object', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_displays_object', 'Xss', '', 0, 0),
+('bx_developer_forms_display', @sName, 'title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_displays_title', '_bx_dev_frm_txt_displays_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_displays_title', 'Xss', '', 0, 0),
+('bx_developer_forms_display', @sName, 'view_mode', '1', '', 0, 'switcher', '_bx_dev_frm_txt_sys_displays_view_mode', '_bx_dev_frm_txt_displays_view_mode', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_display', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_display', @sName, 'do_submit', '_bx_dev_frm_btn_displays_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_displays_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_display', @sName, 'cancel', '_bx_dev_frm_btn_displays_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_displays_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_display_add', 'display_name', 2147483647, 1, 1),
+('bx_developer_forms_display_add', 'module', 2147483647, 1, 2),
+('bx_developer_forms_display_add', 'object', 2147483647, 1, 3),
+('bx_developer_forms_display_add', 'title', 2147483647, 1, 4),
+('bx_developer_forms_display_add', 'view_mode', 2147483647, 1, 5),
+('bx_developer_forms_display_add', 'controls', 2147483647, 1, 6),
+('bx_developer_forms_display_add', 'do_submit', 2147483647, 1, 7),
+('bx_developer_forms_display_add', 'cancel', 2147483647, 1, 8),
+('bx_developer_forms_display_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_display_edit', 'display_name', 2147483647, 1, 1),
+('bx_developer_forms_display_edit', 'module', 2147483647, 1, 2),
+('bx_developer_forms_display_edit', 'object', 2147483647, 1, 3),
+('bx_developer_forms_display_edit', 'title', 2147483647, 1, 4),
+('bx_developer_forms_display_edit', 'view_mode', 2147483647, 1, 5),
+('bx_developer_forms_display_edit', 'controls', 2147483647, 1, 6),
+('bx_developer_forms_display_edit', 'do_submit', 2147483647, 1, 7),
+('bx_developer_forms_display_edit', 'cancel', 2147483647, 1, 8);
+
+--
+-- Forms Builder -> Pre Lists.
+--
+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_developer_forms_prelist', @sName, '_bx_dev_frm_txt_prelists_prelist', '', '', 'do_submit', 'sys_form_pre_lists', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_prelist_add', @sName, 'bx_developer_forms_prelist', '_bx_dev_frm_txt_prelists_display_add', 0),
+('bx_developer_forms_prelist_edit', @sName, 'bx_developer_forms_prelist', '_bx_dev_frm_txt_prelists_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_developer_forms_prelist', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_prelists_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'key', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prelists_key', '_bx_dev_frm_txt_prelists_key', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_prelists_key', 'Xss', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'module', '', '', 0, 'select', '_bx_dev_frm_txt_sys_prelists_module', '_bx_dev_frm_txt_prelists_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_prelists_module', 'Xss', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prelists_title', '_bx_dev_frm_txt_prelists_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_prelists_title', 'Xss', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'use_for_sets', '1', '', 1, 'switcher', '_bx_dev_frm_txt_sys_prelists_use_for_sets', '_bx_dev_frm_txt_prelists_use_for_sets', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'do_submit', '_bx_dev_frm_btn_prelists_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_prelists_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_prelist', @sName, 'cancel', '_bx_dev_frm_btn_prelists_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_prelists_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_prelist_add', 'key', 2147483647, 1, 1),
+('bx_developer_forms_prelist_add', 'module', 2147483647, 1, 2),
+('bx_developer_forms_prelist_add', 'title', 2147483647, 1, 3),
+('bx_developer_forms_prelist_add', 'use_for_sets', 2147483647, 1, 4),
+('bx_developer_forms_prelist_add', 'controls', 2147483647, 1, 5),
+('bx_developer_forms_prelist_add', 'do_submit', 2147483647, 1, 6),
+('bx_developer_forms_prelist_add', 'cancel', 2147483647, 1, 7),
+('bx_developer_forms_prelist_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_prelist_edit', 'module', 2147483647, 1, 2),
+('bx_developer_forms_prelist_edit', 'title', 2147483647, 1, 3),
+('bx_developer_forms_prelist_edit', 'use_for_sets', 2147483647, 1, 4),
+('bx_developer_forms_prelist_edit', 'controls', 2147483647, 1, 5),
+('bx_developer_forms_prelist_edit', 'do_submit', 2147483647, 1, 6),
+('bx_developer_forms_prelist_edit', 'cancel', 2147483647, 1, 7);
+
+--
+-- Forms Builder -> Pre Values.
+--
+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_developer_forms_prevalue', @sName, '_bx_dev_frm_txt_prevalues_prevalue', '', '', 'do_submit', 'sys_form_pre_values', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_prevalue_add', @sName, 'bx_developer_forms_prevalue', '_bx_dev_frm_txt_prevalues_display_add', 0),
+('bx_developer_forms_prevalue_edit', @sName, 'bx_developer_forms_prevalue', '_bx_dev_frm_txt_prevalues_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_developer_forms_prevalue', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_prevalues_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'Key', '', '', 0, 'select', '_bx_dev_frm_txt_sys_prevalues_key', '_bx_dev_frm_txt_prevalues_key', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_prevalues_key', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'Value', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prevalues_value', '_bx_dev_frm_txt_prevalues_value', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:1;s:3:"max";i:255;}', '_bx_dev_frm_err_prevalues_value', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'LKey', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prevalues_lkey', '_bx_dev_frm_txt_prevalues_lkey', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:1;s:3:"max";i:100;}', '_bx_dev_frm_err_prevalues_lkey', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'LKey2', '', '', 0, 'text', '_bx_dev_frm_txt_sys_prevalues_lkey2', '_bx_dev_frm_txt_prevalues_lkey2', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'Data', '', '', 0, 'textarea', '_bx_dev_frm_txt_sys_prevalues_data', '_bx_dev_frm_txt_prevalues_data', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'do_submit', '_bx_dev_frm_btn_prevalues_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_prevalues_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_prevalue', @sName, 'cancel', '_bx_dev_frm_btn_prevalues_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_prevalues_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_prevalue_add', 'Key', 2147483647, 1, 1),
+('bx_developer_forms_prevalue_add', 'Value', 2147483647, 1, 2),
+('bx_developer_forms_prevalue_add', 'LKey', 2147483647, 1, 3),
+('bx_developer_forms_prevalue_add', 'LKey2', 2147483647, 1, 4),
+('bx_developer_forms_prevalue_add', 'Data', 2147483647, 1, 5),
+('bx_developer_forms_prevalue_add', 'controls', 2147483647, 1, 6),
+('bx_developer_forms_prevalue_add', 'do_submit', 2147483647, 1, 7),
+('bx_developer_forms_prevalue_add', 'cancel', 2147483647, 1, 8),
+
+('bx_developer_forms_prevalue_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_prevalue_edit', 'Key', 2147483647, 1, 2),
+('bx_developer_forms_prevalue_edit', 'Value', 2147483647, 1, 3),
+('bx_developer_forms_prevalue_edit', 'LKey', 2147483647, 1, 4),
+('bx_developer_forms_prevalue_edit', 'LKey2', 2147483647, 1, 5),
+('bx_developer_forms_prevalue_edit', 'Data', 2147483647, 1, 6),
+('bx_developer_forms_prevalue_edit', 'controls', 2147483647, 1, 7),
+('bx_developer_forms_prevalue_edit', 'do_submit', 2147483647, 1, 8),
+('bx_developer_forms_prevalue_edit', 'cancel', 2147483647, 1, 9);
+
+--
+-- Forms Builder -> Search Forms.
+--
+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_developer_forms_search_form', @sName, '_bx_dev_frm_txt_search_forms_form', '', '', 'do_submit', 'sys_objects_search_extended', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_search_form_add', @sName, 'bx_developer_forms_search_form', '_bx_dev_frm_txt_search_forms_display_add', 0),
+('bx_developer_forms_search_form_edit', @sName, 'bx_developer_forms_search_form', '_bx_dev_frm_txt_search_forms_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_developer_forms_search_form', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_search_forms_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'object', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_forms_object', '_bx_dev_frm_txt_search_forms_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_object', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'object_content_info', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_forms_object_content_info', '_bx_dev_frm_txt_search_forms_object_content_info', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_forms_object_content_info', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'module', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_forms_module', '_bx_dev_frm_txt_search_forms_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_forms_module', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'title', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_forms_title', '_bx_dev_frm_txt_search_forms_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_title', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'class_name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_forms_class_name', '_bx_dev_frm_txt_search_forms_class_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'class_file', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_forms_class_file', '_bx_dev_frm_txt_search_forms_class_file', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'do_submit', '_bx_dev_frm_btn_search_forms_add', '', 0, 'submit', '_bx_dev_frm_btn_sys_search_forms_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_search_form', @sName, 'cancel', '_bx_dev_frm_btn_search_forms_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_search_forms_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_search_form_add', 'object', 2147483647, 1, 1),
+('bx_developer_forms_search_form_add', 'object_content_info', 2147483647, 1, 2),
+('bx_developer_forms_search_form_add', 'module', 2147483647, 1, 3),
+('bx_developer_forms_search_form_add', 'title', 2147483647, 1, 4),
+('bx_developer_forms_search_form_add', 'class_name', 2147483647, 1, 5),
+('bx_developer_forms_search_form_add', 'class_file', 2147483647, 1, 6),
+('bx_developer_forms_search_form_add', 'controls', 2147483647, 1, 7),
+('bx_developer_forms_search_form_add', 'do_submit', 2147483647, 1, 8),
+('bx_developer_forms_search_form_add', 'cancel', 2147483647, 1, 9),
+
+('bx_developer_forms_search_form_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_search_form_edit', 'object', 2147483647, 1, 2),
+('bx_developer_forms_search_form_edit', 'object_content_info', 2147483647, 1, 3),
+('bx_developer_forms_search_form_edit', 'module', 2147483647, 1, 4),
+('bx_developer_forms_search_form_edit', 'title', 2147483647, 1, 5),
+('bx_developer_forms_search_form_edit', 'class_name', 2147483647, 1, 6),
+('bx_developer_forms_search_form_edit', 'class_file', 2147483647, 1, 7),
+('bx_developer_forms_search_form_edit', 'controls', 2147483647, 1, 8),
+('bx_developer_forms_search_form_edit', 'do_submit', 2147483647, 1, 9),
+('bx_developer_forms_search_form_edit', 'cancel', 2147483647, 1, 10);
+
+--
+-- Forms Builder -> Search Fields.
+--
+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_developer_forms_search_fields', @sName, '_bx_dev_frm_txt_search_fields_form', '', '', 'do_submit', 'sys_search_extended_fields', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_forms_search_fields_edit', @sName, 'bx_developer_forms_search_fields', '_bx_dev_frm_txt_search_fields_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_developer_forms_search_fields', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_frm_txt_sys_search_fields_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'object', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_fields_object', '_bx_dev_frm_txt_search_fields_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_forms_object', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'name', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_fields_name', '_bx_dev_frm_txt_search_fields_name', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_search_fields_name', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'type', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_fields_type', '_bx_dev_frm_txt_search_fields_type', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_search_fields_type', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'caption', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_fields_caption', '_bx_dev_frm_txt_search_fields_caption', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_frm_err_search_fields_caption', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'values', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_fields_values', '_bx_dev_frm_txt_search_fields_values', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'search_type', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_fields_search_type', '_bx_dev_frm_txt_search_fields_search_type', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_search_fields_search_type', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'search_value', '', '', 0, 'text', '_bx_dev_frm_txt_sys_search_fields_search_value', '_bx_dev_frm_txt_search_fields_search_value', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'search_operator', '', '', 0, 'select', '_bx_dev_frm_txt_sys_search_fields_search_operator', '_bx_dev_frm_txt_search_fields_search_operator', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_frm_err_search_fields_search_operator', 'Xss', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'do_submit', '_bx_dev_frm_btn_search_fields_save', '', 0, 'submit', '_bx_dev_frm_btn_sys_search_fields_save', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_forms_search_fields', @sName, 'cancel', '_bx_dev_frm_btn_search_fields_cancel', '', 0, 'button', '_bx_dev_frm_btn_sys_search_fields_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_forms_search_fields_edit', 'id', 2147483647, 1, 1),
+('bx_developer_forms_search_fields_edit', 'object', 2147483647, 1, 2),
+('bx_developer_forms_search_fields_edit', 'name', 2147483647, 1, 3),
+('bx_developer_forms_search_fields_edit', 'type', 2147483647, 1, 4),
+('bx_developer_forms_search_fields_edit', 'caption', 2147483647, 1, 5),
+('bx_developer_forms_search_fields_edit', 'values', 2147483647, 1, 6),
+('bx_developer_forms_search_fields_edit', 'search_type', 2147483647, 1, 7),
+('bx_developer_forms_search_fields_edit', 'search_value', 2147483647, 1, 8),
+('bx_developer_forms_search_fields_edit', 'search_operator', 2147483647, 1, 9),
+('bx_developer_forms_search_fields_edit', 'controls', 2147483647, 1, 10),
+('bx_developer_forms_search_fields_edit', 'do_submit', 2147483647, 1, 11),
+('bx_developer_forms_search_fields_edit', 'cancel', 2147483647, 1, 12);
+
+--
+-- Navigation Builder -> Grid descriptors
+--
+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_developer_nav_menus', 'Sql', 'SELECT `tm`.*, `tms`.`title` AS `set_title`, `tmt`.`title` AS `template_title` FROM `sys_objects_menu` AS `tm` LEFT JOIN `sys_menu_sets` AS `tms` ON `tm`.`set_name`=`tms`.`set_name` LEFT JOIN `sys_menu_templates` AS `tmt` ON `tm`.`template_id`=`tmt`.`id` WHERE 1 ', 'sys_objects_menu', 'id', '', 'active', '', 100, NULL, 'start', '', '', 'tm`.`title,tms`.`title,tmt`.`title', 'auto', '', '', 2147483647, 'BxDevNavigationMenus', 'modules/boonex/developer/classes/BxDevNavigationMenus.php'),
+('bx_developer_nav_sets', 'Sql', 'SELECT * FROM `sys_menu_sets` WHERE 1 ', 'sys_menu_sets', 'set_name', '', '', '', 100, NULL, 'start', '', '', 'title', 'auto', '', '', 2147483647, 'BxDevNavigationSets', 'modules/boonex/developer/classes/BxDevNavigationSets.php'),
+('bx_developer_nav_items', 'Sql', 'SELECT * FROM `sys_menu_items` WHERE 1 ', 'sys_menu_items', 'id', 'order', 'active', '', 100, NULL, 'start_it', '', 'link', 'title_system', 'like', '', '', 2147483647, 'BxDevNavigationItems', 'modules/boonex/developer/classes/BxDevNavigationItems.php');
+
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_developer_nav_menus', 'switcher', '', '10%', 0, '', '', 1),
+('bx_developer_nav_menus', 'title', '_bx_dev_nav_txt_menus_gl_title', '17%', 0, '15', '', 2),
+('bx_developer_nav_menus', 'set_title', '_bx_dev_nav_txt_menus_gl_set_title', '17%', 1, '15', '', 3),
+('bx_developer_nav_menus', 'items', '_bx_dev_nav_txt_menus_gl_items', '10%', 0, '8', '', 4),
+('bx_developer_nav_menus', 'template_title', '_bx_dev_nav_txt_menus_gl_template_title', '17%', 1, '15', '', 5),
+('bx_developer_nav_menus', 'module', '_bx_dev_nav_txt_menus_gl_module', '12%', 0, '10', '', 6),
+('bx_developer_nav_menus', 'actions', '', '17%', 0, '', '', 7),
+('bx_developer_nav_sets', 'title', '_bx_dev_nav_txt_sets_gl_title', '50%', 1, '48', '', 1),
+('bx_developer_nav_sets', 'module', '_bx_dev_nav_txt_sets_gl_module', '15%', 0, '13', '', 2),
+('bx_developer_nav_sets', 'items', '_bx_dev_nav_txt_sets_gl_items', '15%', 0, '13', '', 3),
+('bx_developer_nav_sets', 'actions', '', '20%', 0, '', '', 4),
+('bx_developer_nav_items', 'order', '', '1%', 0, '', '', 1),
+('bx_developer_nav_items', 'switcher', '', '9%', 0, '', '', 2),
+('bx_developer_nav_items', 'icon', '_bx_dev_nav_txt_items_gl_icon', '5%', 0, '', '', 3),
+('bx_developer_nav_items', 'title_system', '_bx_dev_nav_txt_items_gl_title_system', '23%', 1, '23', '', 4),
+('bx_developer_nav_items', 'link', '_bx_dev_nav_txt_items_gl_link', '23%', 0, '23', '', 5),
+('bx_developer_nav_items', 'module', '_bx_dev_nav_txt_items_gl_module', '12%', 0, '12', '', 6),
+('bx_developer_nav_items', 'visible_for_levels', '_bx_dev_nav_txt_items_gl_visible', '10%', 0, '10', '', 7),
+('bx_developer_nav_items', 'actions', '', '17%', 0, '', '', 8);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
+('bx_developer_nav_menus', 'independent', 'add', '_bx_dev_nav_btn_menus_gl_create', '', 0, 1),
+('bx_developer_nav_menus', 'single', 'export', '', 'download', 0, 1),
+('bx_developer_nav_menus', 'single', 'edit', '', 'pencil-alt', 0, 2),
+('bx_developer_nav_menus', 'single', 'delete', '', 'remove', 1, 3),
+('bx_developer_nav_sets', 'independent', 'add', '_bx_dev_nav_btn_sets_gl_create', '', 0, 1),
+('bx_developer_nav_sets', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_nav_sets', 'single', 'delete', '', 'remove', 1, 2),
+('bx_developer_nav_items', 'independent', 'import', '_bx_dev_nav_btn_items_gl_import', '', 0, 1),
+('bx_developer_nav_items', 'independent', 'add', '_bx_dev_nav_btn_items_gl_create', '', 0, 2),
+('bx_developer_nav_items', 'single', 'edit', '', 'pencil-alt', 0, 1),
+('bx_developer_nav_items', 'single', 'delete', '', 'remove', 1, 2),
+('bx_developer_nav_items', 'single', 'show_to', '_bx_dev_nav_btn_items_gl_visible', '', 0, 3);
+
+--
+-- Navigation Builder -> Menus.
+--
+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_developer_nav_menu', @sName, '_bx_dev_nav_txt_menus_menu', '', '', 'do_submit', 'sys_objects_menu', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_nav_menu_add', @sName, 'bx_developer_nav_menu', '_bx_dev_nav_txt_menus_display_add', 0),
+('bx_developer_nav_menu_edit', @sName, 'bx_developer_nav_menu', '_bx_dev_nav_txt_menus_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_developer_nav_menu', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_nav_txt_sys_menus_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'object', '', '', 0, 'text', '_bx_dev_nav_txt_sys_menus_object', '_bx_dev_nav_txt_menus_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_nav_err_menus_object', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'module', '', '', 0, 'select', '_bx_dev_nav_txt_sys_menus_module', '_bx_dev_nav_txt_menus_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_menus_module', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'title', '', '', 0, 'text', '_bx_dev_nav_txt_sys_menus_title', '_bx_dev_nav_txt_menus_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_nav_err_menus_title', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'set_name', '', '', 0, 'select', '_bx_dev_nav_txt_sys_menus_set_name', '_bx_dev_nav_txt_menus_set_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'template_id', '', '', 0, 'select', '_bx_dev_nav_txt_sys_menus_template_id', '_bx_dev_nav_txt_menus_template_id', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_menus_template_id', 'Int', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_menus_deletable', '_bx_dev_nav_txt_menus_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'override_class_name', '', '', 0, 'text', '_bx_dev_nav_txt_sys_menus_override_class_name', '_bx_dev_nav_txt_menus_override_class_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'override_class_file', '', '', 0, 'text', '_bx_dev_nav_txt_sys_menus_override_class_file', '_bx_dev_nav_txt_menus_override_class_file', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'do_submit', '_bx_dev_nav_btn_menus_add', '', 0, 'submit', '_bx_dev_nav_btn_sys_menus_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_menu', @sName, 'cancel', '_bx_dev_nav_btn_menus_cancel', '', 0, 'button', '_bx_dev_nav_btn_sys_menus_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_nav_menu_add', 'object', 2147483647, 1, 1),
+('bx_developer_nav_menu_add', 'module', 2147483647, 1, 2),
+('bx_developer_nav_menu_add', 'title', 2147483647, 1, 3),
+('bx_developer_nav_menu_add', 'set_name', 2147483647, 1, 4),
+('bx_developer_nav_menu_add', 'template_id', 2147483647, 1, 5),
+('bx_developer_nav_menu_add', 'deletable', 2147483647, 1, 6),
+('bx_developer_nav_menu_add', 'override_class_name', 2147483647, 1, 7),
+('bx_developer_nav_menu_add', 'override_class_file', 2147483647, 1, 8),
+('bx_developer_nav_menu_add', 'controls', 2147483647, 1, 9),
+('bx_developer_nav_menu_add', 'do_submit', 2147483647, 1, 10),
+('bx_developer_nav_menu_add', 'cancel', 2147483647, 1, 11),
+('bx_developer_nav_menu_edit', 'id', 2147483647, 1, 1),
+('bx_developer_nav_menu_edit', 'object', 2147483647, 1, 2),
+('bx_developer_nav_menu_edit', 'module', 2147483647, 1, 3),
+('bx_developer_nav_menu_edit', 'title', 2147483647, 1, 4),
+('bx_developer_nav_menu_edit', 'set_name', 2147483647, 1, 5),
+('bx_developer_nav_menu_edit', 'template_id', 2147483647, 1, 6),
+('bx_developer_nav_menu_edit', 'deletable', 2147483647, 1, 7),
+('bx_developer_nav_menu_edit', 'override_class_name', 2147483647, 1, 8),
+('bx_developer_nav_menu_edit', 'override_class_file', 2147483647, 1, 9),
+('bx_developer_nav_menu_edit', 'controls', 2147483647, 1, 10),
+('bx_developer_nav_menu_edit', 'do_submit', 2147483647, 1, 11),
+('bx_developer_nav_menu_edit', 'cancel', 2147483647, 1, 12);
+
+--
+-- Navigation Builder -> Sets.
+--
+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_developer_nav_set', @sName, '_bx_dev_nav_txt_sets_set', '', '', 'do_submit', 'sys_menu_sets', 'set_name', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_nav_set_add', @sName, 'bx_developer_nav_set', '_bx_dev_nav_txt_sets_display_add', 0),
+('bx_developer_nav_set_edit', @sName, 'bx_developer_nav_set', '_bx_dev_nav_txt_sets_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_developer_nav_set', @sName, 'module', '', '', 0, 'select', '_bx_dev_nav_txt_sys_sets_module', '_bx_dev_nav_txt_sets_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_sets_module', 'Xss', '', 0, 0),
+('bx_developer_nav_set', @sName, 'set_name', '', '', 0, 'text', '_bx_dev_nav_txt_sys_sets_set_name', '_bx_dev_nav_txt_sets_set_name', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_nav_err_sets_set_name', 'Xss', '', 0, 0),
+('bx_developer_nav_set', @sName, 'title', '', '', 0, 'text', '_bx_dev_nav_txt_sys_sets_title', '_bx_dev_nav_txt_sets_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_nav_err_sets_title', 'Xss', '', 0, 0),
+('bx_developer_nav_set', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_sets_deletable', '_bx_dev_nav_txt_sets_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_set', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_set', @sName, 'do_submit', '_bx_dev_nav_btn_sets_add', '', 0, 'submit', '_bx_dev_nav_btn_sys_sets_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_set', @sName, 'cancel', '_bx_dev_nav_btn_sets_cancel', '', 0, 'button', '_bx_dev_nav_btn_sys_sets_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_nav_set_add', 'module', 2147483647, 1, 1),
+('bx_developer_nav_set_add', 'set_name', 2147483647, 1, 2),
+('bx_developer_nav_set_add', 'title', 2147483647, 1, 3),
+('bx_developer_nav_set_add', 'deletable', 2147483647, 1, 4),
+('bx_developer_nav_set_add', 'controls', 2147483647, 1, 5),
+('bx_developer_nav_set_add', 'do_submit', 2147483647, 1, 6),
+('bx_developer_nav_set_add', 'cancel', 2147483647, 1, 7),
+('bx_developer_nav_set_edit', 'module', 2147483647, 1, 1),
+('bx_developer_nav_set_edit', 'set_name', 2147483647, 0, 2),
+('bx_developer_nav_set_edit', 'title', 2147483647, 1, 3),
+('bx_developer_nav_set_edit', 'deletable', 2147483647, 1, 4),
+('bx_developer_nav_set_edit', 'controls', 2147483647, 1, 5),
+('bx_developer_nav_set_edit', 'do_submit', 2147483647, 1, 6),
+('bx_developer_nav_set_edit', 'cancel', 2147483647, 1, 7);
+
+--
+-- Navigation Builder -> Items.
+--
+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_developer_nav_item', @sName, '_bx_dev_nav_txt_items_item', '', '', 'do_submit', 'sys_menu_items', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_nav_item_add', @sName, 'bx_developer_nav_item', '_bx_dev_nav_txt_items_display_add', 0),
+('bx_developer_nav_item_edit', @sName, 'bx_developer_nav_item', '_bx_dev_nav_txt_items_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_developer_nav_item', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_nav_txt_sys_items_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'module', '', '', 0, 'select', '_bx_dev_nav_txt_sys_items_module', '_bx_dev_nav_txt_items_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_items_module', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'set_name', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_set_name', '_bx_dev_nav_txt_items_set_name', '', 0, 0, 0, 'a:1:{s:8:"readonly";s:8:"readonly";}', '', '', 'Avail', '', '_bx_dev_nav_err_items_set_name', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'name', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_name', '_bx_dev_nav_txt_items_name', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_items_name', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'title_system', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_title_system', '_bx_dev_nav_txt_items_title_system', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'title', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_title', '_bx_dev_nav_txt_items_title', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_nav_err_items_title', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'link', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_link', '_bx_dev_nav_txt_items_link', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'onclick', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_onclick', '_bx_dev_nav_txt_items_onclick', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'target', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_target', '_bx_dev_nav_txt_items_target', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'icon', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_icon', '_bx_dev_nav_txt_items_icon', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'addon', '', '', 0, 'textarea', '_bx_dev_nav_txt_sys_items_addon', '_bx_dev_nav_txt_items_addon', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'submenu_object', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_submenu_object', '_bx_dev_nav_txt_items_submenu_object', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'submenu_popup', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_submenu_popup', '_bx_dev_nav_txt_items_submenu_popup', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'visibility_custom', '', '', 0, 'textarea', '_bx_dev_nav_txt_sys_items_visibility_custom', '_bx_dev_nav_txt_items_visibility_custom', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'hidden_on', '', '', 0, 'select_multiple', '_bx_dev_nav_txt_sys_items_hidden_on', '_bx_dev_nav_txt_items_hidden_on', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 0, 0),
+('bx_developer_nav_item', @sName, 'hidden_on_pt', '', '', 0, 'select_multiple', '_bx_dev_nav_txt_sys_items_hidden_on_pt', '_bx_dev_nav_txt_items_hidden_on_pt', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 0, 0),
+('bx_developer_nav_item', @sName, 'hidden_on_col', '', '', 0, 'select_multiple', '_bx_dev_nav_txt_sys_items_hidden_on_col', '_bx_dev_nav_txt_items_hidden_on_col', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 0, 0),
+('bx_developer_nav_item', @sName, 'hidden_on_cxt', '', '', 0, 'text', '_bx_dev_nav_txt_sys_items_hidden_on_cxt', '_bx_dev_nav_txt_items_hidden_on_cxt', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_nav_item', @sName, 'primary', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_primary', '_bx_dev_nav_txt_items_primary', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'collapsed', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_collapsed', '_bx_dev_nav_txt_items_collapsed', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'copyable', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_copyable', '_bx_dev_nav_txt_items_copyable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'active_api', '1', '', 0, 'switcher', '_bx_dev_nav_txt_sys_items_active_api', '_bx_dev_nav_txt_items_active_api', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_nav_item', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_item', @sName, 'do_submit', '_bx_dev_nav_btn_items_add', '', 0, 'submit', '_bx_dev_nav_btn_sys_items_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_nav_item', @sName, 'cancel', '_bx_dev_nav_btn_items_cancel', '', 0, 'button', '_bx_dev_nav_btn_sys_items_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_nav_item_add', 'module', 2147483647, 1, 1),
+('bx_developer_nav_item_add', 'set_name', 2147483647, 1, 2),
+('bx_developer_nav_item_add', 'name', 2147483647, 1, 3),
+('bx_developer_nav_item_add', 'title_system', 2147483647, 1, 4),
+('bx_developer_nav_item_add', 'title', 2147483647, 1, 5),
+('bx_developer_nav_item_add', 'link', 2147483647, 1, 6),
+('bx_developer_nav_item_add', 'onclick', 2147483647, 1, 7),
+('bx_developer_nav_item_add', 'target', 2147483647, 1, 8),
+('bx_developer_nav_item_add', 'icon', 2147483647, 1, 9),
+('bx_developer_nav_item_add', 'addon', 2147483647, 1, 10),
+('bx_developer_nav_item_add', 'submenu_object', 2147483647, 1, 11),
+('bx_developer_nav_item_add', 'submenu_popup', 2147483647, 1, 12),
+('bx_developer_nav_item_add', 'visibility_custom', 2147483647, 1, 13),
+('bx_developer_nav_item_add', 'hidden_on', 2147483647, 1, 14),
+('bx_developer_nav_item_add', 'hidden_on_pt', 2147483647, 1, 15),
+('bx_developer_nav_item_add', 'hidden_on_col', 2147483647, 1, 16),
+('bx_developer_nav_item_add', 'hidden_on_cxt', 2147483647, 1, 17),
+('bx_developer_nav_item_add', 'primary', 2147483647, 1, 18),
+('bx_developer_nav_item_add', 'collapsed', 2147483647, 1, 19),
+('bx_developer_nav_item_add', 'copyable', 2147483647, 1, 20),
+('bx_developer_nav_item_add', 'active_api', 2147483647, 1, 21),
+('bx_developer_nav_item_add', 'controls', 2147483647, 1, 22),
+('bx_developer_nav_item_add', 'do_submit', 2147483647, 1, 23),
+('bx_developer_nav_item_add', 'cancel', 2147483647, 1, 24),
+
+('bx_developer_nav_item_edit', 'id', 2147483647, 1, 1),
+('bx_developer_nav_item_edit', 'module', 2147483647, 1, 2),
+('bx_developer_nav_item_edit', 'set_name', 2147483647, 1, 3),
+('bx_developer_nav_item_edit', 'name', 2147483647, 1, 4),
+('bx_developer_nav_item_edit', 'title_system', 2147483647, 1, 5),
+('bx_developer_nav_item_edit', 'title', 2147483647, 1, 6),
+('bx_developer_nav_item_edit', 'link', 2147483647, 1, 7),
+('bx_developer_nav_item_edit', 'onclick', 2147483647, 1, 8),
+('bx_developer_nav_item_edit', 'target', 2147483647, 1, 9),
+('bx_developer_nav_item_edit', 'icon', 2147483647, 1, 10),
+('bx_developer_nav_item_edit', 'addon', 2147483647, 1, 11),
+('bx_developer_nav_item_edit', 'submenu_object', 2147483647, 1, 12),
+('bx_developer_nav_item_edit', 'submenu_popup', 2147483647, 1, 13),
+('bx_developer_nav_item_edit', 'visibility_custom', 2147483647, 1, 14),
+('bx_developer_nav_item_edit', 'hidden_on', 2147483647, 1, 15),
+('bx_developer_nav_item_edit', 'hidden_on_pt', 2147483647, 1, 16),
+('bx_developer_nav_item_edit', 'hidden_on_col', 2147483647, 1, 17),
+('bx_developer_nav_item_edit', 'hidden_on_cxt', 2147483647, 1, 18),
+('bx_developer_nav_item_edit', 'primary', 2147483647, 1, 19),
+('bx_developer_nav_item_edit', 'collapsed', 2147483647, 1, 20),
+('bx_developer_nav_item_edit', 'copyable', 2147483647, 1, 21),
+('bx_developer_nav_item_edit', 'active_api', 2147483647, 1, 22),
+('bx_developer_nav_item_edit', 'controls', 2147483647, 1, 23),
+('bx_developer_nav_item_edit', 'do_submit', 2147483647, 1, 24),
+('bx_developer_nav_item_edit', 'cancel', 2147483647, 1, 25);
+
+--
+-- Page Builder -> Page.
+--
+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_developer_bp_page', @sName, '_bx_dev_bp_txt_page_form', '', '', 'do_submit', 'sys_objects_page', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_bp_page_add', @sName, 'bx_developer_bp_page', '_bx_dev_bp_txt_page_display_add', 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_developer_bp_page', @sName, 'object', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_object', '_bx_dev_bp_txt_page_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_bp_err_page_object', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'module', '', '', 0, 'select', '_bx_dev_bp_txt_sys_page_module', '_bx_dev_bp_txt_page_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_bp_err_page_module', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'title_system', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_title_system', '_bx_dev_bp_txt_page_title_system', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'title', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_title', '_bx_dev_bp_txt_page_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_bp_err_page_title', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'uri', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_uri', '_bx_dev_bp_txt_page_uri', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'url', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_url', '_bx_dev_bp_txt_page_url', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'layout_id', '', '', 0, 'select', '_bx_dev_bp_txt_sys_page_layout_id', '_bx_dev_bp_txt_page_layout_id', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_bp_err_page_layout_id', 'Int', '', 0, 0),
+('bx_developer_bp_page', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_page_deletable', '_bx_dev_bp_txt_page_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_page', @sName, 'override_class_name', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_override_class_name', '_bx_dev_bp_txt_page_override_class_name', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'override_class_file', '', '', 0, 'text', '_bx_dev_bp_txt_sys_page_override_class_file', '_bx_dev_bp_txt_page_override_class_file', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_page', @sName, 'controls', '', 'do_submit,cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_page', @sName, 'do_submit', '_bx_dev_bp_btn_page_add', '', 0, 'submit', '_bx_dev_bp_btn_sys_page_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_page', @sName, 'cancel', '_bx_dev_bp_btn_page_cancel', '', 0, 'button', '_bx_dev_bp_btn_sys_page_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_bp_page_add', 'object', 2147483647, 1, 1),
+('bx_developer_bp_page_add', 'module', 2147483647, 1, 2),
+('bx_developer_bp_page_add', 'title_system', 2147483647, 1, 3),
+('bx_developer_bp_page_add', 'title', 2147483647, 1, 4),
+('bx_developer_bp_page_add', 'uri', 2147483647, 1, 5),
+('bx_developer_bp_page_add', 'url', 2147483647, 1, 6),
+('bx_developer_bp_page_add', 'layout_id', 2147483647, 1, 7),
+('bx_developer_bp_page_add', 'deletable', 2147483647, 1, 8),
+('bx_developer_bp_page_add', 'override_class_name', 2147483647, 1, 9),
+('bx_developer_bp_page_add', 'override_class_file', 2147483647, 1, 10),
+('bx_developer_bp_page_add', 'controls', 2147483647, 1, 11),
+('bx_developer_bp_page_add', 'do_submit', 2147483647, 1, 12),
+('bx_developer_bp_page_add', 'cancel', 2147483647, 1, 13);
+
+--
+-- Page Builder -> Block.
+--
+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_developer_bp_block', @sName, '_bx_dev_bp_txt_block_form', '', '', 'do_submit', 'sys_pages_blocks', 'id', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_bp_block_edit', @sName, 'bx_developer_bp_block', '_bx_dev_bp_txt_block_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_developer_bp_block', @sName, 'id', '0', '', 0, 'hidden', '_bx_dev_bp_txt_sys_block_id', '', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'object', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_object', '_bx_dev_bp_txt_block_object', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:1;s:3:"max";i:64;}', '_bx_dev_bp_err_block_object', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'module', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_module', '_bx_dev_bp_txt_block_module', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_bp_err_block_module', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'title_system', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_title_system', '_bx_dev_bp_txt_block_title_system', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'title', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_title', '_bx_dev_bp_txt_block_title', '', 1, 0, 0, '', '', '', 'Length', 'a:2:{s:3:"min";i:6;s:3:"max";i:100;}', '_bx_dev_bp_err_block_title', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'designbox_id', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_designbox_id', '_bx_dev_bp_txt_block_designbox_id', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_bp_err_block_designbox_id', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'class', '', '', 0, 'text', '_bx_dev_bp_txt_sys_block_class', '_bx_dev_bp_txt_block_class', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+
+('bx_developer_bp_block', @sName, 'async', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_async', '_bx_dev_bp_txt_block_async', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'submenu', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_submenu', '_bx_dev_bp_txt_block_submenu', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'tabs', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_tabs', '_bx_dev_bp_txt_block_tabs', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'hidden_on', '', '', 0, 'select_multiple', '_bx_dev_bp_txt_sys_block_hidden_on', '_bx_dev_bp_txt_block_hidden_on', '', 0, 0, 0, '', '', '', '', '', '', 'Set', '', 0, 0),
+
+('bx_developer_bp_block', @sName, 'visible_for', '', '', 0, 'select', '_bx_dev_bp_txt_sys_block_visible_for', '_bx_dev_bp_txt_block_visible_for', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 0, 0),
+('bx_developer_bp_block', @sName, 'visible_for_levels', '', '', 0, 'checkbox_set', '_bx_dev_bp_txt_sys_block_visible_for_levels', '_bx_dev_bp_txt_block_visible_for_levels', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'deletable', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_deletable', '_bx_dev_bp_txt_block_deletable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'copyable', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_copyable', '_bx_dev_bp_txt_block_copyable', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'active', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_active', '_bx_dev_bp_txt_block_active', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'active_api', '1', '', 0, 'switcher', '_bx_dev_bp_txt_sys_block_active_api', '_bx_dev_bp_txt_block_active_api', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_bp_block', @sName, 'controls', '', 'do_submit,cancel,delete', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_block', @sName, 'do_submit', '_bx_dev_bp_btn_block_add', '', 0, 'submit', '_bx_dev_bp_btn_sys_block_add', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_block', @sName, 'cancel', '_bx_dev_bp_btn_block_cancel', '', 0, 'button', '_bx_dev_bp_btn_sys_block_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";}', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_bp_block', @sName, 'delete', '_bx_dev_bp_btn_block_delete', '', 0, 'button', '_bx_dev_bp_btn_sys_block_delete', '', '', 0, 0, 0, 'a:1:{s:5:"class";s:22:"bx-def-margin-sec-left";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_bp_block_edit', 'id', 2147483647, 1, 1),
+('bx_developer_bp_block_edit', 'object', 2147483647, 1, 2),
+('bx_developer_bp_block_edit', 'module', 2147483647, 1, 3),
+('bx_developer_bp_block_edit', 'title_system', 2147483647, 1, 4),
+('bx_developer_bp_block_edit', 'title', 2147483647, 1, 5),
+('bx_developer_bp_block_edit', 'designbox_id', 2147483647, 1, 6),
+('bx_developer_bp_block_edit', 'async', 2147483647, 1, 7),
+('bx_developer_bp_block_edit', 'submenu', 2147483647, 1, 8),
+('bx_developer_bp_block_edit', 'tabs', 2147483647, 1, 9),
+('bx_developer_bp_block_edit', 'hidden_on', 2147483647, 1, 10),
+('bx_developer_bp_block_edit', 'visible_for', 2147483647, 1, 11),
+('bx_developer_bp_block_edit', 'visible_for_levels', 2147483647, 1, 12),
+('bx_developer_bp_block_edit', 'class', 2147483647, 1, 13),
+('bx_developer_bp_block_edit', 'deletable', 2147483647, 1, 14),
+('bx_developer_bp_block_edit', 'copyable', 2147483647, 1, 15),
+('bx_developer_bp_block_edit', 'active', 2147483647, 1, 16),
+('bx_developer_bp_block_edit', 'active_api', 2147483647, 1, 17),
+('bx_developer_bp_block_edit', 'controls', 2147483647, 1, 18),
+('bx_developer_bp_block_edit', 'do_submit', 2147483647, 1, 19),
+('bx_developer_bp_block_edit', 'cancel', 2147483647, 1, 20),
+('bx_developer_bp_block_edit', 'delete', 2147483647, 1, 21);
+
+--
+-- Polyglot -> Grid descriptors
+--
+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_developer_pgt_manage', 'Sql', 'SELECT * FROM `sys_modules` WHERE 1 ', 'sys_modules', 'id', 'name', '', '', 100, NULL, '', '', '', '', '', '', '', 2147483647, 'BxDevPolyglotManage', 'modules/boonex/developer/classes/BxDevPolyglotManage.php');
+
+INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
+('bx_developer_pgt_manage', 'title', '_bx_dev_pgt_txt_manage_gl_title', '100%', 0, '', '', 1);
+
+INSERT INTO `sys_grid_actions` (`object`, `type`, `name`, `title`, `icon`, `confirm`, `order`) VALUES
+('bx_developer_pgt_manage', 'independent', 'add_keys', '_bx_dev_pgt_btn_manage_gl_add_keys', '', 0, 0),
+('bx_developer_pgt_manage', 'independent', 'recompile', '_bx_dev_pgt_btn_manage_gl_recompile', '', 0, 1),
+('bx_developer_pgt_manage', 'independent', 'restore', '_bx_dev_pgt_btn_manage_gl_restore', '', 0, 2);
+
+--
+-- Polyglot -> Manage.
+--
+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_developer_pgt_keys', @sName, '_bx_dev_pgt_txt_keys_form', '', '', 'do_submit', 'sys_localization_keys', 'ID', '', '', '', 0, 1, 'BxDevFormView', 'modules/boonex/developer/classes/BxDevFormView.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_developer_pgt_keys_add', @sName, 'bx_developer_pgt_keys', '_bx_dev_pgt_txt_keys_display_add', 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_developer_pgt_keys', @sName, 'language', '', '', 0, 'select', '_bx_dev_pgt_txt_sys_keys_language', '_bx_dev_pgt_txt_keys_language', '_bx_dev_pgt_inf_keys_language', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'category', '', '', 0, 'select', '_bx_dev_pgt_txt_sys_keys_category', '_bx_dev_pgt_txt_keys_category', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_pgt_err_keys_category', 'Int', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'content', '', '', 0, 'textarea', '_bx_dev_pgt_txt_sys_keys_content', '_bx_dev_pgt_txt_keys_content', '_bx_dev_pgt_inf_keys_content', 1, 0, 0, '', '', '', 'Avail', '', '_bx_dev_pgt_err_keys_content', 'Xss', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'replace', '1', '', 0, 'switcher', '_bx_dev_pgt_txt_sys_keys_replace', '_bx_dev_pgt_txt_keys_replace', '', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'controls', '', 'do_submit,do_cancel', 0, 'input_set', '', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'do_submit', '_bx_dev_pgt_btn_keys_submit', '', 0, 'submit', '_bx_dev_pgt_btn_sys_keys_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_developer_pgt_keys', @sName, 'do_cancel', '_bx_dev_pgt_btn_keys_cancel', '', 0, 'button', '_bx_dev_pgt_btn_sys_keys_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";}', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_developer_pgt_keys_add', 'language', 2147483647, 1, 1),
+('bx_developer_pgt_keys_add', 'category', 2147483647, 1, 2),
+('bx_developer_pgt_keys_add', 'content', 2147483647, 1, 3),
+('bx_developer_pgt_keys_add', 'replace', 2147483647, 1, 4),
+('bx_developer_pgt_keys_add', 'controls', 2147483647, 1, 5),
+('bx_developer_pgt_keys_add', 'do_submit', 2147483647, 1, 6),
+('bx_developer_pgt_keys_add', 'do_cancel', 2147483647, 1, 7);

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/bp_add_block.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-block">
+    <div class="bx-dev-add-block-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(oData) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        oBxDolStudioPage.processJson(oData);
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/bp_add_page.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-page">
+    <div class="bx-dev-add-page-cnt bx-def-padding-leftright">
+        __form__
+        <script language="javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function(formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function(oData) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        oBxDolStudioPage.processJson(oData);
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 3 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/bp_export.html

@@ -0,0 +1,3 @@
+<div class="bx-dev-bp-export">
+    <div class="bx-dev-bp-export-cnt">__content__</div>
+</div>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_display.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-display">
+    <div class="bx-dev-add-display-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>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_form.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-form">
+    <div class="bx-dev-add-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>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_list.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-list">
+    <div class="bx-dev-add-list-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>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_search_field.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-search-field">
+    <div class="bx-dev-add-search-field-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>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_search_form.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-search-form">
+    <div class="bx-dev-add-search-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>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_add_value.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-value">
+    <div class="bx-dev-add-value-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>

+ 3 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/form_export.html

@@ -0,0 +1,3 @@
+<div class="bx-dev-frm-export">
+    <div class="bx-dev-frm-export-cnt">__content__</div>
+</div>

+ 22 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/nav_add_item.html

@@ -0,0 +1,22 @@
+<div class="bx-dev-add-item">
+    <div class="bx-dev-add-item-cnt">
+        __form__
+        <script language="javascript">
+            $(document).ready(function () {
+            	if($("[bx_grid_action_single = 'delete_icon']").length > 0)
+            	    glGrids.__object__._bindActionsSingle($("#__form_id__"));
+
+                $("#__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>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/nav_add_menu.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-menu">
+    <div class="bx-dev-add-menu-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>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/nav_add_set.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-add-set">
+    <div class="bx-dev-add-set-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>

+ 3 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/nav_export.html

@@ -0,0 +1,3 @@
+<div class="bx-dev-nav-export-form">
+    <div class="bx-dev-nav-export-cnt">__content__</div>
+</div>

+ 19 - 0
modules/boonex/developer/updates/13.0.5_13.0.6/source/template/pgt_add_keys.html

@@ -0,0 +1,19 @@
+<div class="bx-dev-pgt-add-keys">
+    <div class="bx-dev-pgt-add-keys-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/events/updates/13.0.10_13.0.11/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' => '13.0.10',
+    'version_to' => '13.0.11',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * '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_13.0.10_13.0.11/',
+    'home_uri' => 'events_update_13010_13011',
+
+    'module_dir' => 'boonex/events/',
+    'module_uri' => 'events',
+
+    'db_prefix' => 'bx_events_',
+    'class_prefix' => 'BxEvents',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Events',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/events/updates/13.0.10_13.0.11/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);
+    }
+}

+ 634 - 0
modules/boonex/events/updates/13.0.10_13.0.11/source/classes/BxEventsModule.php

@@ -0,0 +1,634 @@
+<?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
+ *
+ * @{
+ */
+
+use Spatie\CalendarLinks\Link;
+
+/**
+ * Events profiles module.
+ */
+class BxEventsModule extends BxBaseModGroupsModule implements iBxDolCalendarService 
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+        
+        $CNF = &$this->_oConfig->CNF;
+        $this->_aSearchableNamesExcept = array_merge($this->_aSearchableNamesExcept, array(
+            $CNF['FIELD_TIMEZONE'],
+            $CNF['FIELD_JOIN_CONFIRMATION'],
+            $CNF['FIELD_REMINDER']
+        ));
+    }
+
+    public function actionCalendarSync($iContentId = 0)
+    {
+        $aContentInfo = (int)$iContentId ? $this->_oDb->getContentInfoById((int)$iContentId) : null;
+        if (!$aContentInfo) {
+            $this->_oTemplate->displayPageNotFound();
+            exit;
+        }
+
+        if (CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedView($aContentInfo)) {
+            $this->_oTemplate->displayAccessDenied();
+            exit;
+        }
+
+        $CNF = $this->_oConfig->CNF;
+
+        if (!isset($aContentInfo['date_start']) || !$aContentInfo['date_start'] || !isset($aContentInfo['date_end']) || !$aContentInfo['date_end'] || !$aContentInfo[$CNF['FIELD_TIMEZONE']] || empty($CNF['OBJECT_METATAGS'])) {
+            $this->_oTemplate->displayPageNotFound();
+            exit;
+        }
+
+        $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
+        $sLocation = $oMetatags && $oMetatags->locationsIsEnabled() ? $oMetatags->locationsString($aContentInfo[$CNF['FIELD_ID']], false) : '';
+
+        $oDateStart = new DateTime('@' . $aContentInfo['date_start']);
+        $oDateEnd = new DateTime('@' . ($aContentInfo['date_end'] > $aContentInfo['repeat_stop'] ? $aContentInfo['date_end'] : $aContentInfo['repeat_stop']));
+
+        $oTz = new DateTimeZone($aContentInfo[$CNF['FIELD_TIMEZONE']]);
+        $oDateStart->setTimezone($oTz);
+        $oDateEnd->setTimezone($oTz);
+
+        $oICalLink = $oDateStart && $oDateEnd ? Link::create(
+            $aContentInfo[$CNF['FIELD_TITLE']],
+            $oDateStart,
+            $oDateEnd
+        ) : null;
+
+        if ($sLocation)
+            $oICalLink->address($sLocation);
+
+        $s = $oICalLink->ics();
+
+        if (!preg_match('/^data:([a-zA-Z0-9\/]+);charset=[a-zA-Z0-9]+;base64,(.*)$/', $s, $m)) {
+            $this->_oTemplate->displayErrorOccured();
+            exit;
+        }
+
+        header('Content-type: ' . $m[1] . '; charset=utf-8');
+        header('Content-Disposition: inline; filename="event.ics"');
+        echo base64_decode($m[2]);
+    }
+
+    public function actionCalendarData()
+    {
+        // check permissions
+        $aSQLPart = array();
+        $iContentId = (int)bx_get('event');
+        $iContextId = (int)bx_get('context_id');
+        
+        if ($iContentId) {
+            $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+            if (CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedView($aContentInfo)) {
+                $this->_oTemplate->displayAccessDenied();
+                exit;
+            }
+            $aSQLPart = array();
+        }
+        else {
+            $oPrivacy = BxDolPrivacy::getObjectInstance($this->_oConfig->CNF['OBJECT_PRIVACY_VIEW']);
+            if($iContextId){
+                if (!$this->serviceIsEnableForContext($iContextId)){
+                    exit;
+                }
+                else{
+                    $aSQLPart = $oPrivacy ? $oPrivacy->getContentByGroupAsSQLPart(- $iContextId) : array();
+                }
+            }
+            else{
+                $aSQLPart = $oPrivacy ? $oPrivacy->getContentPublicAsSQLPart(0, $oPrivacy->getPartiallyVisiblePrivacyGroups()) : array();
+            }
+        }
+
+        // get entries
+        $aEntries = $this->_oDb->getEntriesByDate(bx_get('start'), bx_get('end'), bx_get('event'), $aSQLPart);
+        
+        bx_alert($this->getName(), 'calendar_data', 0, false, array(
+            'event' => $iContentId,
+            'context_id' => $iContextId,
+            'start' => bx_get('start'),
+            'end' =>  bx_get('end'),
+            'sql_part' => &$aSQLPart,
+            'data' => &$aEntries,
+        ));
+        
+        header('Content-Type: application/json; charset=utf-8');
+        echo json_encode($aEntries);
+    }
+    
+    public function serviceIsIcalExportAvaliable($iContentId)
+    {
+        return true;
+        $aContentInfo = $iContentId ? $this->_oDb->getContentInfoById($iContentId) : false;
+        if ($aContentInfo)
+            return false;
+        
+        return $aContentInfo['date_start'] && $aContentInfo['date_end'];
+    }
+    
+    public function decodeDataAPI($aData, $aParams = [])
+    {
+        $CNF = $this->_oConfig->CNF;
+
+        $aResult = parent::decodeDataAPI($aData, $aParams);
+        $aResult = array_merge($aResult, [
+            'date_start' => $aData[$CNF['FIELD_DATE_START']],
+            'date_end' => $aData[$CNF['FIELD_DATE_END']]
+        ]);
+        return $aResult;
+    }
+
+    public function serviceGetCalendarEntries($iProfileId)
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $oConn = BxDolConnection::getObjectInstance($CNF['OBJECT_CONNECTIONS']);
+        $aData = $oConn->getConnectedContent($iProfileId);
+        $aData2 = array(0);
+        foreach($aData as $iProfileId2){
+            $oProfile = BxDolProfile::getInstance($iProfileId2);
+            array_push($aData2, $oProfile->getContentId());
+        }
+        $aSQLPart['where'] = " AND `bx_events_data`.`id` IN(" . implode(',', $aData2) . ")";
+        return $this->_oDb->getEntriesByDate(bx_get('start'), bx_get('end'), null, $aSQLPart);
+	}
+
+    public function serviceGetSafeServices()
+    {
+        $a = parent::serviceGetSafeServices();
+        return array_merge($a, array (
+            'BrowsePastProfiles' => '',
+            'Calendar' => '',
+        ));
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_events Events
+     * @subsection bx_events-browse Browse
+     * @subsubsection bx_events-browse_upcoming_profiles browse_upcoming_profiles
+     * 
+     * @code bx_srv('bx_events', 'browse_upcoming_profiles', [...]); @endcode
+     * 
+     * Browse upcoming events
+     * 
+     * @param $bDisplayEmptyMsg show "empty" message or not if nothing found
+     * @param $bAjaxPaginate use AJAX paginate or not
+     *
+     * @see BxEventsModule::serviceBrowseUpcomingProfiles
+     */
+    /** 
+     * @ref bx_events-browse_upcoming_profiles "browse_upcoming_profiles"
+     */
+    public function serviceBrowseUpcomingProfiles ($aParams = false)
+    {
+        $bDisplayEmptyMsg = false;
+        if(isset($aParams['empty_message'])) {
+            $bDisplayEmptyMsg = (bool)$aParams['empty_message'];
+            unset($aParams['empty_message']);
+        }
+
+        $bAjaxPaginate = true;
+        if(isset($aParams['ajax_paginate'])) {
+            $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
+            unset($aParams['ajax_paginate']);
+        }
+
+        return $this->_serviceBrowse ('upcoming', $aParams, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_events Events
+     * @subsection bx_events-browse Browse
+     * @subsubsection bx_events-browse_upcoming_connected_profiles browse_upcoming_connected_profiles
+     * 
+     * @code bx_srv('bx_events', 'browse_upcoming_connected_profiles', [...]); @endcode
+     * 
+     * Browse upcoming connected (followed) events
+     * 
+     * @param $bDisplayEmptyMsg show "empty" message or not if nothing found
+     * @param $bAjaxPaginate use AJAX paginate or not
+     *
+     * @see BxEventsModule::serviceBrowseUpcomingConnectedProfiles
+     */
+    /** 
+     * @ref bx_events-browse_upcoming_connected_profiles "browse_upcoming_connected_profiles"
+     */
+    public function serviceBrowseUpcomingConnectedProfiles ($aParams = false)
+    {
+        if(!is_array($aParams) || empty($aParams['object']) || empty($aParams['profile']))
+            return '';
+
+        $bDisplayEmptyMsg = false;
+        if(isset($aParams['empty_message'])) {
+            $bDisplayEmptyMsg = (bool)$aParams['empty_message'];
+            unset($aParams['empty_message']);
+        }
+
+        $bAjaxPaginate = true;
+        if(isset($aParams['ajax_paginate'])) {
+            $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
+            unset($aParams['ajax_paginate']);
+        }
+
+        $aDefaults = array(
+            'type' => 'content',
+            'mutual' => false,
+            'profile2' => 0
+        );
+
+        return $this->_serviceBrowse ('upcoming_connected', array_merge($aDefaults, $aParams), BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_events Events
+     * @subsection bx_events-browse Browse
+     * @subsubsection bx_events-browse_past_profiles browse_past_profiles
+     * 
+     * @code bx_srv('bx_events', 'browse_past_profiles', [...]); @endcode
+     * 
+     * Browse past events
+     * 
+     * @param $bDisplayEmptyMsg show "empty" message or not if nothing found
+     * @param $bAjaxPaginate use AJAX paginate or not
+     *
+     * @see BxEventsModule::serviceBrowsePastProfiles
+     */
+    /** 
+     * @ref bx_events-browse_past_profiles "browse_past_profiles"
+     */
+    public function serviceBrowsePastProfiles ($bDisplayEmptyMsg = false, $bAjaxPaginate = true)
+    {
+        return $this->_serviceBrowse ('past', false, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
+    }
+    
+    /**
+     * @page service Service Calls
+     * @section bx_events Events
+     * @subsection bx_events-browse Browse
+     * @subsubsection bx_events-browse_past_profiles browse_past_profiles_by_params
+     * 
+     * @code bx_srv('bx_events', 'browse_past_profiles_by_params', [...]); @endcode
+     * 
+     * Browse past events
+     * 
+     * @param $aParams array with all parameters
+     *
+     * @see BxEventsModule::serviceBrowsePastProfilesByParams
+     */
+    /** 
+     * @ref bx_events-browse_past_profiles_by_params "browse_past_profiles_by_params"
+     */
+    public function serviceBrowsePastProfilesByParams ($aParams)
+    {
+        $bDisplayEmptyMsg = false;
+        if(isset($aParams['empty_message'])) {
+            $bDisplayEmptyMsg = (bool)$aParams['empty_message'];
+            unset($aParams['empty_message']);
+        }
+
+        $bAjaxPaginate = true;
+        if(isset($aParams['ajax_paginate'])) {
+            $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
+            unset($aParams['ajax_paginate']);
+        }
+        return $this->_serviceBrowse ('past', $aParams, BX_DB_PADDING_DEF, $bDisplayEmptyMsg, $bAjaxPaginate);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_events Events
+     * @subsection bx_events-page_blocks Page Blocks
+     * @subsubsection bx_events-calendar calendar
+     * 
+     * @code bx_srv('bx_events', 'calendar', [...]); @endcode
+     * 
+     * Shows event or events calendar
+     * 
+     * @param $aData additional data to point which events to show, leave empty to show all events, specify event's ID in 'event' array key to show calendar for one event only, specify context's ID in 'context_id' array key to show calendar for one context events only. If only one event is specified then it will show calendar only if it's repeating event.
+     * @param $sTemplate template to use to show calendar, or leave empty for default template, possible options: calendar.html, calendar_compact.html
+     * @return HTML string with calendar to display on the site, all necessary CSS and JS files are automatically added to the HEAD section of the site HTML. On error empty string is returned.
+     *
+     * @see BxEventsModule::serviceCalendar
+     */
+    /** 
+     * @ref bx_events-calendar "calendar"
+     */
+    public function serviceCalendar($aData = array(), $sTemplate = 'calendar.html')
+    {
+        if(isset($aData['context_id']) && !$this->serviceIsEnableForContext($aData['context_id']))
+            return '';
+
+        $oCalendar = new BxTemplCalendar([
+            'eventSources' => [
+                bx_append_url_params(BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'calendar_data', $aData),
+            ],
+        ], $this->_oTemplate);
+
+        $this->_oTemplate->addCss(['main.css']);
+        return $oCalendar->display($sTemplate);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_events Events
+     * @subsection bx_events-other Other
+     * @subsubsection bx_events-process_reminders process_reminders
+     * 
+     * @code bx_srv('bx_events', 'process_reminders'); @endcode
+     * 
+     * Send remiders to event's participants.
+     *
+     * It must be processed once every hour for the proper processing.
+     * @return nothing
+     *
+     * @see BxEventsModule::serviceProcessReminders
+     */
+    /** 
+     * @ref bx_events-process_reminders "process_reminders"
+     */
+    public function serviceProcessReminders()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $iNow = time();
+        $bUseIn = $this->_oConfig->isInternalNotifications();
+        $sModule = $this->getName();
+
+        // get all events for today and tomorrow, since the max reminder is 24 hours
+        $aEntries = $this->_oDb->getEntriesByDate('@' . time(), '@' . (time() + 3600 * getParam('bx_events_reminder_interval')));
+
+        foreach($aEntries as $aEntry) {
+            $aReminders = $bUseIn ? [$aEntry['reminder']] : [24, 1];
+            if(!$aReminders)
+                continue;
+
+            $oEventProfile = BxDolProfile::getInstanceByContentAndType($aEntry[$CNF['FIELD_ID']], $sModule);
+            if(!$oEventProfile)
+                continue;
+
+            $iEventProfileId = $oEventProfile->id();
+            foreach($aReminders as $iReminder) {
+                $iTimestamp = $aEntry['start_utc'] - (3600 * $iReminder);
+                if($iNow <= ($iTimestamp - 3600) || $iNow >= $iTimestamp)
+                    continue;
+
+                if($bUseIn)
+                    $this->sendReminders($aEntry);
+
+                bx_alert($sModule, 'reminder', $aEntry[$CNF['FIELD_ID']], $iEventProfileId, array(
+                    'object_author_id' => $iEventProfileId,
+                    'reminder' => $iReminder
+                ));
+            }
+        }
+    }
+
+    public function serviceGetNotificationsData()
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aResults = parent::serviceGetNotificationsData();
+        
+        $sModule = $this->_aModule['name'];
+        $aSettingsTypes = ['follow_context'];
+
+        $aResults['handlers'] = array_merge($aResults['handlers'], [
+            ['group' => $sModule . '_reminder', 'type' => 'insert', 'alert_unit' => $sModule, 'alert_action' => 'reminder', 'module_name' => $sModule, 'module_method' => 'get_notifications_reminder', 'module_class' => 'Module', 'module_event_privacy' => $CNF['OBJECT_PRIVACY_VIEW_NOTIFICATION_EVENT']],
+        ]);
+
+        $aResults['settings'] = array_merge($aResults['settings'], [
+            ['group' => 'reminder', 'unit' => $sModule, 'action' => 'reminder', 'types' => $aSettingsTypes],
+        ]);
+
+        $aResults['alerts'] = array_merge($aResults['alerts'], [
+            ['unit' => $sModule, 'action' => 'reminder'],
+        ]);
+
+        return $aResults;
+    }
+
+    public function serviceGetNotificationsInsertData($oAlert, $aHandler, $aDataItems)
+    {
+        if($oAlert->sAction != 'reminder' || empty($aDataItems) || !is_array($aDataItems) || empty($oAlert->aExtras['reminder']))
+            return $aDataItems;
+
+        foreach($aDataItems as $iIndex => $aDataItem) {
+            $aContent = [];
+            if(!empty($aDataItem['content']))
+                $aContent = unserialize($aDataItem['content']);
+
+            $aContent['reminder'] = (int)$oAlert->aExtras['reminder'];
+
+            $aDataItems[$iIndex]['content'] = serialize($aContent);
+        }
+
+        return $aDataItems;
+    }
+
+    public function serviceGetNotificationsReminder($aEvent)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!isset($aEvent['content']['reminder']))
+            return [];
+
+        $iContentId = (int)$aEvent['object_id'];
+        $oEventProfile = BxDolProfile::getInstanceByContentAndType((int)$iContentId, $this->getName());
+        if(!$oEventProfile)
+            return [];
+
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return [];
+
+        return [
+            'entry_sample' => $CNF['T']['txt_sample_single'],
+            'entry_url' => bx_absolute_url(str_replace(BX_DOL_URL_ROOT, '', $oEventProfile->getUrl()), '{bx_url_root}'),
+            'entry_caption' => $oEventProfile->getDisplayName(),
+            'entry_author' => $oEventProfile->id(),
+            'lang_key' => '_bx_events_txt_ntfs_reminder_' . $aEvent['content']['reminder']
+        ];
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_events Events
+     * @subsection bx_events-internal Internal
+     * @subsubsection bx_events-get_timeline_post get_timeline_post
+     *
+     * @code bx_srv('bx_events', 'get_timeline_post', [...]); @endcode
+     * 
+     * Get Timeline post. It's needed for Timeline module.
+     * 
+     * @param $aEvent timeline event array from Timeline module
+     * @return array in special format which is needed specifically for Timeline module to display the data.
+     *
+     * @see BxEventsModule::serviceGetTimelinePost
+     */
+    /** 
+     * @ref bx_events-get_timeline_post "get_timeline_post"
+     */
+    public function serviceGetTimelinePost($aEvent, $aBrowseParams = array())
+    {
+        $a = parent::serviceGetTimelinePost($aEvent, $aBrowseParams);
+        if($a === false)
+            return false;
+
+        $aContentInfo = $this->_oDb->getContentInfoById($aEvent['object_id']);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return $a;
+
+        $CNF = $this->_oConfig->CNF;
+
+        $oDateStart = date_create('@' . $aContentInfo['date_start']);
+        if ($oDateStart)
+            $oDateStart->setTimezone(new DateTimeZone($aContentInfo['timezone'] ? $aContentInfo['timezone'] : 'UTC'));
+
+        $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
+        $sLocationString = $oMetatags ? $oMetatags->locationsString($aContentInfo[$CNF['FIELD_ID']], false) : false;
+
+        $a['content']['raw'] = $this->_oTemplate->parseHtmlByName('timeline_post.html', [
+            'title' => $a['content']['title'],
+            'title_attr' => bx_html_attribute($a['content']['title']),
+            'url' => $a['content']['url'],
+            'bx_if:date' => [
+                'condition' => $oDateStart,
+                'content' => [
+                    'date' => $oDateStart ? bx_time_js($aContentInfo['date_start'], BX_FORMAT_DATE_TIME, true) : '',
+                    'date_c' => $oDateStart->format('c'),
+            ]],
+            'bx_if:location' => array(
+                'condition' => !!$sLocationString,
+                'content' => array('location' => $sLocationString),
+            ),
+        ]);
+
+        return $a;
+    }
+    
+    public function actionIntervals()
+    {
+        $sAction = bx_get('a');
+        $sMethodName = 'subaction' . ucfirst($sAction);
+        if (!method_exists($this, $sMethodName)) {
+            $this->_oTemplate->pageNotFound();
+            return;
+        }
+        $this->$sMethodName();
+    }
+
+    public function subactionRestore($iContentId = 0)
+    {
+        if (!$iContentId)
+            $iContentId = (int)bx_get('c');
+
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+
+        if (
+            ($aContentInfo && CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedEdit ($aContentInfo))
+            ||
+            (!$aContentInfo && CHECK_ACTION_RESULT_ALLOWED !== $this->checkAllowedAdd ())
+        ) {
+            $this->_oTemplate->displayAccessDenied();
+            exit;
+        }
+
+        $a = $this->_oDb->getIntervals($iContentId);
+        foreach ($a as $iIntervalId => $r) {
+            $a[$iIntervalId]['file_id'] = $r['interval_id'];
+            $a[$iIntervalId]['repeat_stop'] = bx_process_output ($r['repeat_stop'], BX_DATA_DATE_TS);
+        }
+
+        if ('json' == bx_get('f')) {
+            header('Content-type: text/html; charset=utf-8');
+            echo json_encode($a);
+        }
+    }
+
+    public function subactionDelete()
+    {
+        header('Content-type: text/html; charset=utf-8');
+
+        $iIntervalId = (int)bx_get('id');
+
+        if (!($aContentInfo = $this->_oDb->getContentInfoByIntervalId($iIntervalId))) {
+            echo _t('_sys_request_page_not_found_cpt');
+        }
+        elseif (CHECK_ACTION_RESULT_ALLOWED !== ($sMsg = $this->checkAllowedEdit ($aContentInfo))) {
+            echo $sMsg;
+        } 
+        elseif (!$this->_oDb->deleteIntervalById($iIntervalId)) {
+            echo _t('_sys_txt_error_occured');
+        } 
+        else {
+            echo 'ok';
+        }
+    }
+
+    public function checkAllowedIcalExport ($aDataEntry, $isPerformAction = false)
+    {
+        return $aDataEntry['date_start'] && $aDataEntry['date_end'] ? CHECK_ACTION_RESULT_ALLOWED : _t('_sys_txt_access_denied');
+    }
+
+    /**
+     * Send reminder to event's paritcipants
+     * @param $aContentInfo content info
+     * @return number of sent reminders
+     */ 
+    protected function sendReminders($aContentInfo)
+    {
+        $CNF = $this->_oConfig->CNF;
+
+        $oConnection = BxDolConnection::getObjectInstance($CNF['OBJECT_CONNECTIONS']);
+        if (!$oConnection)
+            return false;
+
+        $oGroupProfile = BxDolProfile::getInstanceByContentAndType($aContentInfo[$CNF['FIELD_ID']], $this->getName());
+        if (!$oGroupProfile)
+            return false;
+
+        $aParticipants = $oConnection->getConnectedInitiators($oGroupProfile->id(), true);
+        if (!$aParticipants)
+            return true;
+
+        $oDateBegin = new DateTime();
+        $oDateBegin->setTimestamp($aContentInfo['start_utc']);
+        $oDateBegin->setTimezone(new DateTimeZone($aContentInfo['timezone'] ? $aContentInfo['timezone'] : 'UTC'));
+        $sEntryBegin = $oDateBegin->format('r');
+        $sEntryBeginShort = date(getParam('bx_events_short_date_format'), $oDateBegin->getTimestamp());
+        $oDateBegin->setTimezone(new DateTimeZone('UTC'));
+        $sEntryBeginUTC = $oDateBegin->format('c');
+
+        $oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
+        $sLocationString = $oMetatags->locationsString($aContentInfo[$CNF['FIELD_ID']], false);
+
+        $iCounter = 0;
+        foreach ($aParticipants as $iProfileId) {
+
+            $b = sendMailTemplate($CNF['EMAIL_REMINDER'], 0, $iProfileId, array(
+                'EntryUrl' => bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_ENTRY'] . '&id=' . $aContentInfo[$CNF['FIELD_ID']])),
+                'EntryTitle' => $aContentInfo['title'],
+                'EntryBegin' => $sEntryBegin,
+                'EntryBeginShort' => $sEntryBeginShort,
+                'EntryBeginUTC' => $sEntryBeginUTC,
+                'EntryLocation' => $sLocationString ? _t('_bx_events_email_reminder_location', $sLocationString) : '',
+            ), BX_EMAIL_NOTIFY);
+
+            $iCounter += $b ? 1 : 0;
+        }
+        return $iCounter;
+    }
+}
+
+/** @} */

+ 152 - 0
modules/boonex/events/updates/13.0.10_13.0.11/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' => '13.0.11',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * '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',
+    ),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/files/updates/13.0.9_13.0.10/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' => '13.0.9',
+    'version_to' => '13.0.10',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * '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_13.0.9_13.0.10/',
+    'home_uri' => 'files_update_1309_13010',
+
+    '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/13.0.9_13.0.10/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);
+    }
+}

+ 546 - 0
modules/boonex/files/updates/13.0.9_13.0.10/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->serviceIsAllowedAddContentToProfile($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,
+                ],
+            ],
+        ]));
+    }
+}
+
+/** @} */

+ 135 - 0
modules/boonex/files/updates/13.0.9_13.0.10/source/install/config.php

@@ -0,0 +1,135 @@
+<?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' => '13.0.10',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * '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',
+    ),
+
+	/**
+     * 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/forum/updates/13.0.12_13.0.13/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' => 'Discussions',
+    'version_from' => '13.0.12',
+    'version_to' => '13.0.13',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/forum/updates/update_13.0.12_13.0.13/',
+    'home_uri' => 'forum_update_13012_13013',
+
+    'module_dir' => 'boonex/forum/',
+    'module_uri' => 'forum',
+
+    'db_prefix' => 'bx_forum_',
+    'class_prefix' => 'BxForum',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Discussions',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/forum/updates/13.0.12_13.0.13/install/installer.php

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

+ 244 - 0
modules/boonex/forum/updates/13.0.12_13.0.13/source/classes/BxForumMenuCategories.php

@@ -0,0 +1,244 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaBaseView UNA Base Representation Classes
+ * @{
+ */
+
+/**
+ * Menu representation.
+ * @see BxDolMenu
+ */
+class BxForumMenuCategories extends BxTemplMenu
+{
+    protected $_sModule;
+    protected $_oModule;
+
+    protected $_iMenuItemsMin;
+
+    public function __construct ($aObject, $oTemplate)
+    {
+        parent::__construct ($aObject, $oTemplate);
+
+        $this->_sModule = 'bx_forum';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+        $this->_bDisplayAddons = true;
+    }
+
+    public function getMenuItems ()
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        
+        $aItems = [];
+        
+        $aCategoriest = bx_srv('system', 'categories_list', ['bx_forum_cats', ['show_empty' => true]], 'TemplServiceCategory');
+        
+        if (!($o = BxDolCategory::getObjectInstance('bx_forum_cats')))
+            return $aItems;
+
+		$aCategories = $o->getCategoriesList(false, true);
+        
+		if(!isset($aCategories['bx_repeat:cats']))
+			return $aItems;
+        
+        $iCount = 0;
+        foreach ($aCategories['bx_repeat:cats'] as $sKey => $aCategory) {
+            $iCount +=  $aCategories['bx_repeat:cats'][$sKey]['num'];
+        }
+        
+        $aItems[] =  [
+            'class_add' => 'bx-psmi-show-0' .  (bx_get('category') == '' ? ' bx-menu-item-active' : ''),
+            'name' => 'show-0',
+            'title' => _t('_bx_forum_txt_all_categories'),
+            'link' => BxDolPermalinks::getInstance()->permalink($CNF['URL_HOME']),
+            'bx_if:onclick' => [
+                'condition' => false,
+                'content' => [
+                    'onclick' => 'javascript:',
+                ]
+            ],
+            'attrs' => '',
+            'bx_if:image' => array (
+                'condition' => false,
+                'content' => [],
+            ),
+            'bx_if:image_inline' => array (
+                'condition' => false,
+                'content' => [],
+            ),
+            'bx_if:icon' => array (
+                'condition' => true,
+                'content' => ['icon' => 'swatchbook'],
+            ),
+            'bx_if:icon-a' => array (
+                'condition' => false,
+                'content' => [],
+            ),
+            'bx_if:icon-html' => array (
+                'condition' => false,
+                'content' => [],
+            ),
+            'bx_if:addon' => [
+                'condition' => true,
+                'content' => ['addon' => $iCount]
+            ]
+        ];
+
+        foreach ($aCategories['bx_repeat:cats'] as $sKey => $aCategory) {
+            $aCategoryData = $this->_oModule->_oDb->getCategories(['type' => 'by_category', 'category' => $aCategory['value']]);
+            if(empty($aCategoryData) || (!empty($aCategoryData['visible_for_levels']) && BxDolAcl::getInstance()->isMemberLevelInSet($aCategoryData['visible_for_levels']))) {
+                
+                $aCategories['bx_repeat:cats'][$sKey]['icon'] = $this->_oTemplate->getImage(isset($aCategoryData['icon']) ? $aCategoryData['icon'] : 'folder', array('class' => 'sys-icon sys-colored'));
+                
+                if (!isset($aCategoryData['icon']) || $aCategoryData['icon'] == '')
+                    $aCategoryData['icon'] = 'folder';
+                
+                list($sIcon, $sIconUrl, $sIconA, $sIconHtml) = BxTemplFunctions::getInstance()->getIcon($aCategoryData['icon']);
+                
+                $aItems[] =  [
+                    // TODO
+                    'class_add' => 'bx-psmi-show-' . $aCategories['bx_repeat:cats'][$sKey]['value'] . (bx_get('category') == $aCategories['bx_repeat:cats'][$sKey]['value'] ? ' bx-menu-item-active' : ''),
+                    'name' => 'show-' . $aCategories['bx_repeat:cats'][$sKey]['value'],
+                    'title' => $aCategories['bx_repeat:cats'][$sKey]['name'],
+                    'link' => $aCategories['bx_repeat:cats'][$sKey]['url'],
+                    'bx_if:onclick' => [
+                        'condition' => false,
+                        'content' => [
+                            'onclick' => 'javascript:',
+                        ]
+                    ],
+                    'attrs' => '',
+                    'bx_if:image' => array (
+                        'condition' => (bool)$sIconUrl,
+                        'content' => array('icon_url' => $sIconUrl),
+                    ),
+                    'bx_if:image_inline' => array (
+                        'condition' => false,
+                        'content' => array('image' => ''),
+                    ),
+                    'bx_if:icon' => array (
+                        'condition' => (bool)$sIcon,
+                        'content' => array('icon' => $sIcon),
+                    ),
+                    'bx_if:icon-a' => array (
+                        'condition' => (bool)$sIconA,
+                        'content' => array('icon-a' => $sIconA),
+                    ),
+                    'bx_if:icon-html' => array (
+                        'condition' => (bool)$sIconHtml,
+                        'content' => array('icon' => $sIconHtml),
+                    ),
+                    'bx_if:addon' => [
+                        'condition' => true,
+                        'content' => ['addon' => $aCategories['bx_repeat:cats'][$sKey]['num']]
+                    ]
+                ];
+            }
+        }
+
+        if(empty($aItems) || !is_array($aItems))
+            return $aItems;
+
+        $iMaxNum = count($aItems);
+        $iMenuItemsMin = (int)getParam('bx_forum_visible_categories');
+        if($iMaxNum <= $iMenuItemsMin)
+            return $aItems;
+
+        $mixedCollpsed = $this->getUserChoiceCollapsed();
+        $bCollpsed = $mixedCollpsed === false || $mixedCollpsed == 1;
+
+        for($i = $iMenuItemsMin; $i < $iMaxNum; $i++)
+            $aItems[$i]['class_add'] .= ' bx-mi-aux' . ($bCollpsed ? ' bx-mi-hidden' : '');
+
+        $aShowMoreLinks = [
+            'more' => ['title' => '_sys_show_more', 'icon' => 'chevron-down', 'class' => $bCollpsed ? '' : 'bx-mi-hidden'],
+            'less' => ['title' => '_sys_show_less', 'icon' => 'chevron-up', 'class' => !$bCollpsed ? '' : 'bx-mi-hidden']
+        ];
+
+        foreach($aShowMoreLinks as $sLink => $aLink)
+            $aItems[] = array(
+                'class_add' => 'bx-psmi-show-' . $sLink . ' ' . $aLink['class'],
+                'name' => 'show-' . $sLink,
+                'title' => _t($aLink['title']),
+                'link' => 'javascript:void(0)',
+                'bx_if:onclick' => [
+                    'condition' => true,
+                    'content' => [
+                        'onclick' => 'bx_menu_show_more_less(this, \'' . $this->_sObject . '\', \'.bx-menu-object-' . $this->_sObject . '\')',
+                    ]
+                ],
+                'attrs' => '',
+                'bx_if:image' => [
+                    'condition' => false,
+                    'content' => ['icon_url' => ''],
+                ],
+                'bx_if:image_inline' => [
+                    'condition' => false,
+                    'content' => ['image' => ''],
+                ],
+                'bx_if:icon' => [
+                    'condition' => true,
+                    'content' => ['icon' => $aLink['icon']],
+                ],
+                'bx_if:icon-html' => [
+                    'condition' => false,
+                    'content' => ['icon-a' => ''],
+                ],
+                'bx_if:icon-a' => [
+                    'condition' => false,
+                    'content' => ['icon-a' => ''],
+                ],
+                'bx_if:addon' => [
+                    'condition' => false,
+                    'content' => []
+                ]
+            );
+
+        return $aItems;
+    }
+
+    protected function getMenuItemsRaw ()
+    {
+        $aItems = $this->_oQuery->getMenuItemsBy(array(
+            'type' => 'set_name', 
+            'set_name' => $this->_aObject['set_name']
+        ));
+
+        $aDuplicates = $this->_oQuery->getMenuItemsBy(array(
+            'type' => 'set_name_duplicates', 
+            'set_name' => $this->_aObject['set_name']
+        ));
+
+        $oProfile = BxDolProfile::getInstance();
+        if(!$oProfile)
+            return array();
+
+        $sModule = $oProfile->getModule();
+
+        $aResult = array();
+        foreach($aItems as $aItem) {
+            if(in_array($aItem['name'], $aDuplicates) && $aItem['module'] != $sModule)
+                continue;
+            
+            $aResult[$aItem['name']] = $aItem;
+        }
+
+        return $aResult;
+    }
+    
+    protected function _getMenuItem($a)
+    {
+        $mixedResult = parent::_getMenuItem($a);
+
+        if($mixedResult !== false && !empty($mixedResult['link']) && strpos($mixedResult['link'], 'javascript:') === false)
+            $mixedResult['link'] = bx_append_url_params($mixedResult['link'], [
+                'owner' => 1
+            ]);
+
+        return $mixedResult;
+    }
+}
+
+/** @} */

+ 1068 - 0
modules/boonex/forum/updates/13.0.12_13.0.13/source/classes/BxForumModule.php

@@ -0,0 +1,1068 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Forum Forum
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxForumModule extends BxBaseModTextModule
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+
+    public function sortParticipants ($aParticipants, $iProfileIdLastComment, $iProfileIdAuthor, $iProfileIdCurrent = 0)
+    {
+        if (!$iProfileIdCurrent)
+            $iProfileIdCurrent = bx_get_logged_profile_id();
+
+        $aMoveUp = array($iProfileIdCurrent, $iProfileIdLastComment, $iProfileIdAuthor);
+
+        asort($aParticipants, SORT_NUMERIC);
+
+        foreach ($aMoveUp as $iProfileId) {
+            if (!isset($aParticipants[$iProfileId]))
+                continue;
+
+            $a = array($iProfileId => $aParticipants[$iProfileId]);
+            unset($aParticipants[$iProfileId]);
+            $aParticipants = $a + $aParticipants;
+        }
+
+        return $aParticipants;
+    }
+
+    /**
+     * Action methods
+     */
+    public function actionUpdateStatus($sAction = '', $iContentId = 0)
+    {
+        if(empty($sAction) && bx_get('action') !== false)
+            $sAction = bx_process_input(bx_get('action'));
+
+        if(empty($iContentId) && bx_get('id') !== false)
+            $iContentId = (int)bx_get('id');
+
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(empty($aContentInfo) || !is_array($aContentInfo))
+            return echoJson(array('code' => 1, 'message' => _t('_bx_forum_err_not_found')));
+
+        $sMethodCheck = 'checkAllowed' . bx_gen_method_name($sAction) . 'AnyEntry';
+        $sResult = $this->$sMethodCheck($aContentInfo);
+        if($sResult !== CHECK_ACTION_RESULT_ALLOWED)
+            return echoJson(array('code' => 2, 'message' => $sResult));
+
+        bx_audit(
+            $iContentId, 
+            $this->getName(), 
+            '_sys_audit_action_' . $sAction,  
+            $this->_prepareAuditParams($aContentInfo, false)
+        );
+        
+        if(!$this->_oDb->updateStatus($sAction, $aContentInfo))
+            return echoJson(array('code' => 3, 'message' => _t('_error occured')));
+
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        $this->alertAfterEdit($aContentInfo);
+
+        echoJson(array('code' => 0, 'id' => $iContentId, 'reload' => 1));
+    }
+
+    public function actionAjaxGetAuthors()
+    {
+        $aResult = BxDolService::call('system', 'profiles_search', array(bx_get('term')), 'TemplServiceProfiles');
+
+        header('Content-Type:text/javascript; charset=utf-8');
+        echo json_encode($aResult);
+    }
+
+    /**
+     * Service methods
+     */
+
+    public function serviceGetSafeServices()
+    {
+        $a = parent::serviceGetSafeServices();
+        return array_merge($a, array (
+            'BrowseNew' => '',
+            'BrowseLatest' => '',
+            'BrowseTop' => '',
+            'BrowsePopular' => '',
+            'BrowseUpdated' => '',
+            'BrowsePartaken' => '',
+            'BrowseIndex' => '',
+            'Search' => '',
+        ));
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-other Other
+     * @subsubsection bx_forum-get_searchable_fields get_searchable_fields
+     * 
+     * @code bx_srv('bx_forum', 'get_searchable_fields', [...]); @endcode
+     * 
+     * Get searchable fields for Studio settings.
+     * 
+     * @return an array with key-value pairs to be used in settings dropdown field.
+     * 
+     * @see BxForumModule::serviceGetSearchableFields
+     */
+    /** 
+     * @ref bx_forum-get_searchable_fields "get_searchable_fields"
+     */
+    public function serviceGetSearchableFields ($aInputsAdd = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $aResult = parent::serviceGetSearchableFields($aInputsAdd);
+        $aResult[$CNF['FIELD_TEXT_COMMENTS']] = _t('_bx_forum_form_entry_input_text_comments');
+
+        return $aResult;
+    }
+
+    /**
+     * Service methods
+     */
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-other Other
+     * @subsubsection bx_forum-get_info get_info
+     * 
+     * @code bx_srv('bx_forum', 'get_info', [...]); @endcode
+     * 
+     * Get content info by content ID. Is used in "Content Info Objects" system.
+     * 
+     * @param $iContentId integer value with content ID.
+     * @param $bSearchableFieldsOnly (optional) boolean value determining all info or "searchable fields" only will be returned.
+     * @return an array with content info. Empty array is returned if something is wrong.
+     * 
+     * @see BxForumModule::serviceGetInfo
+     */
+    /** 
+     * @ref bx_forum-get_info "get_info"
+     */
+    public function serviceGetInfo ($iContentId, $bSearchableFieldsOnly = true)
+    {
+        $aContentInfo = $this->_getFields($iContentId);
+        if(empty($aContentInfo))
+            return array();
+
+        return $aContentInfo;
+    }
+    
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_new browse_new
+     * 
+     * @code bx_srv('bx_forum', 'browse_new', [...]); @endcode
+     * 
+     * Get page block with a list of items ordered by newness and represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseNew
+     */
+    /** 
+     * @ref bx_forum-browse_new "browse_new"
+     */
+	public function serviceBrowseNew ($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true, $bShowHeader = false)
+    {
+    	$sType = 'new';
+
+    	if($sUnitView != 'table')   
+        	return $this->_serviceBrowse($sType, $sUnitView ? array('unit_view' => $sUnitView) : false, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType,
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), $bShowHeader);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_latest browse_latest
+     * 
+     * @code bx_srv('bx_forum', 'browse_latest', [...]); @endcode
+     * 
+     * Get page block with a list of items ordered by Recent Posts in them and represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @param $bShowHeader (optional) boolean value determining whether a resulting table should have Header section or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseNew
+     */
+    /** 
+     * @ref bx_forum-browse_latest "browse_latest"
+     */
+    public function serviceBrowseLatest($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true, $bShowHeader = true)
+    {
+        $sType = 'latest';
+
+        if($sUnitView != 'table')
+            return $this->_serviceBrowse($sType, $sUnitView ? array('unit_view' => $sUnitView) : false, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType,
+            'author' => bx_get_logged_profile_id(),
+            'empty_message' => $bEmptyMessage, 
+            'ajax_paginate' => $bAjaxPaginate
+        ), $bShowHeader);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_featured browse_featured
+     * 
+     * @code bx_srv('bx_forum', 'browse_featured', [...]); @endcode
+     * 
+     * Get page block with a list of featured items represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @param $bShowHeader (optional) boolean value determining whether a resulting table should have Header section or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseFeatured
+     */
+    /** 
+     * @ref bx_forum-browse_featured "browse_featured"
+     */
+    public function serviceBrowseFeatured($sUnitView = false, $bEmptyMessage = false, $bAjaxPaginate = true, $bShowHeader = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+    	$sType = 'featured';
+
+    	if($sUnitView != 'table')
+            return $this->_serviceBrowse($sType, $sUnitView ? array('unit_view' => $sUnitView) : false, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType, 
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), $bShowHeader);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_top browse_top
+     * 
+     * @code bx_srv('bx_forum', 'browse_top', [...]); @endcode
+     * 
+     * Get page block with a list of items ordered by a number of comments and represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseTop
+     */
+    /** 
+     * @ref bx_forum-browse_top "browse_top"
+     */
+    public function serviceBrowseTop($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true, $bShowHeader = true)
+    {
+        $sType = 'top';
+
+        if($sUnitView != 'table')
+            return $this->_serviceBrowse($sType, $sUnitView ? array('unit_view' => $sUnitView) : false, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType,
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), $bShowHeader);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_popular browse_popular
+     * 
+     * @code bx_srv('bx_forum', 'browse_popular', [...]); @endcode
+     * 
+     * Get page block with a list of items ordered by a number of views and represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowsePopular
+     */
+    /** 
+     * @ref bx_forum-browse_popular "browse_popular"
+     */
+    public function serviceBrowsePopular ($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true, $bShowHeader = false)
+    {
+        $sType = 'popular';
+
+        if($sUnitView != 'table')
+            return $this->_serviceBrowse($sType, $sUnitView ? array('unit_view' => $sUnitView) : false, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType,
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), $bShowHeader);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_updated browse_updated
+     * 
+     * @code bx_srv('bx_forum', 'browse_updated', [...]); @endcode
+     * 
+     * Get page block with a list of updated items represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseUpdated
+     */
+    /** 
+     * @ref bx_forum-browse_updated "browse_updated"
+     */
+    public function serviceBrowseUpdated ($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true, $bShowHeader = false)
+    {
+        $sType = 'updated';
+
+        if($sUnitView != 'table')
+            return $this->_serviceBrowse($sType, $sUnitView ? array('unit_view' => $sUnitView) : false, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType,
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), $bShowHeader);
+    }
+
+        /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_partaken browse_partaken
+     * 
+     * @code bx_srv('bx_forum', 'browse_partaken', [...]); @endcode
+     * 
+     * Get page block with a list of partaken items represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowsePartaken
+     */
+    /** 
+     * @ref bx_forum-browse_partaken "browse_partaken"
+     */
+    public function serviceBrowsePartaken ($iProfileId = 0, $aParams = [])
+    {
+        $sType = 'partaken';
+
+        if(!$iProfileId)
+            $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
+        if(!$iProfileId)
+            $iProfileId = bx_get_logged_profile_id();
+
+        $bEmptyMessage = true;
+        if(isset($aParams['empty_message'])) {
+            $bEmptyMessage = (bool)$aParams['empty_message'];
+            unset($aParams['empty_message']);
+        }
+
+        $bAjaxPaginate = true;
+        if(isset($aParams['ajax_paginate'])) {
+            $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
+            unset($aParams['ajax_paginate']);
+        }
+        
+        $bShowHeader = false;
+        if(isset($aParams['show_header'])) {
+            $bShowHeader = (bool)$aParams['show_header'];
+            unset($aParams['show_header']);
+        }
+
+        if(isset($aParams['unit_view']) && $aParams['unit_view'] != 'table')
+            return $this->_serviceBrowse($sType, array_merge(['author' => $iProfileId], $aParams), BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable([
+            'type' => $sType,
+            'author' => $iProfileId,
+            'include_contexts' => isset($aParams['include_contexts']) && $aParams['include_contexts'],
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ], $bShowHeader);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_index browse_index
+     * 
+     * @code bx_srv('bx_forum', 'browse_index', [...]); @endcode
+     * 
+     * Get page block for Site's Home page with a list of items ordered by newness and represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @param $bShowHeader (optional) boolean value determining whether a resulting table should have Header section or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseIndex
+     */
+    /** 
+     * @ref bx_forum-browse_index "browse_index"
+     */
+    public function serviceBrowseIndex($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true, $bShowHeader = false)
+    {
+    	$sType = 'index';
+
+    	if($sUnitView != 'table')
+            return $this->_serviceBrowse($sType, $sUnitView ? array('unit_view' => $sUnitView) : false, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType,
+            'per_page' => (int)$this->_oDb->getParam('bx_forum_per_page_index'),
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), $bShowHeader);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_author browse_author
+     * 
+     * @code bx_srv('bx_forum', 'browse_author', [...]); @endcode
+     * 
+     * Get page block with a list of items filtered by Author and represented as table.
+     * 
+     * @param $iProfileId (optional) integer value with author ID. If empty value is provided, an attempt to get it from GET/POST arrays will be performed.
+     * @param $aParams (optional) an array of additional params. It's not used for now.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseAuthor
+     */
+    /** 
+     * @ref bx_forum-browse_author "browse_author"
+     */
+	public function serviceBrowseAuthor ($iProfileId = 0, $aParams = array())
+    {
+        if(!$iProfileId)
+            $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
+
+        if(!$iProfileId)
+            return '';
+
+        $bEmptyMessage = true;
+        if(isset($aParams['empty_message'])) {
+            $bEmptyMessage = (bool)$aParams['empty_message'];
+            unset($aParams['empty_message']);
+        }
+
+        $bAjaxPaginate = true;
+        if(isset($aParams['ajax_paginate'])) {
+            $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
+            unset($aParams['ajax_paginate']);
+        }
+        
+        if(isset($aParams['unit_view']) && $aParams['unit_view'] != 'table')
+            return $this->_serviceBrowse('author', array_merge(array('author' => $iProfileId), $aParams), BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable(array(
+        	'type' => 'author', 
+        	'author' => $iProfileId, 
+        	'where' => array('fld' => 'author', 'val' => $iProfileId, 'opr' => '='), 
+        	'per_page' => (int)$this->_oDb->getParam('bx_forum_per_page_profile'),
+        	'empty_message' => $bEmptyMessage,
+        	'ajax_paginate' => $bAjaxPaginate
+        ), false);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_favorite browse_favorite
+     * 
+     * @code bx_srv('bx_forum', 'browse_favorite', [...]); @endcode
+     * 
+     * Get page block with a list of favorited items by some profile and represented as table.
+     * 
+     * @param $iProfileId (optional) integer value with profile ID. If empty value is provided, an attempt to get it from GET/POST arrays will be performed.
+     * @param $aParams (optional) an array of additional params. It's not used for now.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseFavorite
+     */
+    /** 
+     * @ref bx_forum-browse_favorite "browse_favorite"
+     */
+    public function serviceBrowseFavorite ($iProfileId = 0, $aParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $bEmptyMessage = false;
+        if(isset($aParams['empty_message'])) {
+            $bEmptyMessage = (bool)$aParams['empty_message'];
+            unset($aParams['empty_message']);
+        }
+
+        $bAjaxPaginate = true;
+        if(isset($aParams['ajax_paginate'])) {
+            $bAjaxPaginate = (bool)$aParams['ajax_paginate'];
+            unset($aParams['ajax_paginate']);
+        }
+
+        return $this->_serviceBrowseTable(array(
+            'type' => 'favorite', 
+            'per_page' => (int)$this->_oDb->getParam('bx_forum_per_page_profile'),
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), false);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_category browse_category
+     * 
+     * @code bx_srv('bx_forum', 'browse_category', [...]); @endcode
+     * 
+     * Get page block with a list of items filter by some category and represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseCategory
+     */
+    /** 
+     * @ref bx_forum-browse_category "browse_category"
+     */
+    public function serviceBrowseCategory($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true, $aParams = [])
+    {
+        if (bx_is_api()){
+            return parent::serviceBrowseCategory($sUnitView, $bEmptyMessage, $bAjaxPaginate, []);
+        }
+        
+        $sType = 'category';
+        $iCategory = bx_process_input(bx_get('category'), BX_DATA_INT);
+
+        $aCategory = $this->_oDb->getCategories(array('type' => 'by_category', 'category' => $iCategory));
+        if(!empty($aCategory['visible_for_levels']) && !BxDolAcl::getInstance()->isMemberLevelInSet($aCategory['visible_for_levels']))
+            return $bEmptyMessage ? MsgBox(_t('_sys_txt_access_denied')) : '';
+
+        if($sUnitView != 'table') {
+            $aParams = array_merge(['category' => $iCategory], $aParams);
+            if($sUnitView)
+                $aParams['unit_view'] = $sUnitView;
+
+            return $this->_serviceBrowse($sType, $aParams, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+        }
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType, 
+            'where' => array('fld' => 'cat', 'val' => $iCategory, 'opr' => '='),
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), !isset($aParams['show_header']) || (bool)$aParams['show_header'] === true);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_keyword browse_keyword
+     * 
+     * @code bx_srv('bx_forum', 'browse_keyword', [...]); @endcode
+     * 
+     * Get page block with a list of items filter by some keyword and represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseKeyword
+     */
+    /** 
+     * @ref bx_forum-browse_keyword "browse_keyword"
+     */
+    public function serviceBrowseKeyword($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true, $bShowHeader = false)
+    {
+        $sType = 'keyword';
+        $sKeyword = bx_process_input(bx_get('keyword'));
+
+        if($sUnitView != 'table')   
+            return $this->_serviceBrowse($sType, $sUnitView ? array('unit_view' => $sUnitView) : false, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType, 
+            'where' => $this->_getSearchKeywordDescriptor('#' . $sKeyword),
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), $bShowHeader);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-browse_search_results browse_search_results
+     * 
+     * @code bx_srv('bx_forum', 'browse_search_results', [...]); @endcode
+     * 
+     * Get page block with search results by Author(s), Category, Keyword represented as table.
+     * 
+     * @param $sUnitView (optional) string with unit view type.
+     * @param $bEmptyMessage (optional) boolean value determining whether an "Empty" message should be returned or not.
+     * @param $bAjaxPaginate (optional) boolean value determining whether an Ajax based pagination should be used or not.
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceBrowseSearchResults
+     */
+    /** 
+     * @ref bx_forum-browse_search_results "browse_search_results"
+     */
+    public function serviceBrowseSearchResults($sUnitView = false, $bEmptyMessage = true, $bAjaxPaginate = true)
+    {
+        $sType = 'search';
+
+        $aAuthors = bx_process_input(bx_get('author'));
+        $iCategory = bx_process_input(bx_get('category'), BX_DATA_INT);
+        $sKeyword = bx_process_input(bx_get('keyword'));
+
+        $aCategory = $this->_oDb->getCategories(array('type' => 'by_category', 'category' => $iCategory));
+        if(!empty($aCategory['visible_for_levels']) && !BxDolAcl::getInstance()->isMemberLevelInSet($aCategory['visible_for_levels']))
+            return $bEmptyMessage ? MsgBox(_t('_sys_txt_access_denied')) : '';
+
+        if($sUnitView != 'table')   
+            return $this->_serviceBrowse('', $sUnitView ? array('unit_view' => $sUnitView) : false, BX_DB_PADDING_DEF, $bEmptyMessage, $bAjaxPaginate);
+
+        $aWhereGroupAnd = array('grp' => true, 'opr' => 'AND', 'cnds' => array());
+        if(!empty($aAuthors) && is_array($aAuthors))
+            $aWhereGroupAnd['cnds'][] = $this->_getSearchAuthorDescriptor($aAuthors);
+
+        if(!empty($iCategory))
+            $aWhereGroupAnd['cnds'][] = array('fld' => 'cat', 'val' => $iCategory, 'opr' => '=');
+
+        if(!empty($sKeyword))
+            $aWhereGroupAnd['cnds'][] = $this->_getSearchKeywordDescriptor($sKeyword);
+
+        return $this->_serviceBrowseTable(array(
+            'type' => $sType, 
+            'where' => $aWhereGroupAnd,
+            'empty_message' => $bEmptyMessage,
+            'ajax_paginate' => $bAjaxPaginate
+        ), false);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-other Other
+     * @subsubsection bx_forum-get_discussions_num get_discussions_num
+     * 
+     * @code bx_srv('bx_forum', 'get_discussions_num', [...]); @endcode
+     * 
+     * Get number of discussions for some profile.
+     * 
+     * @param $iProfileId (optional) profile to get discussions for, if omitted then currently logged in profile is used.
+     * @return integer value with a number of discussions.
+     * 
+     * @see BxForumModule::serviceGetDiscussionsNum
+     */
+    /** 
+     * @ref bx_forum-get_discussions_num "get_discussions_num"
+     */
+    public function serviceGetDiscussionsNum ($iProfileId = 0)
+    {
+        if (!$iProfileId)
+            $iProfileId = bx_get_logged_profile_id();
+
+        return $this->_oDb->getEntriesNumByAuthor((int)$iProfileId);
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-other Other
+     * @subsubsection bx_forum-get_unreplied_discussions_num get_unreplied_discussions_num
+     * 
+     * @code bx_srv('bx_forum', 'get_unreplied_discussions_num', [...]); @endcode
+     * 
+     * Get number of unreplied discussions for some profile.
+     * 
+     * @param $iProfileId (optional) profile to get unreplied discussions for, if omitted then currently logged is profile is used.
+     * @return integer value with a number of discussions.
+     * 
+     * @see BxForumModule::serviceGetUnrepliedDiscussionsNum
+     */
+    /** 
+     * @ref bx_forum-get_unreplied_discussions_num "get_unreplied_discussions_num"
+     */
+    public function serviceGetUnrepliedDiscussionsNum ($iProfileId = 0)
+    {
+        if (!$iProfileId)
+            $iProfileId = bx_get_logged_profile_id();
+
+        return $this->_oDb->getUnrepliedDiscussionsNum((int)$iProfileId);
+    }
+
+    /**
+     * 
+     */
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-other Other
+     * @subsubsection bx_forum-trigger_comment_post trigger_comment_post
+     * 
+     * @code bx_srv('bx_forum', 'trigger_comment_post', [...]); @endcode
+     * 
+     * Update last comment time and author when new comment was added in the discussion.
+     * 
+     * @param $iContentId integer value with content ID.
+     * @param $iProfileId integer value with profile ID.
+     * @param $iCommentId integer value with comment ID.
+     * @param $iTimestamp (optional) date in UNIX Timestamp format, if omitted then current date is used.
+     * @param $sCommentText (optional) string value with text of comment.
+     * @return boolean value determining where the operation is successful or not.
+     * 
+     * @see BxForumModule::serviceTriggerCommentPost
+     */
+    /** 
+     * @ref bx_forum-trigger_comment_post "trigger_comment_post"
+     */
+    public function serviceTriggerCommentPost ($iContentId, $iProfileId, $iCommentId, $iTimestamp = 0, $sCommentText = '')
+    {
+    	$CNF = $this->_oConfig->CNF;
+
+    	$iContentId = (int)$iContentId;
+        if(!$iContentId)
+            return false;
+
+        $aContentInfo = $this->_oDb->getContentInfoById($iContentId);
+        if(!$aContentInfo)
+			return false;
+
+        $oCmts = BxDolCmts::getObjectInstance($CNF['OBJECT_COMMENTS'], $iContentId);
+        if(!$oCmts)
+            return false;
+
+        if(!$iTimestamp)
+			$iTimestamp = time();
+
+        if(!$this->_oDb->updateLastCommentTimeProfile((int)$iContentId, (int)$iProfileId, $iCommentId, $iTimestamp))
+			return false;
+
+        // send notification to author
+        if($iProfileId != $aContentInfo[$CNF['FIELD_AUTHOR']]) {
+	        $oProfile = BxDolProfile::getInstanceMagic($iProfileId);
+	        if($oProfile) 
+                sendMailTemplate('bx_forum_new_reply', 0, $aContentInfo[$CNF['FIELD_AUTHOR']], array(
+                    'SenderDisplayName' => $oProfile->getDisplayName(),
+                    'SenderUrl' => $oProfile->getUrl(),
+                    'PageUrl' => $oCmts->getItemUrl($iCommentId),
+                    'PageTitle' => $oCmts->getObjectTitle(),
+                    'Message' => $sCommentText,
+                ), BX_EMAIL_NOTIFY);
+        }
+
+        return true;
+    }
+    
+    
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-entity_author entity_author
+     * 
+     * @code bx_srv('bx_forum', 'entity_author', [...]); @endcode
+     * 
+     * Get page block with author.
+     * 
+     * @param $iContentId (optional) content 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 something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceEntityAuthor
+     */
+    /** 
+     * @ref bx_forum-entity_author "entity_author"
+     */
+    public function serviceEntityAuthor ($iContentId = 0)
+    {
+        if (bx_is_api())
+            return $this->_serviceTemplateFunc ('entryAuthor', $iContentId);
+        
+        return bx_srv('system', 'get_block_author', ['bx_forum', $iContentId], 'TemplServices');
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-entity_participants entity_participants
+     * 
+     * @code bx_srv('bx_forum', 'entity_participants', [...]); @endcode
+     * 
+     * Get page block with discussion's collaborators.
+     * 
+     * @param $iContentId (optional) content 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 something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceEntityParticipants
+     */
+    /** 
+     * @ref bx_forum-entity_participants "entity_participants"
+     */
+    public function serviceEntityParticipants ($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 $this->_oTemplate->entryParticipants ($aContentInfo, 5, 'right');
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_forum Discussions
+     * @subsection bx_forum-page_blocks Page Blocks
+     * @subsubsection bx_forum-search search
+     * 
+     * @code bx_srv('bx_forum', 'search', [...]); @endcode
+     * 
+     * Get page block with search form.
+     * 
+     * @return HTML string with block content to display on the site or false if something is wrong. All necessary CSS and JS files are automatically added to the HEAD section of the site HTML.
+     * 
+     * @see BxForumModule::serviceSearch
+     */
+    /** 
+     * @ref bx_forum-search "search"
+     */
+	public function serviceSearch()
+    {
+    	$CNF = $this->_oConfig->CNF;
+    	$oForm = BxDolForm::getObjectInstance($CNF['OBJECT_FORM_SEARCH'], $CNF['OBJECT_FORM_SEARCH_DISPLAY_FULL'], $this->_oTemplate);
+    	$oForm->initChecker();
+
+        return $oForm->getCode();
+    }
+
+    public function checkAllowedSubscribe(&$aDataEntry, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sMsg = $this->checkAllowedView($aDataEntry);
+        if($sMsg !== CHECK_ACTION_RESULT_ALLOWED)
+            return $sMsg;
+
+        return $this->_checkAllowedConnect ($aDataEntry, $isPerformAction, $CNF['OBJECT_CONNECTION_SUBSCRIBERS'], false, false);
+    }
+
+    public function checkAllowedUnsubscribe(&$aDataEntry, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        return $this->_checkAllowedConnect ($aDataEntry, $isPerformAction, $CNF['OBJECT_CONNECTION_SUBSCRIBERS'], false, true);
+    }
+
+    public function checkAllowedStickAnyEntry($aDataEntry, $isPerformAction = false)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	if((int)$aDataEntry[$CNF['FIELD_STICK']] != 0)
+    		return false;
+
+		return $this->_checkAllowedAction('stick any entry', $aDataEntry, $isPerformAction);
+    }
+
+    public function checkAllowedUnstickAnyEntry($aDataEntry, $isPerformAction = false)
+    {
+    	$CNF = $this->_oConfig->CNF;
+
+    	if((int)$aDataEntry[$CNF['FIELD_STICK']] != 1)
+			return false;
+
+    	return $this->_checkAllowedAction('stick any entry', $aDataEntry, $isPerformAction);
+    }
+    
+    public function checkAllowedResolveAnyEntry($aDataEntry, $isPerformAction = false)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	if((int)$aDataEntry[$CNF['FIELD_RESOLVABLE']] != 1 || (int)$aDataEntry[$CNF['FIELD_RESOLVE']] != 0)
+    		return false;
+
+        if($aDataEntry[$CNF['FIELD_AUTHOR']] == bx_get_logged_profile_id())
+            return CHECK_ACTION_RESULT_ALLOWED;
+        
+		return $this->_checkAllowedAction('resolve any entry', $aDataEntry, $isPerformAction);
+    }
+
+    public function checkAllowedUnresolveAnyEntry($aDataEntry, $isPerformAction = false)
+    {
+    	$CNF = $this->_oConfig->CNF;
+        
+    	if((int)$aDataEntry[$CNF['FIELD_RESOLVABLE']] != 1 || (int)$aDataEntry[$CNF['FIELD_RESOLVE']] != 1)
+			return false;
+        
+        if($aDataEntry[$CNF['FIELD_AUTHOR']] == bx_get_logged_profile_id())
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+    	return $this->_checkAllowedAction('resolve any entry', $aDataEntry, $isPerformAction);
+    }
+
+    public function checkAllowedLockAnyEntry($aDataEntry, $isPerformAction = false)
+    {
+    	$CNF = $this->_oConfig->CNF;
+
+    	if((int)$aDataEntry[$CNF['FIELD_LOCK']] != 0)
+    		return false;
+
+		return $this->_checkAllowedAction('lock any entry', $aDataEntry, $isPerformAction);
+    }
+
+	public function checkAllowedUnlockAnyEntry($aDataEntry, $isPerformAction = false)
+    {
+    	$CNF = $this->_oConfig->CNF;
+
+    	if((int)$aDataEntry[$CNF['FIELD_LOCK']] != 1)
+    		return false;
+
+		return $this->_checkAllowedAction('lock any entry', $aDataEntry, $isPerformAction);
+    }
+
+    public function checkAllowedHideAnyEntry($aDataEntry, $isPerformAction = false)
+    {
+    	$CNF = $this->_oConfig->CNF;
+
+    	if($aDataEntry[$CNF['FIELD_STATUS_ADMIN']] == 'hidden')
+    		return false;
+
+		return $this->_checkAllowedAction('hide any entry', $aDataEntry, $isPerformAction);
+    }
+
+	public function checkAllowedUnhideAnyEntry($aDataEntry, $isPerformAction = false)
+    {
+    	$CNF = &$this->_oConfig->CNF;
+
+    	if($aDataEntry[$CNF['FIELD_STATUS_ADMIN']] != 'hidden')
+    		return false;
+
+		return $this->_checkAllowedAction('hide any entry', $aDataEntry, $isPerformAction);
+    }
+
+    protected function _checkAllowedConnect (&$aDataEntry, $isPerformAction, $sObjConnection, $isMutual, $isInvertResult)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$this->_iProfileId)
+            return _t('_sys_txt_access_denied');
+
+        $isConnected = BxDolConnection::getObjectInstance($sObjConnection)->isConnected($this->_iProfileId, $aDataEntry[$CNF['FIELD_ID']], $isMutual);
+        if($isInvertResult)
+            $isConnected = !$isConnected;
+
+        return $isConnected ? _t('_sys_txt_access_denied') : CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+	/**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
+     */
+    protected function _checkAllowedAction($sAction, $aDataEntry, $isPerformAction = false)
+    {
+        if($this->_isModerator($isPerformAction))
+			return CHECK_ACTION_RESULT_ALLOWED;
+
+		$aCheck = checkActionModule($this->_iProfileId, $sAction, $this->getName(), $isPerformAction);
+    	if($aCheck[CHECK_ACTION_RESULT] === CHECK_ACTION_RESULT_ALLOWED)
+    		return CHECK_ACTION_RESULT_ALLOWED;
+
+        return _t('_sys_txt_access_denied');
+    }
+
+    protected function _serviceBrowseTable($aParams, $isDisplayHeader = true)
+    {
+        $sGrid = $this->_oConfig->CNF['OBJECT_GRID'];
+        if(!empty($aParams['grid'])) {
+            $sGrid = $aParams['grid'];
+            unset($aParams['grid']);
+        }
+
+        $oGrid = BxDolGrid::getObjectInstance($sGrid);
+        if(!$oGrid)
+            return false;
+        
+        $oGrid->setBrowseParams($aParams);
+        
+        $this->_oTemplate->addJsTranslation(array('_sys_grid_search'));
+        $this->_oTemplate->addCss(array('grid_tools.css'));
+       
+        return $oGrid->getCode($isDisplayHeader);
+    }
+
+    protected function _getSearchAuthorDescriptor($aAutor)
+    {
+        return array('grp' => true, 'opr' => 'OR', 'cnds' => array(
+            array('fld' => 'author', 'val' => $aAutor, 'opr' => 'IN'),
+            array('fld' => 'author_comment', 'val' => $aAutor, 'opr' => 'IN'),
+        ));
+    }
+
+    protected function _getSearchKeywordDescriptor($sKeyword)
+    {
+        return array('grp' => true, 'opr' => 'OR', 'cnds' => array(
+            array('fld' => 'title', 'val' => $sKeyword, 'opr' => 'LIKE'),
+            array('fld' => 'text', 'val' => $sKeyword, 'opr' => 'LIKE'),
+            array('fld' => 'text_comments', 'val' => $sKeyword, 'opr' => 'LIKE')
+        ));
+    }
+}
+
+/** @} */

+ 151 - 0
modules/boonex/forum/updates/13.0.12_13.0.13/source/install/config.php

@@ -0,0 +1,151 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Forum Forum
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_forum',
+    'title' => 'Discussions',
+    'note' => 'Discussions module.',
+    'version' => '13.0.13',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/forum/',
+    'home_uri' => 'forum',
+
+    'db_prefix' => 'bx_forum_',
+    'class_prefix' => 'BxForum',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Discussions',
+    
+    /**
+     * 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_forum_files'
+    ),
+
+    /**
+     * Transcoders.
+     */
+    'transcoders' => array(
+        'bx_forum_preview',
+        'bx_forum_miniature', 
+        'bx_forum_gallery',
+        'bx_forum_cover',
+
+        'bx_forum_preview_photos',
+        'bx_forum_miniature_photos', 
+        'bx_forum_gallery_photos',
+        'bx_forum_view_photos',
+
+        'bx_forum_videos_poster',
+        'bx_forum_videos_poster_preview',
+        'bx_forum_videos_mp4',
+        'bx_forum_videos_mp4_hd',
+
+        'bx_forum_preview_files',
+        'bx_forum_gallery_files',
+
+        'bx_forum_preview_cmts',
+    ),
+
+    /**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_forum',
+    	'bx_forum_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/market/updates/13.0.9_13.0.10/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' => '13.0.9',
+    'version_to' => '13.0.10',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * '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_13.0.9_13.0.10/',
+    'home_uri' => 'market_update_1309_13010',
+
+    'module_dir' => 'boonex/market/',
+    'module_uri' => 'market',
+
+    'db_prefix' => 'bx_market_',
+    'class_prefix' => 'BxMarket',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Market',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/market/updates/13.0.9_13.0.10/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
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 2 - 0
modules/boonex/market/updates/13.0.9_13.0.10/install/sql/install.sql

@@ -0,0 +1,2 @@
+-- TABLES
+ALTER TABLE `bx_market_products` MODIFY `cover_raw` longtext NOT NULL;

+ 1376 - 0
modules/boonex/market/updates/13.0.9_13.0.10/source/classes/BxMarketModule.php

@@ -0,0 +1,1376 @@
+<?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 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)
+    {
+    	$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-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));
+    }
+}
+
+/** @} */

+ 165 - 0
modules/boonex/market/updates/13.0.9_13.0.10/source/classes/BxMarketSearchResult.php

@@ -0,0 +1,165 @@
+<?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
+ *
+ * @{
+ */
+
+class BxMarketSearchResult extends BxBaseModTextSearchResult
+{
+    function __construct($sMode = '', $aParams = array())
+    {
+        parent::__construct($sMode, $aParams);
+
+        $this->aCurrent = array(
+            'name' => 'bx_market',
+            'module_name' => 'bx_market',
+            'object_metatags' => 'bx_market',
+            'title' => _t('_bx_market_page_title_browse'),
+            'table' => 'bx_market_products',
+            'ownFields' => array('id', 'title', 'text', 'price_single', 'price_recurring', 'duration_recurring', 'thumb', 'cover', 'cover_raw', 'author', 'added', 'cover_raw'),
+            'searchFields' => array(),
+            'restriction' => array(
+                'author' => array('value' => '', 'field' => 'author', 'operator' => '='),
+                'category' => array('value' => '', 'field' => 'cat', 'operator' => '='),
+                'featured' => array('value' => '', 'field' => 'featured', 'operator' => '<>'),
+                'except' => array('value' => '', 'field' => 'id', 'operator' => 'not in'),
+                'status' => array('value' => 'active', 'field' => 'status', 'operator' => '='),
+                'statusAdmin' => array('value' => 'active', 'field' => 'status_admin', 'operator' => '='),
+            ),
+            'paginate' => array('perPage' => getParam('bx_market_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' => 'text',
+            		'Image' => 'thumb'
+                ),
+            ),
+            'ident' => 'id',
+        );
+
+        $this->sFilterName = 'bx_market_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;
+                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_LATEST']);
+                $this->aCurrent['title'] = _t('_bx_market_page_title_browse_latest');
+                $this->aCurrent['rss']['link'] = 'modules/?r=market/rss/' . $sMode;
+                break;
+
+            case 'featured':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_FEATURED']);
+                $this->aCurrent['title'] = _t('_bx_market_page_title_browse_featured');
+                $this->aCurrent['restriction']['featured']['value'] = '0';
+                $this->aCurrent['rss']['link'] = 'modules/?r=market/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'featured';
+                break;
+
+            case 'popular':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_POPULAR']);
+                $this->aCurrent['title'] = _t('_bx_market_page_title_browse_popular');
+                $this->aCurrent['rss']['link'] = 'modules/?r=market/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'popular';
+                break;
+
+            case 'top':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_TOP']);
+                $this->aCurrent['title'] = _t('_bx_market_page_title_browse_top');
+                $this->aCurrent['rss']['link'] = 'modules/?r=market/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'top';
+                break;
+                
+             case 'category':
+                $iCategory = (int)$aParams['category'];
+                $this->addMarkers([
+                    'category_id' => $iCategory,
+                    'category_name' => BxDolCategory::getObjectInstance($CNF['OBJECT_CATEGORY'])->getCategoryTitle($iCategory),
+                ]);
+
+                $this->aCurrent['restriction']['category']['value'] = $iCategory;
+
+                $this->sBrowseUrl = $CNF['URL_CATEGORY'] . '&category={category_id}';
+                $this->aCurrent['title'] = _t('_bx_market_page_title_browse_by_category');
+                $this->aCurrent['rss']['link'] = 'modules/?r=market/rss/' . $sMode . '/' . $iCategory;
+                break;
+
+            case 'updated':
+                $this->sBrowseUrl = BxDolPermalinks::getInstance()->permalink($CNF['URL_UPDATED']);
+                $this->aCurrent['title'] = _t('_bx_market_page_title_browse_updated');
+                $this->aCurrent['rss']['link'] = 'modules/?r=market/rss/' . $sMode;
+                $this->aCurrent['sorting'] = 'updated';
+                break;
+
+            case '': // search results
+                $this->sBrowseUrl = BX_DOL_SEARCH_KEYWORD_PAGE;
+                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);
+    }
+
+    public function setMetaType($s)
+    {
+        $this->_sMetaType = $s;
+        if(!empty($this->_sMetaType) && $this->_sMetaType == 'keyword')
+            $this->aCurrent['title'] = _t('_bx_market_page_title_browse_keyword', bx_process_pass(bx_get('keyword')));
+    }
+
+    public function setCategoryObject($s)
+    {
+        $this->_sCategoryObject = $s;
+        if(!empty($this->_sCategoryObject) && $o = BxDolCategory::getObjectInstance($this->_sCategoryObject))
+            $this->aCurrent['title'] = _t('_bx_market_page_title_browse_category', $o->getCategoryTitle((int)bx_get('keyword')));
+    }
+
+    function displayResultBlock ()
+    {
+    	return BxDolPayments::getInstance()->getCartJs() . parent::displayResultBlock();
+    }
+}
+
+/** @} */

+ 140 - 0
modules/boonex/market/updates/13.0.9_13.0.10/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' => '13.0.10',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * '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'
+    ),
+
+);
+
+/** @} */

+ 633 - 0
modules/boonex/market/updates/13.0.9_13.0.10/source/install/sql/install.sql

@@ -0,0 +1,633 @@
+
+SET @sStorageEngine = (SELECT `value` FROM `sys_options` WHERE `name` = 'sys_storage_default');
+
+-- TABLE: entries
+CREATE TABLE IF NOT EXISTS `bx_market_products` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `author` int(11) unsigned NOT NULL default '0',
+  `added` int(11) NOT NULL default '0',
+  `changed` int(11) NOT NULL default '0',
+  `thumb` int(11) NOT NULL default '0',
+  `cover` int(11) NOT NULL default '0',
+  `cover_raw` longtext NOT NULL,
+  `package` int(11) NOT NULL default '0',
+  `name` varchar(255) NOT NULL,
+  `title` varchar(255) NOT NULL,
+  `text` text NOT NULL,
+  `notes` text NOT NULL,
+  `notes_purchased` text NOT NULL,
+  `cat` int(11) NOT NULL,
+  `price_single` float NOT NULL default '0',
+  `price_recurring` float NOT NULL default '0',
+  `duration_recurring` varchar(16) NOT NULL default 'month',
+  `trial_recurring` int(11) NOT NULL default '0',
+  `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',
+  `rrate` float NOT NULL default '0',
+  `rvotes` 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',
+  `allow_view_to` varchar(32) NOT NULL DEFAULT '3',
+  `allow_purchase_to` varchar(32) NOT NULL DEFAULT '3',
+  `allow_comment_to` varchar(32) NOT NULL DEFAULT 'c',
+  `allow_vote_to` varchar(32) NOT NULL DEFAULT 'c',
+  `status` enum('active','hidden') NOT NULL DEFAULT 'active',
+  `status_admin` enum('active','hidden','pending') NOT NULL DEFAULT 'active',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `name` (`name`(191)),
+  FULLTEXT KEY `title_text` (`title`,`text`)
+);
+
+-- TABLE: subentries
+CREATE TABLE IF NOT EXISTS `bx_market_subproducts` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `initiator` int(11) NOT NULL,
+  `content` int(11) NOT NULL,
+  `added` int(10) unsigned NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `initiator` (`initiator`,`content`),
+  KEY `content` (`content`)
+);
+
+-- TABLE: licenses
+CREATE TABLE IF NOT EXISTS `bx_market_licenses` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `product_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 '',
+  `domain` varchar(128) 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 `product_id` (`product_id`, `profile_id`),
+  KEY `license` (`license`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_market_licenses_deleted` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `profile_id` int(11) unsigned NOT NULL default '0',
+  `product_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 '',
+  `domain` varchar(128) 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 `product_id` (`product_id`,`profile_id`),
+  KEY `license` (`license`)
+);
+
+-- TABLE: storages & transcoders
+CREATE TABLE IF NOT EXISTS `bx_market_files` (
+  `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_market_files2products` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `content_id` int(11) unsigned NOT NULL,
+  `file_id` int(11) NOT NULL,
+  `type` enum('version','update') NOT NULL DEFAULT 'version',
+  `version` varchar(255) NOT NULL,
+  `version_to` varchar(255) NOT NULL,
+  `downloads` int(11) NOT NULL,
+  `order` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `file_content` (`file_id`,`content_id`),
+  KEY `content_id` (`content_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_market_downloads_track` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `file_id` int(11) NOT NULL default '0',
+  `profile_id` int(11) NOT NULL default '0',
+  `profile_nip` int(11) unsigned NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_market_photos` (
+  `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_market_photos2products` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `content_id` int(11) unsigned NOT NULL,
+  `file_id` int(11) NOT NULL,
+  `title` varchar(255) NOT NULL,
+  `order` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `file_content` (`file_id`,`content_id`),
+  KEY `content_id` (`content_id`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_market_photos_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_market_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_market_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: votes
+CREATE TABLE IF NOT EXISTS `bx_market_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_market_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`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_market_reactions` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `object_id` int(11) NOT NULL default '0',
+  `reaction` varchar(32) NOT NULL default '',
+  `count` int(11) NOT NULL default '0',
+  `sum` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `reaction` (`object_id`, `reaction`)
+);
+
+CREATE TABLE IF NOT EXISTS `bx_market_reactions_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',
+  `reaction` varchar(32) NOT NULL default '',
+  `value` tinyint(4) NOT NULL default '0',
+  `date` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  KEY `vote` (`object_id`, `author_nip`)
+);
+
+-- TABLE: views
+CREATE TABLE `bx_market_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: metas
+CREATE TABLE `bx_market_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 `bx_market_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 `bx_market_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: reports
+CREATE TABLE IF NOT EXISTS `bx_market_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_market_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: favorites
+CREATE TABLE `bx_market_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_market_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_market_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_market_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`)
+);
+
+
+-- 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_market_files', @sStorageEngine, '', 360, 2592000, 3, 'bx_market_files', 'allow-deny', 'zip', '', 0, 0, 0, 0, 0, 0),
+('bx_market_photos', @sStorageEngine, '', 360, 2592000, 3, 'bx_market_photos', 'allow-deny', '{image}', '', 0, 0, 0, 0, 0, 0),
+('bx_market_photos_resized', @sStorageEngine, '', 360, 2592000, 3, 'bx_market_photos_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_market_preview', 'bx_market_photos_resized', 'Storage', 'a:1:{s:6:"object";s:16:"bx_market_photos";}', 'no', '1', '2592000', '0'),
+('bx_market_icon', 'bx_market_photos_resized', 'Storage', 'a:1:{s:6:"object";s:16:"bx_market_photos";}', 'no', '1', '2592000', '0'),
+('bx_market_thumb', 'bx_market_photos_resized', 'Storage', 'a:1:{s:6:"object";s:16:"bx_market_photos";}', 'no', '1', '2592000', '0'),
+('bx_market_cover', 'bx_market_photos_resized', 'Storage', 'a:1:{s:6:"object";s:16:"bx_market_photos";}', 'no', '1', '2592000', '0'),
+('bx_market_screenshot', 'bx_market_photos_resized', 'Storage', 'a:1:{s:6:"object";s:16:"bx_market_photos";}', 'no', '1', '2592000', '0'),
+('bx_market_gallery', 'bx_market_photos_resized', 'Storage', 'a:1:{s:6:"object";s:16:"bx_market_photos";}', 'no', '1', '2592000', '0');
+
+INSERT INTO `sys_transcoder_filters` (`transcoder_object`, `filter`, `filter_params`, `order`) VALUES 
+('bx_market_preview', 'Resize', 'a:3:{s:1:"w";s:3:"300";s:1:"h";s:3:"200";s:11:"crop_resize";s:1:"1";}', '0'),
+('bx_market_gallery', 'Resize', 'a:1:{s:1:"w";s:3:"500";}', '0'),
+('bx_market_icon', 'Resize', 'a:3:{s:1:"w";s:2:"50";s:1:"h";s:2:"50";s:13:"square_resize";s:1:"1";}', '0'),
+('bx_market_thumb', 'Resize', 'a:3:{s:1:"w";s:3:"100";s:1:"h";s:3:"100";s:13:"square_resize";s:1:"1";}', '0'),
+('bx_market_cover', 'Resize', 'a:1:{s:1:"w";s:4:"1200";}', '0'),
+('bx_market_screenshot', 'Resize', 'a:3:{s:1:"w";s:3:"200";s:1:"h";s:3:"120";s:11:"crop_resize";s:1:"1";}', '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_market', 'bx_market', '_bx_market_form_entry', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'bx_market_products', 'id', '', '', 'a:2:{i:0;s:9:"do_submit";i:1;s:10:"do_publish";}', '', 0, 1, 'BxMarketFormEntry', 'modules/boonex/market/classes/BxMarketFormEntry.php');
+
+INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
+('bx_market', 'bx_market_entry_add', 'bx_market', 0, '_bx_market_form_entry_display_add'),
+('bx_market', 'bx_market_entry_delete', 'bx_market', 0, '_bx_market_form_entry_display_delete'),
+('bx_market', 'bx_market_entry_edit', 'bx_market', 0, '_bx_market_form_entry_display_edit'),
+('bx_market', 'bx_market_entry_view', 'bx_market', 1, '_bx_market_form_entry_display_view'),
+('bx_market', 'bx_market_entry_view_full', 'bx_market', 1, '_bx_market_form_entry_display_view_full');
+
+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_market', 'bx_market', '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_market', 'bx_market', 'allow_view_to', '', '', 0, 'custom', '_bx_market_form_entry_input_sys_allow_view_to', '_bx_market_form_entry_input_allow_view_to', '', 1, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'allow_purchase_to', '', '', 0, 'custom', '_bx_market_form_entry_input_sys_allow_purchase_to', '_bx_market_form_entry_input_allow_purchase_to', '', 1, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'allow_comment_to', '', '', 0, 'custom', '_bx_market_form_entry_input_sys_allow_comment_to', '_bx_market_form_entry_input_allow_comment_to', '', 1, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'allow_vote_to', '', '', 0, 'custom', '_bx_market_form_entry_input_sys_allow_vote_to', '_bx_market_form_entry_input_allow_vote_to', '', 1, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'delete_confirm', 1, '', 0, 'checkbox', '_bx_market_form_entry_input_sys_delete_confirm', '_bx_market_form_entry_input_delete_confirm', '_bx_market_form_entry_input_delete_confirm_info', 1, 0, 0, '', '', '', 'Avail', '', '_bx_market_form_entry_input_delete_confirm_error', '', '', 1, 0),
+('bx_market', 'bx_market', 'do_publish', '_bx_market_form_entry_input_do_publish', '', 0, 'submit', '_bx_market_form_entry_input_sys_do_publish', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'do_submit', '_bx_market_form_entry_input_do_submit', '', 0, 'submit', '_bx_market_form_entry_input_sys_do_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'location', '', '', 0, 'location', '_sys_form_input_sys_location', '_sys_form_input_location', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'cover_raw', '', '', 0, 'textarea', '_bx_market_form_entry_input_sys_cover_raw', '_bx_market_form_entry_input_cover_raw', '_bx_market_form_entry_input_cover_raw_inf', 0, 0, 0, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+('bx_market', 'bx_market', 'pictures', 'a:1:{i:0;s:15:"bx_market_html5";}', 'a:1:{s:15:"bx_market_html5";s:25:"_sys_uploader_html5_title";}', 0, 'files', '_bx_market_form_entry_input_sys_pictures', '_bx_market_form_entry_input_pictures', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'files', 'a:1:{i:0;s:15:"bx_market_html5";}', 'a:1:{s:15:"bx_market_html5";s:25:"_sys_uploader_html5_title";}', 0, 'files', '_bx_market_form_entry_input_sys_files', '_bx_market_form_entry_input_files', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'text', '', '', 0, 'textarea', '_bx_market_form_entry_input_sys_text', '_bx_market_form_entry_input_text', '', 1, 0, 2, '', '', '', 'Avail', '', '_bx_market_form_entry_input_text_err', 'XssHtml', '', 1, 0),
+('bx_market', 'bx_market', 'notes', '', '', 0, 'textarea', '_bx_market_form_entry_input_sys_notes', '_bx_market_form_entry_input_notes', '_bx_market_form_entry_input_notes_inf', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_market', 'bx_market', 'notes_purchased', '', '', 0, 'textarea', '_bx_market_form_entry_input_sys_notes_purchased', '_bx_market_form_entry_input_notes_purchased', '_bx_market_form_entry_input_notes_purchased_inf', 0, 0, 3, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+('bx_market', 'bx_market', 'title', '', '', 0, 'text', '_bx_market_form_entry_input_sys_title', '_bx_market_form_entry_input_title', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_market_form_entry_input_title_err', 'Xss', '', 1, 0),
+('bx_market', 'bx_market', 'name', '', '', 0, 'text', '_bx_market_form_entry_input_sys_name', '_bx_market_form_entry_input_name', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_market_form_entry_input_name_err', 'Xss', '', 1, 0),
+('bx_market', 'bx_market', 'cat', '', '#!bx_market_cats', 0, 'select', '_bx_market_form_entry_input_sys_cat', '_bx_market_form_entry_input_cat', '', 1, 0, 0, '', '', '', 'avail', '', '_bx_market_form_entry_input_cat_err', 'Xss', '', 1, 0),
+('bx_market', 'bx_market', 'price_single', '', '', 0, 'price', '_bx_market_form_entry_input_sys_price_single', '_bx_market_form_entry_input_price_single', '_bx_market_form_entry_input_price_single_inf', 0, 0, 0, '', '', '', '', '', '', 'Float', '', 1, 0),
+('bx_market', 'bx_market', 'price_recurring', '', '', 0, 'price', '_bx_market_form_entry_input_sys_price_recurring', '_bx_market_form_entry_input_price_recurring', '_bx_market_form_entry_input_price_recurring_inf', 0, 0, 0, '', '', '', '', '', '', 'Float', '', 1, 0),
+('bx_market', 'bx_market', 'duration_recurring', 'month', '#!bx_market_durations', 0, 'select', '_bx_market_form_entry_input_sys_duration_recurring', '_bx_market_form_entry_input_duration_recurring', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('bx_market', 'bx_market', 'trial_recurring', '', '', 0, 'text', '_bx_market_form_entry_input_sys_trial_recurring', '_bx_market_form_entry_input_trial_recurring', '_bx_market_form_entry_input_trial_recurring_inf', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+('bx_market', 'bx_market', 'added', '', '', 0, 'datetime', '_bx_market_form_entry_input_sys_added', '_bx_market_form_entry_input_added', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'changed', '', '', 0, 'datetime', '_bx_market_form_entry_input_sys_changed', '_bx_market_form_entry_input_changed', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'header_beg_single', '', '', 0, 'block_header', '_bx_market_form_entry_input_sys_header_beg_single', '_bx_market_form_entry_input_header_beg_single', '', 0, 1, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'header_beg_recurring', '', '', 0, 'block_header', '_bx_market_form_entry_input_sys_header_beg_recurring', '_bx_market_form_entry_input_header_beg_recurring', '', 0, 1, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'header_beg_privacy', '', '', 0, 'block_header', '_bx_market_form_entry_input_sys_header_beg_privacy', '_bx_market_form_entry_input_header_beg_privacy', '', 0, 1, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'header_beg_notes', '', '', 0, 'block_header', '_bx_market_form_entry_input_sys_header_beg_notes', '_bx_market_form_entry_input_header_beg_notes', '', 0, 1, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'header_beg_other', '', '', 0, 'block_header', '_bx_market_form_entry_input_sys_header_beg_other', '_bx_market_form_entry_input_header_beg_other', '', 0, 1, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'header_end_single', '', '', 0, 'block_end', '_bx_market_form_entry_input_sys_header_end_single', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_market', 'bx_market', 'header_end_recurring', '', '', 0, 'block_end', '_bx_market_form_entry_input_sys_header_end_recurring', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_market', 'bx_market', 'header_end_privacy', '', '', 0, 'block_end', '_bx_market_form_entry_input_sys_header_end_privacy', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_market', 'bx_market', 'header_end_notes', '', '', 0, 'block_end', '_bx_market_form_entry_input_sys_header_end_notes', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_market', 'bx_market', 'header_end_other', '', '', 0, 'block_end', '_bx_market_form_entry_input_sys_header_end_other', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
+('bx_market', 'bx_market', 'warning_single', '_bx_market_err_not_accept_payments_single', '', 0, 'value', '_bx_market_form_entry_input_sys_warning_single', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'warning_recurring', '_bx_market_err_not_accept_payments_recurring', '', 0, 'value', '_bx_market_form_entry_input_sys_warning_recurring', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', 'subentries', '', '', 0, 'custom', '_bx_market_form_entry_input_sys_subentries', '_bx_market_form_entry_input_subentries', '_bx_market_form_entry_input_subentries_inf', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+('bx_market', 'bx_market', '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_market_entry_add', 'title', 2147483647, 1, 1),
+('bx_market_entry_add', 'name', 2147483647, 1, 2),
+('bx_market_entry_add', 'cat', 2147483647, 1, 3),
+('bx_market_entry_add', 'text', 2147483647, 1, 4),
+('bx_market_entry_add', 'cover_raw', 2147483647, 0, 5),
+('bx_market_entry_add', 'pictures', 2147483647, 1, 6),
+('bx_market_entry_add', 'files', 2147483647, 1, 7),
+('bx_market_entry_add', 'header_beg_single', 2147483647, 1, 8),
+('bx_market_entry_add', 'warning_single', 2147483647, 1, 9),
+('bx_market_entry_add', 'price_single', 2147483647, 1, 10),
+('bx_market_entry_add', 'header_end_single', 2147483647, 1, 11),
+('bx_market_entry_add', 'header_beg_recurring', 2147483647, 1, 12),
+('bx_market_entry_add', 'warning_recurring', 2147483647, 1, 13),
+('bx_market_entry_add', 'duration_recurring', 2147483647, 1, 14),
+('bx_market_entry_add', 'price_recurring', 2147483647, 1, 15),
+('bx_market_entry_add', 'trial_recurring', 2147483647, 1, 16),
+('bx_market_entry_add', 'header_end_recurring', 2147483647, 1, 17),
+('bx_market_entry_add', 'header_beg_privacy', 2147483647, 1, 18),
+('bx_market_entry_add', 'allow_view_to', 2147483647, 1, 19),
+('bx_market_entry_add', 'allow_purchase_to', 2147483647, 1, 20),
+('bx_market_entry_add', 'allow_comment_to', 2147483647, 1, 21),
+('bx_market_entry_add', 'allow_vote_to', 2147483647, 1, 22),
+('bx_market_entry_add', 'cf', 2147483647, 1, 23),
+('bx_market_entry_add', 'header_end_privacy', 2147483647, 1, 24),
+('bx_market_entry_add', 'header_beg_notes', 2147483647, 1, 25),
+('bx_market_entry_add', 'notes', 2147483647, 1, 26),
+('bx_market_entry_add', 'notes_purchased', 2147483647, 1, 27),
+('bx_market_entry_add', 'header_end_notes', 2147483647, 1, 28),
+('bx_market_entry_add', 'header_beg_other', 2147483647, 1, 29),
+('bx_market_entry_add', 'location', 2147483647, 1, 30),
+('bx_market_entry_add', 'subentries', 2147483647, 1, 31),
+('bx_market_entry_add', 'header_end_other', 2147483647, 1, 32),
+('bx_market_entry_add', 'do_publish', 2147483647, 1, 33),
+
+('bx_market_entry_delete', 'delete_confirm', 2147483647, 1, 1),
+('bx_market_entry_delete', 'do_submit', 2147483647, 1, 2),
+
+('bx_market_entry_edit', 'title', 2147483647, 1, 1),
+('bx_market_entry_edit', 'name', 2147483647, 1, 2),
+('bx_market_entry_edit', 'cat', 2147483647, 1, 3),
+('bx_market_entry_edit', 'text', 2147483647, 1, 4),
+('bx_market_entry_edit', 'cover_raw', 2147483647, 0, 5),
+('bx_market_entry_edit', 'pictures', 2147483647, 1, 6),
+('bx_market_entry_edit', 'files', 2147483647, 1, 7),
+('bx_market_entry_edit', 'header_beg_single', 2147483647, 1, 8),
+('bx_market_entry_edit', 'warning_single', 2147483647, 1, 9),
+('bx_market_entry_edit', 'price_single', 2147483647, 1, 10),
+('bx_market_entry_edit', 'header_end_single', 2147483647, 1, 11),
+('bx_market_entry_edit', 'header_beg_recurring', 2147483647, 1, 12),
+('bx_market_entry_edit', 'warning_recurring', 2147483647, 1, 13),
+('bx_market_entry_edit', 'duration_recurring', 2147483647, 1, 14),
+('bx_market_entry_edit', 'price_recurring', 2147483647, 1, 15),
+('bx_market_entry_edit', 'trial_recurring', 2147483647, 1, 16),
+('bx_market_entry_edit', 'header_end_recurring', 2147483647, 1, 17),
+('bx_market_entry_edit', 'header_beg_privacy', 2147483647, 1, 18),
+('bx_market_entry_edit', 'allow_view_to', 2147483647, 1, 19),
+('bx_market_entry_edit', 'allow_purchase_to', 2147483647, 1, 20),
+('bx_market_entry_edit', 'allow_comment_to', 2147483647, 1, 21),
+('bx_market_entry_edit', 'allow_vote_to', 2147483647, 1, 22),
+('bx_market_entry_edit', 'cf', 2147483647, 1, 23),
+('bx_market_entry_edit', 'header_end_privacy', 2147483647, 1, 24),
+('bx_market_entry_edit', 'header_beg_notes', 2147483647, 1, 25),
+('bx_market_entry_edit', 'notes', 2147483647, 1, 26),
+('bx_market_entry_edit', 'notes_purchased', 2147483647, 1, 27),
+('bx_market_entry_edit', 'header_end_notes', 2147483647, 1, 28),
+('bx_market_entry_edit', 'header_beg_other', 2147483647, 1, 29),
+('bx_market_entry_edit', 'location', 2147483647, 1, 30),
+('bx_market_entry_edit', 'subentries', 2147483647, 1, 31),
+('bx_market_entry_edit', 'header_end_other', 2147483647, 1, 32),
+('bx_market_entry_edit', 'do_submit', 2147483647, 1, 33),
+
+('bx_market_entry_view', 'cat', 2147483647, 1, 1),
+('bx_market_entry_view', 'added', 2147483647, 1, 2),
+('bx_market_entry_view', 'changed', 2147483647, 1, 3),
+
+('bx_market_entry_view_full', 'cat', 2147483647, 1, 1),
+('bx_market_entry_view_full', 'added', 2147483647, 1, 2),
+('bx_market_entry_view_full', 'changed', 2147483647, 1, 3),
+('bx_market_entry_view_full', 'notes', 2147483647, 1, 4);
+
+-- PRE-VALUES
+INSERT INTO `sys_form_pre_lists`(`key`, `title`, `module`, `use_for_sets`) VALUES
+('bx_market_cats', '_bx_market_pre_lists_cats', 'bx_market', '0'),
+('bx_market_durations', '_bx_market_pre_lists_durations', 'bx_market', '0');
+
+INSERT INTO `sys_form_pre_values`(`Key`, `Value`, `Order`, `LKey`, `LKey2`) VALUES
+('bx_market_cats', '', 0, '_sys_please_select', ''),
+('bx_market_cats', '1', 1, '_bx_market_cat_administration', ''),
+('bx_market_cats', '2', 2, '_bx_market_cat_adult', ''),
+('bx_market_cats', '3', 3, '_bx_market_cat_advertisement', ''),
+('bx_market_cats', '4', 4, '_bx_market_cat_affiliates', ''),
+('bx_market_cats', '5', 5, '_bx_market_cat_authentication', ''),
+('bx_market_cats', '6', 6, '_bx_market_cat_calendars', ''),
+('bx_market_cats', '7', 7, '_bx_market_cat_communication', ''),
+('bx_market_cats', '8', 8, '_bx_market_cat_content', ''),
+('bx_market_cats', '9', 9, '_bx_market_cat_conversion', ''),
+('bx_market_cats', '10', 10, '_bx_market_cat_core_changes', ''),
+('bx_market_cats', '11', 11, '_bx_market_cat_dating', ''),
+('bx_market_cats', '12', 12, '_bx_market_cat_documentation', ''),
+('bx_market_cats', '13', 13, '_bx_market_cat_ecommerce', ''),
+('bx_market_cats', '14', 14, '_bx_market_cat_events', ''),
+('bx_market_cats', '15', 15, '_bx_market_cat_feedback', ''),
+('bx_market_cats', '16', 16, '_bx_market_cat_flash', ''),
+('bx_market_cats', '17', 17, '_bx_market_cat_games', ''),
+('bx_market_cats', '18', 18, '_bx_market_cat_graphics', ''),
+('bx_market_cats', '19', 19, '_bx_market_cat_hosting', ''),
+('bx_market_cats', '20', 20, '_bx_market_cat_integrations', ''),
+('bx_market_cats', '21', 21, '_bx_market_cat_location', ''),
+('bx_market_cats', '22', 22, '_bx_market_cat_management', ''),
+('bx_market_cats', '23', 23, '_bx_market_cat_mobile', ''),
+('bx_market_cats', '24', 24, '_bx_market_cat_multimedia', ''),
+('bx_market_cats', '25', 25, '_bx_market_cat_music', ''),
+('bx_market_cats', '26', 26, '_bx_market_cat_navigation', ''),
+('bx_market_cats', '27', 27, '_bx_market_cat_other', ''),
+('bx_market_cats', '28', 28, '_bx_market_cat_performance', ''),
+('bx_market_cats', '29', 29, '_bx_market_cat_photos', ''),
+('bx_market_cats', '30', 30, '_bx_market_cat_search', ''),
+('bx_market_cats', '31', 31, '_bx_market_cat_security', ''),
+('bx_market_cats', '32', 32, '_bx_market_cat_services', ''),
+('bx_market_cats', '33', 33, '_bx_market_cat_social', ''),
+('bx_market_cats', '34', 34, '_bx_market_cat_spam_prevention', ''),
+('bx_market_cats', '35', 35, '_bx_market_cat_statistics', ''),
+('bx_market_cats', '36', 36, '_bx_market_cat_templates', ''),
+('bx_market_cats', '37', 37, '_bx_market_cat_tools', ''),
+('bx_market_cats', '38', 38, '_bx_market_cat_translations', ''),
+('bx_market_cats', '39', 39, '_bx_market_cat_video', ''),
+
+('bx_market_durations', 'day', 1, '_bx_market_cat_duration_day', ''),
+('bx_market_durations', 'week', 2, '_bx_market_cat_duration_week', ''),
+('bx_market_durations', 'month', 3, '_bx_market_cat_duration_month', ''),
+('bx_market_durations', 'year', 4, '_bx_market_cat_duration_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_market', 'bx_market', 'bx_market_cmts', 1, 5000, 1000, 3, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-product&id={object_id}', '', 'bx_market_products', 'id', 'author', 'title', 'comments', 'BxMarketCmts', 'modules/boonex/market/classes/BxMarketCmts.php'),
+('bx_market_notes', 'bx_market', 'bx_market_cmts_notes', 1, 5000, 1000, 0, 5, 3, 'tail', 1, 'bottom', 1, 1, 1, -3, 1, 'cmt', 'page.php?i=view-post&id={object_id}', '', 'bx_market_products', 'id', 'author', 'title', '', 'BxTemplCmtsNotes', '');
+
+-- VOTES
+INSERT INTO `sys_objects_vote` (`Name`, `Module`, `TableMain`, `TableTrack`, `PostTimeout`, `MinValue`, `MaxValue`, `IsUndo`, `IsOn`, `TriggerTable`, `TriggerFieldId`, `TriggerFieldAuthor`, `TriggerFieldRate`, `TriggerFieldRateCount`, `ClassName`, `ClassFile`) VALUES 
+('bx_market', 'bx_market', 'bx_market_votes', 'bx_market_votes_track', '604800', '1', '5', '0', '1', 'bx_market_products', 'id', 'author', 'rate', 'votes', 'BxMarketVoteStars', 'modules/boonex/market/classes/BxMarketVoteStars.php'),
+('bx_market_reactions', 'bx_market', 'bx_market_reactions', 'bx_market_reactions_track', '604800', '1', '1', '1', '1', 'bx_market_products', 'id', 'author', 'rrate', 'rvotes', 'BxMarketVoteReactions', 'modules/boonex/market/classes/BxMarketVoteReactions.php');
+
+-- 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_market', 'bx_market', 'bx_market_scores', 'bx_market_scores_track', '604800', '0', 'bx_market_products', 'id', 'author', 'score', 'sc_up', 'sc_down', 'BxMarketScore', 'modules/boonex/market/classes/BxMarketScore.php');
+
+-- 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_market', 'bx_market', 'bx_market_reports', 'bx_market_reports_track', '1', 'page.php?i=view-product&id={object_id}', 'bx_market_notes', 'bx_market_products', 'id', 'author', 'reports', '', '');
+
+-- 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_market', 'bx_market', 'bx_market_views_track', '86400', '1', 'bx_market_products', 'id', 'author', 'views', '', '');
+
+-- 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_market', 'bx_market_favorites_track', 'bx_market_favorites_lists', '1', '1', '1', 'page.php?i=view-product&id={object_id}', 'bx_market_products', '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_market', 'bx_market', '1', '1', 'page.php?i=view-product&id={object_id}', 'bx_market_products', '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_market', '_bx_market', 'bx_market', 'added', 'edited', 'deleted', '', ''),
+('bx_market_cmts', '_bx_market_cmts', 'bx_market', 'commentPost', 'commentUpdated', 'commentRemoved', 'BxDolContentInfoCmts', '');
+
+INSERT INTO `sys_content_info_grids` (`object`, `grid_object`, `grid_field_id`, `condition`, `selection`) VALUES
+('bx_market', 'bx_market_administration', 'id', '', ''),
+('bx_market', 'bx_market_common', 'id', '', '');
+
+-- SEARCH EXTENDED
+INSERT INTO `sys_objects_search_extended` (`object`, `object_content_info`, `module`, `title`, `active`, `class_name`, `class_file`) VALUES
+('bx_market', 'bx_market', 'bx_market', '_bx_market_search_extended', 1, '', ''),
+('bx_market_cmts', 'bx_market_cmts', 'bx_market', '_bx_market_search_extended_cmts', 1, 'BxTemplSearchExtendedCmts', '');
+
+-- STUDIO: page & widget
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, 'bx_market', '_bx_market', '_bx_market', 'bx_market@modules/boonex/market/|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_market', 'content', '{url_studio}module.php?name=bx_market', '', 'bx_market@modules/boonex/market/|std-icon.svg', '_bx_market', '', '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));

+ 51 - 0
modules/boonex/massmailer/updates/13.0.5_13.0.6/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' => 'Mass mailer',
+    'version_from' => '13.0.5',
+    'version_to' => '13.0.6',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/massmailer/updates/update_13.0.5_13.0.6/',
+    'home_uri' => 'massmailer_update_1305_1306',
+
+    'module_dir' => 'boonex/massmailer/',
+    'module_uri' => 'massmailer',
+
+    'db_prefix' => 'bx_massmailer_',
+    'class_prefix' => 'BxMassMailer',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'MassMailer',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/massmailer/updates/13.0.5_13.0.6/install/installer.php

@@ -0,0 +1,13 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ */
+
+class BxMassMailerUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 9 - 0
modules/boonex/massmailer/updates/13.0.5_13.0.6/install/sql/install.sql

@@ -0,0 +1,9 @@
+SET @sName = 'bx_massmailer';
+
+
+-- FORMS
+DELETE FROM `sys_form_inputs` WHERE `object`=@sName AND `name`='controls';
+UPDATE `sys_form_inputs` SET `name`='controls' WHERE `object`=@sName AND `name`='controls2';
+
+DELETE FROM `sys_form_display_inputs` WHERE `display_name` IN ('bx_massmailer_campaign_add', 'bx_massmailer_campaign_edit') AND `input_name` IN ('controls', 'cancel');
+UPDATE `sys_form_display_inputs` SET `input_name`='controls' WHERE `display_name` IN ('bx_massmailer_campaign_send_test', 'bx_massmailer_campaign_send_all') AND `input_name`='controls2';

+ 219 - 0
modules/boonex/massmailer/updates/13.0.5_13.0.6/source/classes/BxMassMailerGridCampaigns.php

@@ -0,0 +1,219 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+* Copyright (c) UNA, Inc - https://una.io
+* MIT License - https://opensource.org/licenses/MIT
+*
+* @defgroup    MassMailer Mass Mailer
+* @ingroup     UnaModules
+* 
+* @{
+*/
+
+class BxMassMailerGridCampaigns extends BxTemplGrid
+{
+    protected $MODULE;
+    protected $_oModule;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        $this->MODULE = 'bx_massmailer';
+        $this->_oModule = BxDolModule::getInstance($this->MODULE);
+        parent::__construct ($aOptions, $oTemplate);
+        
+        $this->_sDefaultSortingOrder = 'DESC';
+    }
+    
+    public function performActionDelete()
+    {
+        $aIds = bx_get('ids');
+        foreach ($aIds as $iId){
+            $this->_oModule->_oDb->deleteCampaignData($iId);
+        }
+        parent::performActionDelete();
+    }
+    
+    public function performActionSendTest()
+    {
+        $sAction = 'send_test';
+        $aIds = bx_get('ids');
+        $iId = $aIds[0];
+        $oForm = BxDolForm::getObjectInstance('bx_massmailer', 'bx_massmailer_campaign_send_test');
+        if (!$oForm)
+            return '';
+        $oForm->setId('bx_massmailer_campaign_send_test');
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . 'grid.php?' . bx_encode_url_params($_GET, array('_r'));
+        $aContentInfo = array('email' => BxDolProfile::getInstance()->getAccountObject()->getEmail());
+        $oForm->initChecker($aContentInfo, array());
+        if($oForm->isSubmittedAndValid()) {
+            $mixedResult = $this->_oModule->sendTest($oForm->aInputs['email']['value'], $iId);
+            if($mixedResult !== false)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $mixedResult);
+            else
+                $aRes = array('msg' => _t('_bx_massmailer_txt_send_test_error'));
+            echoJson($aRes);
+        }
+        else {
+            
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('bx_massmailer_campaign_send_test', _t('_bx_massmailer_campaign_form_send_test_title'), $this->_oModule->_oTemplate->parseHtmlByName('manage_item.html', array(
+                'form_id' => $oForm->id,
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            )));
+            echoJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))));
+        }
+    }
+    
+    public function performActionSendAll()
+    {
+        $sAction = 'send_all';
+        $aIds = bx_get('ids');
+        $iId = $aIds[0];
+        $oForm = BxDolForm::getObjectInstance('bx_massmailer', 'bx_massmailer_campaign_send_all');
+        if (!$oForm)
+            return '';
+        $oForm->setId('bx_massmailer_campaign_send_all');
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . 'grid.php?' . bx_encode_url_params($_GET, array('_r'));
+        $oForm->initChecker(array(), array());
+        $oForm->aInputs['campaign_info']['value'] = _t($oForm->aInputs['campaign_info']['value'], $this->_oModule->getEmailCountInSegment($iId));
+        if($oForm->isSubmittedAndValid()) {
+            $mixedResult = $this->_oModule->sendAll($iId);
+            if($mixedResult !== false)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $mixedResult);
+            else
+                $aRes = array('msg' => $mixedResult);
+            echoJson($aRes);
+        }
+        else {
+            $sContent = BxTemplFunctions::getInstance()->popupBox('bx_massmailer_campaign_send_all', _t('_bx_massmailer_campaign_form_send_all_title'), $this->_oModule->_oTemplate->parseHtmlByName('manage_item.html', array(
+                'form_id' => $oForm->id,
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            )));
+            echoJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))));
+        }
+    }
+    
+    public function performActionCopy()
+    {
+        $aIds = bx_get('ids');
+        $iId = $aIds[0];
+        $mixedResult = $this->_oModule->_oDb->copyCampaign($iId);
+        if($mixedResult !== false)
+            $aRes = array('grid' => $this->getCode(false), 'blink' => $mixedResult);
+        else
+            $aRes = array('msg' => $mixedResult);
+        echoJson($aRes);
+    }
+    
+    protected function _getCellAdded($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(bx_time_js($mixedValue), $sKey, $aField, $aRow);
+    }
+    
+    protected function _getCellSegments($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($this->_oModule->getSegments($mixedValue), $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellDateSent($mixedValue, $sKey, $aField, $aRow)
+    {
+        $sValue = bx_time_js($mixedValue);
+        if ($mixedValue == '0')
+            $sValue = _t('_bx_massmailer_txt_never_sent');
+        return parent::_getCellDefault($sValue, $sKey, $aField, $aRow);
+    }
+    
+    protected function _getCellIsOnePerAccount($mixedValue, $sKey, $aField, $aRow)
+    {
+        $sValue = _t('_bx_massmailer_grid_column_title_adm_is_one_per_account_no');
+        if ($mixedValue == '1')
+            $sValue = _t('_bx_massmailer_grid_column_title_adm_is_one_per_account_yes');
+        return parent::_getCellDefault($sValue, $sKey, $aField, $aRow);
+    }
+    
+    protected function _getActionCopy($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(isset($aRow["date_sent"]) && $aRow["date_sent"] == '0')
+            return '';
+        return parent::_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+    
+    protected function _getActionSendTest($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(isset($aRow["date_sent"]) && $aRow["date_sent"] != '0')
+            return '';
+        return parent::_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+    
+    protected function _getActionSendAll($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(isset($aRow["date_sent"]) && $aRow["date_sent"] != '0')
+            return '';
+        return parent::_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+    
+    protected function _getActionAdd ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        $sUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_ADD_CAMPAIGN']));
+
+        unset($a['attr']['bx_grid_action_independent']);
+        $a['attr'] = array_merge($a['attr'], array(
+            "onclick" => "window.open('" . $sUrl . "','_self');"
+        ));
+
+        return parent::_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+    
+    protected function _getActionEdit ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(isset($aRow["date_sent"]) && $aRow["date_sent"] != '0')
+            return '';
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_EDIT_CAMPAIGN'] . '&id=' . $aRow[$CNF['FIELD_ID']]));
+
+        $a['attr'] = array_merge($a['attr'], array(
+            "onclick" => "window.open('" . $sUrl . "','_self');"
+        ));
+
+        return $this->_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+    
+    protected function _getActionView ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(isset($aRow["date_sent"]) && $aRow["date_sent"] == '0')
+            return '';
+        
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        
+        $sUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink('page.php?i=' . $CNF['URI_VIEW_CAMPAIGN'] . '&id=' . $aRow[$CNF['FIELD_ID']]));
+
+        unset($a['attr']['bx_grid_action_independent']);
+        $a['attr'] = array_merge($a['attr'], array(
+            "onclick" => "window.open('" . $sUrl . "','_self');"
+        ));
+
+        return parent::_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+    
+    protected function _getCellAuthor($mixedValue, $sKey, $aField, $aRow)
+    {
+        $oProfile = BxDolProfile::getInstance($aRow['author']);
+        if (!$oProfile)
+            $oProfile = BxDolProfileUndefined::getInstance();
+        return parent::_getCellDefault($oProfile->getDisplayName(), $sKey, $aField, $aRow);
+    }
+    
+    protected function _isVisibleGrid ($a)
+    {
+        if (isAdmin() || !isset($a['visible_for_levels']))
+            return true;
+        return $this->_oModule->checkAllowed() === CHECK_ACTION_RESULT_ALLOWED ? true : false;
+    }
+}
+
+/** @} */

+ 23 - 0
modules/boonex/massmailer/updates/13.0.5_13.0.6/source/classes/BxMassMailerPageEntry.php

@@ -0,0 +1,23 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    MassMailer Mass Mailer
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Entry create/edit pages
+ */
+class BxMassMailerPageEntry extends BxTemplPage
+{
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+    }
+}
+
+/** @} */

+ 77 - 0
modules/boonex/massmailer/updates/13.0.5_13.0.6/source/install/config.php

@@ -0,0 +1,77 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    MassMailer Mass Mailer
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_massmailer',
+    'title' => 'Mass mailer',
+    'note' => 'Mass mailer module.',
+    'version' => '13.0.6',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/massmailer/',
+    'home_uri' => 'massmailer',
+
+    'db_prefix' => 'bx_massmailer_',
+    'class_prefix' => 'BxMassMailer',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'MassMailer',
+
+    /**
+     * 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,
+    ),
+    'enable_success' => array(
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable_failed' => array (
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 137 - 0
modules/boonex/massmailer/updates/13.0.5_13.0.6/source/install/sql/install.sql

@@ -0,0 +1,137 @@
+SET @sName = 'bx_massmailer';
+
+-- TABLES
+CREATE TABLE `bx_massmailer_campaigns` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `title` varchar(255) DEFAULT NULL,
+  `subject` varchar(255) DEFAULT NULL,
+  `from_name` varchar(255) DEFAULT NULL,
+  `reply_to` varchar(255) DEFAULT NULL,
+  `body` text DEFAULT NULL,
+  `segments` varchar(255) DEFAULT NULL,
+  `author` int(11) NOT NULL,
+  `added` int(11) NOT NULL default '0',
+  `changed` int(11) NOT NULL default '0',
+  `date_sent` int(11) NOT NULL default '0',
+  `email_list` text DEFAULT NULL,
+  `is_one_per_account` smallint(1) NOT NULL,
+  `is_track_links` smallint(1) NOT NULL,  
+  PRIMARY KEY (`id`),
+  FULLTEXT KEY `title_text` (`title`, `subject`)
+);
+
+CREATE TABLE `bx_massmailer_segments` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `title` varchar(255) DEFAULT NULL,
+  `info` text DEFAULT NULL,
+  `email_list` text DEFAULT NULL,
+  PRIMARY KEY (`id`)
+);
+
+CREATE TABLE `bx_massmailer_letters` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `campaign_id` int(11) NOT NULL,
+  `email` varchar(255) NOT NULL,
+  `date_sent` int(11) NOT NULL default '0',
+  `date_seen` int(11) NOT NULL default '0',
+  `date_click` int(11) NOT NULL default '0',
+  `hash` varchar(35) NOT NULL,
+  PRIMARY KEY (`id`),
+  INDEX `campaign_id` (`campaign_id`),
+  INDEX `hash` (`hash`)
+);
+
+CREATE TABLE `bx_massmailer_links` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `letter_hash` varchar(35) DEFAULT NULL,
+  `hash` varchar(35) DEFAULT NULL,
+  `link` varchar(255) DEFAULT NULL,
+  `title` varchar(255) DEFAULT NULL,
+  `campaign_id` int(11) DEFAULT NULL,
+  `date_click` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  INDEX `campaign_id` (`campaign_id`),
+  INDEX `hash` (`hash`),
+  INDEX `letter_hash` (`letter_hash`)
+);
+
+CREATE TABLE `bx_massmailer_unsubscribe` (
+  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
+  `account_id` int(11) DEFAULT NULL,
+  `campaign_id` int(11) DEFAULT NULL,
+  `unsubscribed` int(11) NOT NULL default '0',
+  PRIMARY KEY (`id`),
+  INDEX `campaign_id` (`campaign_id`)
+);
+
+-- STUDIO: page & widget
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, @sName, '_bx_massmailer', '_bx_massmailer', 'bx_massmailer@modules/boonex/massmailer/|std-icon.svg');
+
+SET @iPageId = LAST_INSERT_ID();
+
+INSERT INTO `sys_std_widgets` (`page_id`, `module`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`, `featured`) 
+VALUES(@iPageId, @sName, 'extensions', '{url_studio}module.php?name=bx_massmailer', '', 'bx_massmailer@modules/boonex/massmailer/|std-icon.svg', '_bx_massmailer', '', '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";}', 0);
+
+SET @iParentPageId = (SELECT `id` FROM `sys_std_pages` WHERE `name` = 'home');
+SET @iParentPageOrder = (SELECT IFNULL(MAX(`order`), 0) + 1 FROM `sys_std_pages_widgets` WHERE `page_id` = @iParentPageId);
+
+INSERT INTO `sys_std_pages_widgets` (`page_id`, `widget_id`, `order`) VALUES
+(@iParentPageId, LAST_INSERT_ID(), @iParentPageOrder);
+
+-- FORMS
+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(@sName, @sName, '_bx_massmailer_form_campaign', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'a:2:{i:0;s:9:"do_submit";i:1;s:7:"do_send";}', 'bx_massmailer_campaigns', 'id', '', '', 'a:1:{s:14:"checker_helper";s:29:"BxMassMailerFormCheckerHelper";}', 0, 1, 'BxMassMailerFormEntry', 'modules/boonex/massmailer/classes/BxMassMailerFormEntry.php');
+
+INSERT INTO `sys_form_displays` (`display_name`, `module`, `object`, `title`, `view_mode`) VALUES
+('bx_massmailer_campaign_add', @sName, @sName, '_bx_massmailer_form_campaign_add_display', 0),
+('bx_massmailer_campaign_edit', @sName, @sName, '_bx_massmailer_form_campaign_edit_display', 0),
+('bx_massmailer_campaign_send_test', @sName, @sName, '_bx_massmailer_form_campaign_send_test_display', 0),
+('bx_massmailer_campaign_send_all', @sName, @sName, '_bx_massmailer_form_campaign_send_all_display', 0);
+
+INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `unique`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES
+(@sName, @sName, 'do_submit', '_bx_massmailer_form_entry_input_do_submit', '', 0, 'submit', '_bx_massmailer_form_entry_input_sys_do_submit', '', '', 0, 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+(@sName, @sName, 'do_send', '_bx_massmailer_form_entry_input_do_send', '', 0, 'submit', '_bx_massmailer_form_entry_input_sys_do_send', '', '', 0, 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
+(@sName, @sName, 'title', '', '', 0, 'text', '_bx_massmailer_form_campaign_input_sys_title', '_bx_massmailer_form_campaign_input_title', '', 1, 0, 0, 2, '', '', '', 'Avail', '', '_bx_massmailer_form_campaign_input_title_err', 'XssHtml', '', 1, 0),
+(@sName, @sName, 'segments', '', '', 0, 'select', '_bx_massmailer_form_campaign_input_sys_segments', '_bx_massmailer_form_campaign_input_segments', '', 1, 0, 0, 2, '', '', '', 'Avail', '', '_bx_massmailer_form_campaign_input_segments_err', 'XssHtml', '', 1, 0),
+(@sName, @sName, 'subject', '', '', 0, 'text', '_bx_massmailer_form_campaign_input_sys_subject', '_bx_massmailer_form_campaign_input_subject', '', 1, 0, 0, 2, '', '', '', 'Avail', '', '_bx_massmailer_form_campaign_input_subject_err', 'XssHtml', '', 1, 0),
+(@sName, @sName, 'from_name', '', '', 0, 'text', '_bx_massmailer_form_campaign_input_sys_from_name', '_bx_massmailer_form_campaign_input_from_name', '_bx_massmailer_form_campaign_input_from_name_info', 0, 0, 0, 2, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+(@sName, @sName, 'reply_to', '', '', 0, 'text', '_bx_massmailer_form_campaign_input_sys_reply_to', '_bx_massmailer_form_campaign_input_reply_to', '_bx_massmailer_form_campaign_input_reply_to_info', 0, 0, 0, 2, '', '', '', 'EmailOrEmpty', '', '_bx_massmailer_form_campaign_input_reply_to_err', 'XssHtml', '', 1, 0),
+(@sName, @sName, 'body', '', '', 0, 'textarea', '_bx_massmailer_form_campaign_input_sys_body', '_bx_massmailer_form_campaign_input_body', '', 1, 0, 0, 2, '', '', '', 'UnsubscribeUrl', '', '_bx_massmailer_form_campaign_input_body_err', 'XssHtml', '', 1, 0),
+(@sName, @sName, 'is_one_per_account', '1', '', 1, 'switcher', '_bx_massmailer_form_campaign_input_sys_is_one_per_account', '_bx_massmailer_form_campaign_input_is_one_per_account', '', 0, 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+(@sName, @sName, 'is_track_links', '1', '', 1, 'switcher', '_bx_massmailer_form_campaign_input_sys_is_track_links', '_bx_massmailer_form_campaign_input_is_track_links', '', 0, 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
+(@sName, @sName, 'email', '', '', 0, 'text', '_bx_massmailer_form_campaign_input_sys_test_email', '_bx_massmailer_form_campaign_input_email', '', 1, 0, 0, 2, '', '', '', 'Email', '', '_sys_form_account_input_email_error', 'XssHtml', '', 1, 0),
+(@sName, @sName, 'campaign_info', '_bx_massmailer_form_campaign_input_from_name_info_value', '', 0, 'value', '_bx_massmailer_form_campaign_input_sys_campaign_info', '', '', 0, 0, 0, 2, '', '', '', '', '', '', 'XssHtml', '', 1, 0),
+(@sName, @sName, 'cancel', '_bx_massmailer_form_entry_input_cancel', '', 0, 'button', '_bx_dev_bp_btn_sys_block_cancel', '', '', 0, 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";}', '', '', 'Avail', '', '', '', '', 0, 0),
+(@sName, @sName, 'controls', '', 'do_send,cancel', 0, 'input_set', '', '', '', 0, 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0);
+
+INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_for_levels`, `active`, `order`) VALUES
+('bx_massmailer_campaign_add', 'title', 2147483647, 1, 1),
+('bx_massmailer_campaign_add', 'segments', 2147483647, 1, 2),
+('bx_massmailer_campaign_add', 'subject', 2147483647, 1, 3),
+('bx_massmailer_campaign_add', 'from_name', 2147483647, 1, 4),
+('bx_massmailer_campaign_add', 'reply_to', 2147483647, 1, 5),
+('bx_massmailer_campaign_add', 'body', 2147483647, 1, 6),
+('bx_massmailer_campaign_add', 'is_one_per_account', 2147483647, 1, 7),
+('bx_massmailer_campaign_add', 'is_track_links', 2147483647, 1, 8),
+('bx_massmailer_campaign_add', 'do_submit', 2147483647, 1, 9),
+
+('bx_massmailer_campaign_edit', 'title', 2147483647, 1, 1),
+('bx_massmailer_campaign_edit', 'segments', 2147483647, 1, 2),
+('bx_massmailer_campaign_edit', 'subject', 2147483647, 1, 3),
+('bx_massmailer_campaign_edit', 'from_name', 2147483647, 1, 4),
+('bx_massmailer_campaign_edit', 'reply_to', 2147483647, 1, 5),
+('bx_massmailer_campaign_edit', 'body', 2147483647, 1, 6),
+('bx_massmailer_campaign_edit', 'is_one_per_account', 2147483647, 1, 7),
+('bx_massmailer_campaign_edit', 'is_track_links', 2147483647, 1, 8),
+('bx_massmailer_campaign_edit', 'do_submit', 2147483647, 1, 9),
+
+('bx_massmailer_campaign_send_test', 'email', 2147483647, 1, 1),
+('bx_massmailer_campaign_send_test', 'controls', 2147483647, 1, 2),
+('bx_massmailer_campaign_send_test', 'do_send', 2147483647, 1, 3),
+('bx_massmailer_campaign_send_test', 'cancel', 2147483647, 1, 4),
+
+('bx_massmailer_campaign_send_all', 'campaign_info', 2147483647, 1, 1),
+('bx_massmailer_campaign_send_all', 'controls', 2147483647, 1, 2),
+('bx_massmailer_campaign_send_all', 'do_send', 2147483647, 1, 3),
+('bx_massmailer_campaign_send_all', 'cancel', 2147483647, 1, 4);

+ 51 - 0
modules/boonex/notifications/updates/13.0.11_13.0.12/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' => '13.0.11',
+    'version_to' => '13.0.12',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * '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_13.0.11_13.0.12/',
+    'home_uri' => 'notifications_update_13011_13012',
+
+    '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/13.0.11_13.0.12/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);
+    }
+}

+ 440 - 0
modules/boonex/notifications/updates/13.0.11_13.0.12/source/classes/BxNtfsDb.php

@@ -0,0 +1,440 @@
+<?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
+ *
+ * @{
+ */
+
+class BxNtfsDb extends BxBaseModNotificationsDb
+{
+    protected $_sTableEvt2Usr;
+    protected $_sTableQueue;
+    protected $_sTableRead;
+
+    /*
+     * Constructor.
+     */
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+
+        $this->_aDeliveryTypes = array(
+            BX_BASE_MOD_NTFS_DTYPE_SITE, 
+            BX_BASE_MOD_NTFS_DTYPE_EMAIL, 
+            BX_BASE_MOD_NTFS_DTYPE_PUSH
+        );
+
+        $this->_sTableEvt2Usr = $this->_sPrefix . 'events2users';
+        $this->_sTableQueue = $this->_sPrefix . 'queue';
+        $this->_sTableRead = $this->_sPrefix . 'read';
+    }
+
+    public function markAsClicked($iUserId, $iEventId)
+    {
+        return $this->query("INSERT INTO `" . $this->_sTableEvt2Usr . "` (`user_id`, `event_id`, `clicked`) VALUES (:user_id, :event_id, 1) ON DUPLICATE KEY UPDATE `clicked`=1", [
+            'user_id' => $iUserId,
+            'event_id' => $iEventId
+        ]);
+    }
+
+    public function markAsRead($iUserId, $iEventId)
+    {
+        $this->queueDeleteByProfile($iUserId, $iEventId);
+
+        return $this->query("INSERT INTO `" . $this->_sTableRead . "` (`user_id`, `event_id`) VALUES (:user_id, :event_id) ON DUPLICATE KEY UPDATE `event_id`=:event_id", array(
+            'user_id' => $iUserId,
+            'event_id' => $iEventId
+        ));
+    }
+
+    public function getLastRead($iUserId)
+    {
+        return (int)$this->getOne("SELECT `event_id` FROM `" . $this->_sTableRead . "` WHERE `user_id`=:user_id LIMIT 1", array(
+            'user_id' => (int)$iUserId
+        ));
+    }
+
+    public function deleteEvent($aParams, $sWhereAddon = "")
+    {
+        $aEvents = $this->getAll("SELECT * FROM `{$this->_sTable}` WHERE " . $this->arrayToSQL($aParams, " AND ") . $sWhereAddon);
+        if(!empty($aEvents) && is_array($aEvents)) 
+            foreach($aEvents as $aEvent)
+                $this->queueDelete(array('event_id' => $aEvent['id']));
+
+        return parent::deleteEvent($aParams, $sWhereAddon);
+    }
+
+    public function getEvents($aParams, $bReturnCount = false)
+    {
+        bx_alert($this->_oConfig->getName(), 'get_events_before', 0, 0, [
+            'params' => &$aParams,
+        ]);
+
+        if($aParams['browse'] != 'list' || $aParams['type'] != BX_NTFS_TYPE_OBJECT_OWNER_AND_CONNECTIONS)
+            return parent::getEvents($aParams);
+
+        $bClickedIndicator = $this->_oConfig->isClickedIndicator();
+        $bCountOnly = !empty($aParams['count_only']);
+        $bViewerId = !empty($aParams['viewer_id']);
+
+        //--- Get query for 'Object Owner' notifications
+        $sLimitClause = isset($aParams['per_page']) ? "LIMIT 0, " . ($aParams['start'] + $aParams['per_page']) : "";
+
+        $aParams['type'] = BX_BASE_MOD_NTFS_TYPE_OBJECT_OWNER;
+        list($sMethod, $sSelectClause, $sJoinClausePo, $sWhereClausePo, $sOrderClausePo) = $this->_getSqlPartsEvents($aParams);
+
+        if($bClickedIndicator && $bViewerId) {
+            $sSelectClause .= ", IF(ISNULL(`{$this->_sTableEvt2Usr}`.`clicked`) OR `{$this->_sTableEvt2Usr}`.`clicked`=0, 0, 1) AS `clicked`";
+            $sJoinClausePo .= $this->prepareAsString(" LEFT JOIN `{$this->_sTableEvt2Usr}` ON `{$this->_sTable}`.`id`=`{$this->_sTableEvt2Usr}`.`event_id` AND `{$this->_sTableEvt2Usr}`.`user_id`=?", $aParams['viewer_id']);
+        }
+
+        $sQueryOwner = "SELECT {select}
+            FROM `{$this->_sTable}`
+            LEFT JOIN `{$this->_sTableHandlers}` ON `{$this->_sTable}`.`type`=`{$this->_sTableHandlers}`.`alert_unit` AND `{$this->_sTable}`.`action`=`{$this->_sTableHandlers}`.`alert_action` " . $sJoinClausePo . "
+            WHERE 1 " . $sWhereClausePo . (!$bCountOnly ? " " . $sOrderClausePo . " " . $sLimitClause : "");
+
+        //--- Get query for 'Connections based' notifications
+        $aParams['type'] = BX_BASE_MOD_NTFS_TYPE_CONNECTIONS;
+        list($sMethod, $sSelectClause, $sJoinClausePc, $sWhereClausePc, $sOrderClausePc) = $this->_getSqlPartsEvents($aParams);
+
+        if($bClickedIndicator && $bViewerId) {
+            $sSelectClause .= ", IF(ISNULL(`{$this->_sTableEvt2Usr}`.`clicked`) OR `{$this->_sTableEvt2Usr}`.`clicked`=0, 0, 1) AS `clicked`";
+            $sJoinClausePc .= $this->prepareAsString(" LEFT JOIN `{$this->_sTableEvt2Usr}` ON `{$this->_sTable}`.`id`=`{$this->_sTableEvt2Usr}`.`event_id` AND `{$this->_sTableEvt2Usr}`.`user_id`=?", $aParams['viewer_id']);
+        }
+
+        $sQueryConnections = "SELECT {select}
+            FROM `{$this->_sTable}`
+            LEFT JOIN `{$this->_sTableHandlers}` ON `{$this->_sTable}`.`type`=`{$this->_sTableHandlers}`.`alert_unit` AND `{$this->_sTable}`.`action`=`{$this->_sTableHandlers}`.`alert_action` " . $sJoinClausePc . "
+            WHERE 1 " . $sWhereClausePc . (!$bCountOnly ? " " . $sOrderClausePc . " " . $sLimitClause : "");
+
+        //--- Combine both queries in one
+        $bStartFromItem = isset($aParams['start_from_item']) && $aParams['start_from_item'] === true;
+
+        $sUnionMethod = 'getColumn';
+        $sUnionOrderClause = $sUnionLimitClause = '';
+        if(!$bCountOnly) {
+            $sUnionMethod = $sMethod;
+            $sUnionOrderClause = "ORDER BY `date` DESC, `id` DESC";
+            $sUnionLimitClause = isset($aParams['per_page']) ? "LIMIT " . (!$bStartFromItem ? $aParams['start'] : 0) . ", " . $aParams['per_page'] : "";
+        }
+
+        $aAlertParams = $aParams;
+        unset($aAlertParams['browse']);
+
+        /**
+         * Parts: 
+         * PO - notifications related to Owner's content
+         * PC - notifications related to Connections' content
+         */
+        bx_alert($this->_oConfig->getName(), 'get_events', 0, 0, [
+            'browse' => $aParams['browse'],
+            'params' => $aAlertParams,
+            'table' => $this->_sTable,
+            'method' => &$sUnionMethod,
+            'select_clause' => &$sSelectClause,
+            'join_clause_po' => &$sJoinClausePo,
+            'join_clause_pc' => &$sJoinClausePc,
+            'where_clause_po' => &$sWhereClausePo,
+            'where_clause_pc' => &$sWhereClausePc,
+            'order_clause_po' => &$sOrderClausePo,
+            'order_clause_pc' => &$sOrderClausePc,
+            'order_clause' => &$sUnionOrderClause,
+            'limit_clause' => &$sUnionLimitClause,
+            'query_po' => &$sQueryOwner,
+            'query_pc' => &$sQueryConnections,
+        ]);
+
+        $sQuery = "(" . $sQueryOwner . ") UNION (" . $sQueryConnections . ") {order} {limit}";
+        $sQuery = str_replace(['{select}', '{order}', '{limit}'], [$sSelectClause, $sUnionOrderClause, $sUnionLimitClause], $sQuery);
+        $aEntries = $this->$sUnionMethod($sQuery);
+
+        if($bCountOnly)
+            $aEntries = (int)array_sum($aEntries);
+
+        return $aEntries;
+    }
+
+    protected function _getSqlPartsEvents($aParams)
+    {
+        list($sMethod, $sSelectClause, $sJoinClause, $sWhereClause, $sOrderClause, $sLimitClause) = parent::_getSqlPartsEvents($aParams);
+
+        switch($aParams['browse']) {
+            case 'list':
+                if(!empty($aParams['count_only'])) {
+                    $sMethod = 'getOne';
+                    $sSelectClause = 'COUNT(DISTINCT `' . $this->_sTable . '`.`id`)';
+                }
+                else {
+                    $sSelectClause .= ", `{$this->_sTableHandlers}`.`priority` AS `priority`";
+
+                    if(in_array($aParams['type'], array(BX_BASE_MOD_NTFS_TYPE_CONNECTIONS, BX_NTFS_TYPE_OBJECT_OWNER_AND_CONNECTIONS)))
+                        $sSelectClause = 'DISTINCT ' . $sSelectClause;
+                }
+
+                break;
+        }
+
+        return array($sMethod, $sSelectClause, $sJoinClause, $sWhereClause, $sOrderClause, $sLimitClause);
+    }
+
+    protected function _getSqlPartsEventsList($aParams)
+    {
+        $sJoinClause = $sWhereClause = "";
+
+        //--- Apply 'start from'  filter
+        $sWhereClauseStartFrom = '';
+        if(isset($aParams['start_from_item']) && $aParams['start_from_item'] === true && $aParams['start'] != 0)
+            $sWhereClauseStartFrom = $this->prepareAsString("AND `{$this->_sTable}`.`id`<? ", (int)$aParams['start']);
+
+        //--- Apply status filter
+        $sWhereClauseStatus = '';
+        if(isset($aParams['active']))
+            $sWhereClauseStatus = $this->prepareAsString("AND `{$this->_sTable}`.`active`=? ", (int)$aParams['active']);
+
+        //--- Apply modules or handlers filter
+        $sWhereClauseModules = '';
+        if(!empty($aParams['modules']) && is_array($aParams['modules']))
+            $sWhereClauseModules = "AND `" . $this->_sTable . "`.`type` IN (" . $this->implode_escape($aParams['modules']) . ") ";
+
+        if($sWhereClauseModules == '') {
+            $aHidden = $this->_oConfig->getHandlersHidden();
+            $sWhereClauseModules = !empty($aHidden) && is_array($aHidden) ? "AND `" . $this->_sTableHandlers . "`.`id` NOT IN (" . $this->implode_escape($aHidden) . ") " : "";
+        }
+
+        //--- Check flag 'New'
+        $sWhereClauseNew = '';
+        if(!empty($aParams['new']) && !empty($aParams['owner_id']))
+            $sWhereClauseNew = $this->prepareAsString("AND `{$this->_sTable}`.`id`>? ", $this->getLastRead((int)$aParams['owner_id']));
+
+        //--- Check type
+        $sWhereClauseSettings = $sWhereClauseType = '';
+        if(!empty($aParams['owner_id']))
+            switch($aParams['type']) {
+                /*
+                 * Note. It isn't used for now and outdated.
+                 */
+                case BX_BASE_MOD_NTFS_TYPE_OWNER:
+                    $sWhereClauseType = $this->prepareAsString("AND `{$this->_sTable}`.`owner_id`=? ", $aParams['owner_id']);
+                    break;
+
+                case BX_BASE_MOD_NTFS_TYPE_OBJECT_OWNER:
+                    $sJoinClause .= $this->prepareAsString(" INNER JOIN `{$this->_sTableSettings}` ON `{$this->_sTableHandlers}`.`id`=`{$this->_sTableSettings}`.`handler_id` AND `{$this->_sTableSettings}`.`delivery`='" . BX_BASE_MOD_NTFS_DTYPE_SITE . "' AND `{$this->_sTableSettings}`.`active`='1' AND `{$this->_sTableSettings}`.`type`=?", BX_NTFS_STYPE_PERSONAL);
+
+                    $sJoinClauseS2U = " LEFT JOIN `{$this->_sTableSettings2Users}` ON `{$this->_sTableSettings}`.`id`=`{$this->_sTableSettings2Users}`.`setting_id` ";
+                    if(!empty($aParams['owner_id']))
+                        $sJoinClauseS2U .= $this->prepareAsString("AND `{$this->_sTableSettings2Users}`.`user_id`=? ", $aParams['owner_id']);
+
+                    $sJoinClause .= $sJoinClauseS2U;
+                    $sWhereClauseSettings = "AND ((ISNULL(`{$this->_sTableSettings2Users}`.`active`) AND `{$this->_sTableSettings}`.`value`='1') OR (NOT ISNULL(`{$this->_sTableSettings2Users}`.`active`) AND `{$this->_sTableSettings2Users}`.`active`='1')) ";
+
+                    $sWhereClauseType = $this->prepareAsString("AND `{$this->_sTable}`.`owner_id`<>`{$this->_sTable}`.`object_owner_id` AND ((`{$this->_sTable}`.`owner_id`=? AND `{$this->_sTable}`.`object_privacy_view`<0 AND `{$this->_sTable}`.`owner_id`=ABS(`{$this->_sTable}`.`object_privacy_view`)) OR `{$this->_sTable}`.`object_owner_id`=?) ", $aParams['owner_id'], $aParams['owner_id']);
+                    break;
+
+                case BX_BASE_MOD_NTFS_TYPE_CONNECTIONS:
+                    $oConnection = BxDolConnection::getObjectInstance($this->_oConfig->getObject('conn_subscriptions'));
+                    $aQueryParts = $oConnection->getConnectedContentAsSQLParts($this->_sPrefix . "events", 'owner_id', $aParams['owner_id']);
+                    if(empty($aQueryParts) || !is_array($aQueryParts) || empty($aQueryParts['join']))
+                        break;
+
+                    $sJoinClause .= " LEFT JOIN `sys_profiles` AS `tsp` ON `{$this->_sTable}`.`owner_id`=`tsp`.`id` " . $aQueryParts['join'];
+
+                    $sWhereClauseType = '';
+                    if(!empty($aQueryParts['fields']['added']))
+                        $sWhereClauseType = "AND `{$this->_sTable}`.`date` > " . $aQueryParts['fields']['added'];
+
+                    list($aModulesProfiles, $aModulesContexts) = $this->_oConfig->getProfileBasedModules();
+                    $sJoinClause .= $this->prepareAsString(" INNER JOIN `{$this->_sTableSettings}` ON `{$this->_sTableHandlers}`.`id`=`{$this->_sTableSettings}`.`handler_id` AND `{$this->_sTableSettings}`.`delivery`='" . BX_BASE_MOD_NTFS_DTYPE_SITE . "' AND `{$this->_sTableSettings}`.`active`='1' AND ((`{$this->_sTableSettings}`.`type`=? AND `tsp`.`type` IN (" . $this->implode_escape($aModulesProfiles) . ")) || (`{$this->_sTableSettings}`.`type`=? AND `tsp`.`type` IN (" . $this->implode_escape($aModulesContexts) . ")))", BX_NTFS_STYPE_FOLLOW_MEMBER, BX_NTFS_STYPE_FOLLOW_CONTEXT);
+
+                    $sJoinClauseS2U = " LEFT JOIN `{$this->_sTableSettings2Users}` ON `{$this->_sTableSettings}`.`id`=`{$this->_sTableSettings2Users}`.`setting_id` ";
+                    if(!empty($aParams['owner_id']))
+                        $sJoinClauseS2U .= $this->prepareAsString("AND `{$this->_sTableSettings2Users}`.`user_id`=? ", $aParams['owner_id']);
+
+                    $sJoinClause .= $sJoinClauseS2U;
+                    $sWhereClauseSettings = "AND ((ISNULL(`{$this->_sTableSettings2Users}`.`active`) AND `{$this->_sTableSettings}`.`value`='1') OR (NOT ISNULL(`{$this->_sTableSettings2Users}`.`active`) AND `{$this->_sTableSettings2Users}`.`active`='1')) ";
+                    break;
+
+                case BX_NTFS_TYPE_OBJECT_OWNER_AND_CONNECTIONS:
+                    /**
+                     * 'personal' notifications are taken by: 
+                     * 1. 'owner_id', in case of somebody performed an action in requested profile's context
+                     * 'object_privacy_view' < 0 AND 'owner_id' == ABS('object_privacy_view'))
+                     * Currently it happens when somebody posted a Direct Timeline post in requested profile.
+                     * 2. 'object_owner_id', in case of somebody performed an action with content 
+                     * owned by the requested profile.
+                     */
+                    $sWhereClauseObjectOwner = $this->prepareAsString("`{$this->_sTable}`.`owner_id` <> `{$this->_sTable}`.`object_owner_id` AND ((`{$this->_sTable}`.`owner_id`=? AND `{$this->_sTable}`.`object_privacy_view`<0 AND `{$this->_sTable}`.`owner_id`=ABS(`{$this->_sTable}`.`object_privacy_view`)) OR `{$this->_sTable}`.`object_owner_id`=?) ", $aParams['owner_id'], $aParams['owner_id']);
+
+                    /**
+                     * 'follow' notifications are taken by connection with `owner_id`
+                     */
+                    $sWhereClauseConnections = '0';
+                    $oConnection = BxDolConnection::getObjectInstance($this->_oConfig->getObject('conn_subscriptions'));
+                    $aQueryParts = $oConnection->getConnectedContentAsSQLPartsExt($this->_sTable, 'owner_id', $aParams['owner_id']);
+                    if(!empty($aQueryParts) && is_array($aQueryParts) && !empty($aQueryParts['join'])) {
+                        $sJoinClause .= " LEFT JOIN `sys_profiles` AS `tsp` ON `{$this->_sTable}`.`owner_id`=`tsp`.`id`";
+                        $sJoinClause .= " LEFT JOIN `" . $aQueryParts['join']['table'] . "` AS `" . $aQueryParts['join']['table_alias'] . "` ON (" . $aQueryParts['join']['condition'] . ")";
+
+                        $sWhereClauseConnections = "NOT ISNULL(`c`.`content`) ";
+                        if(!empty($aQueryParts['fields']['added']))
+                            $sWhereClauseConnections .= "AND `{$this->_sTable}`.`date` >= `" . $aQueryParts['fields']['added']['table_alias'] . "`.`" . $aQueryParts['fields']['added']['name'] . "` ";
+                    }
+
+                    list($aModulesProfiles, $aModulesContexts) = $this->_oConfig->getProfileBasedModules();
+                    $sJoinClause .= $this->prepareAsString(" INNER JOIN `{$this->_sTableSettings}` ON `{$this->_sTableHandlers}`.`id`=`{$this->_sTableSettings}`.`handler_id` AND `{$this->_sTableSettings}`.`delivery`='" . BX_BASE_MOD_NTFS_DTYPE_SITE . "' AND `{$this->_sTableSettings}`.`active`='1' AND (`{$this->_sTableSettings}`.`type`=? OR ((`{$this->_sTableSettings}`.`type`=? AND `tsp`.`type` IN (" . $this->implode_escape($aModulesProfiles) . ")) || (`{$this->_sTableSettings}`.`type`=? AND `tsp`.`type` IN (" . $this->implode_escape($aModulesContexts) . "))))", BX_NTFS_STYPE_PERSONAL, BX_NTFS_STYPE_FOLLOW_MEMBER, BX_NTFS_STYPE_FOLLOW_CONTEXT);
+
+                    $sJoinClauseS2U = " LEFT JOIN `{$this->_sTableSettings2Users}` ON `{$this->_sTableSettings}`.`id`=`{$this->_sTableSettings2Users}`.`setting_id` ";
+                    if(!empty($aParams['owner_id']))
+                        $sJoinClauseS2U .= $this->prepareAsString("AND `{$this->_sTableSettings2Users}`.`user_id`=? ", $aParams['owner_id']);
+
+                    $sJoinClause .= $sJoinClauseS2U;
+                    $sWhereClauseSettings = "AND ((ISNULL(`{$this->_sTableSettings2Users}`.`active`) AND `{$this->_sTableSettings}`.`value`='1') OR (NOT ISNULL(`{$this->_sTableSettings2Users}`.`active`) AND `{$this->_sTableSettings2Users}`.`active`='1')) ";
+
+                    $sWhereClauseType = "AND ((" . $sWhereClauseObjectOwner . ") OR (" . $sWhereClauseConnections . ")) ";
+                    break;
+            }
+
+        $aAlertParams = $aParams;
+        unset($aAlertParams['type'], $aAlertParams['owner_id']);
+
+        bx_alert($this->_oConfig->getName(), 'get_list_by_type', 0, 0, [
+            'type' => $aParams['type'],
+            'owner_id' => $aParams['owner_id'],
+            'params' => $aAlertParams,
+            'table' => $this->_sTable,
+            'join_clause' => &$sJoinClause,
+            'where_clause' => &$sWhereClause,
+            'where_clause_start_from' => &$sWhereClauseStartFrom,
+            'where_clause_status' => &$sWhereClauseStatus,
+            'where_clause_modules' => &$sWhereClauseModules,
+            'where_clause_new' => &$sWhereClauseNew,
+            'where_clause_settings' => &$sWhereClauseSettings,
+            'where_clause_type' => &$sWhereClauseType
+        ]);
+
+        $sWhereClause .= $sWhereClauseStartFrom;
+        $sWhereClause .= $sWhereClauseStatus;
+        $sWhereClause .= $sWhereClauseModules;
+        $sWhereClause .= $sWhereClauseNew;
+        $sWhereClause .= $sWhereClauseSettings;
+        $sWhereClause .= $sWhereClauseType;
+
+        return [$sJoinClause, $sWhereClause];
+    }
+
+    public function getEventsToProcess($iLimit = 0)
+    {
+        $aEvents = $this->getAll("SELECT * FROM `" . $this->_sTable . "` WHERE `id`>:id ORDER BY `id` ASC" . ($iLimit != 0 ? ' LIMIT ' . $iLimit : ''), array(
+            'id' => $this->_oConfig->getProcessedEvent()
+        ));
+
+        if(!empty($aEvents) && is_array($aEvents)) {
+            $aEventEnd = end($aEvents);
+
+            $this->_oConfig->setProcessedEvent($aEventEnd['id']);
+
+            reset($aEvents);
+        }
+
+        return $aEvents;
+    }
+
+    public function queueGet($aParams)
+    {
+    	$aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
+
+    	$sSelectClause = "*";
+    	$sWhereClause = $sOrderClause = $sLimitClause = "";
+
+        switch($aParams['type']) {
+            case 'count':
+            	$aMethod['name'] = 'getOne';
+
+                $sSelectClause = "COUNT(*)";
+                break;
+
+            case 'id':
+            	$aMethod['name'] = 'getRow';
+            	$aMethod['params'][1] = array(
+                    'id' => $aParams['id']
+                );
+
+                $sWhereClause = " AND `id`=:id";
+                break;
+
+            case 'all_to_send':
+                $aMethod['name'] = 'getAllWithKey';
+                $aMethod['params'][1] = 'id';
+                $aMethod['params'][2] = array(
+                    'timeout' => $aParams['timeout']
+                );
+
+                $sWhereClause = " AND `date`+:timeout < UNIX_TIMESTAMP()";
+                $sOrderClause = "`date` ASC";
+                break;
+        }
+
+        $sOrderClause = !empty($sOrderClause) ? "ORDER BY " . $sOrderClause : $sOrderClause;
+        $sLimitClause = !empty($sLimitClause) ? "LIMIT " . $sLimitClause : $sLimitClause;
+
+        $aMethod['params'][0] = "SELECT
+                " . $sSelectClause . "
+            FROM `" . $this->_sTableQueue . "`
+            WHERE 1" . $sWhereClause . " " . $sOrderClause . " " . $sLimitClause;
+
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+
+    public function queueAdd($aSet)
+    {
+        if(empty($aSet))
+            return false;
+
+        return (int)$this->query("INSERT INTO `" . $this->_sTableQueue . "` SET " . $this->arrayToSQL($aSet)) > 0;
+    }
+
+    public function queueDelete($aWhere)
+    {
+    	if(empty($aWhere))
+            return false;
+
+        return (int)$this->query("DELETE FROM `" . $this->_sTableQueue . "` WHERE " . $this->arrayToSQL($aWhere, ' AND ')) > 0;
+    }
+
+    public function queueDeleteByProfile($iProfileId, $iEventId)
+    {
+        return (int)$this->query("DELETE FROM `" . $this->_sTableQueue . "` WHERE `profile_id`=:profile_id AND `event_id`<=:event_id", array(
+            'profile_id' => $iProfileId,
+            'event_id' => $iEventId
+        )) > 0;
+    }
+
+    public function queueDeleteByIds($mixedId)
+    {
+        if(empty($mixedId))
+            return false;
+
+        if(!is_array($mixedId))
+            $mixedId = array($mixedId);
+
+        return (int)$this->query("DELETE FROM `" . $this->_sTableQueue . "` WHERE `id` IN (" . $this->implode_escape($mixedId) . ")") > 0;
+    }
+    
+    public function cleanEvents($iClearIntervalInDays)
+    {
+        $this->query("DELETE FROM `{$this->_sTable}` WHERE `date` < :date", array('date' => time() - $iClearIntervalInDays * 86400));
+    }   
+
+    public function filterProfileIdsByModule($aIds, $mixedModule)
+    {
+        if(!is_array($mixedModule))
+            $mixedModule = array($mixedModule);
+
+        return $this->getColumn("SELECT `id` FROM `sys_profiles` WHERE `id` IN (" . $this->implode_escape($aIds) . ") AND `type` IN (" . $this->implode_escape($mixedModule) . ")");
+    }
+}
+
+/** @} */

+ 82 - 0
modules/boonex/notifications/updates/13.0.11_13.0.12/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' => '13.0.12',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * '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/payment/updates/13.0.10_13.0.11/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' => 'Payment',
+    'version_from' => '13.0.10',
+    'version_to' => '13.0.11',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/payment/updates/update_13.0.10_13.0.11/',
+    'home_uri' => 'payment_update_13010_13011',
+
+    'module_dir' => 'boonex/payment/',
+    'module_uri' => 'payment',
+
+    'db_prefix' => 'bx_payment_',
+    'class_prefix' => 'BxPayment',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Payment',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/payment/updates/13.0.10_13.0.11/install/installer.php

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

+ 338 - 0
modules/boonex/payment/updates/13.0.10_13.0.11/source/classes/BxPaymentConfig.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    Payment Payment
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxPaymentConfig extends BxBaseModPaymentConfig
+{
+    protected $_bCreditsOnly;
+
+    protected $_iPayAttemptsMax;
+    protected $_iPayAttemptsInterval;
+
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $sBaseUrl = BX_DOL_URL_ROOT . $this->getBaseUri();
+
+        $this->CNF = array_merge($this->CNF, array(
+            // module icon
+            'ICON' => 'credit-card col-gray-dark',
+
+            // database tables
+            'TABLE_COMMISSIONS' => $aModule['db_prefix'] . 'commissions',
+            'TABLE_INVOICES' => $aModule['db_prefix'] . 'invoices',
+
+            // page URIs
+            'URL_JOIN' => 'page.php?i=payment-join',
+            'URL_CARTS' => 'page.php?i=payment-carts',
+            'URL_CART' => 'page.php?i=payment-cart',
+            'URL_CART_CHECKOUT' => $sBaseUrl . 'initialize_checkout/' . BX_PAYMENT_TYPE_SINGLE . '/',
+            'URL_AUTHORIZE' => $sBaseUrl . 'authorize_checkout/',
+            'URL_SUBSCRIBE' => $sBaseUrl . 'subscribe/',
+            'URL_SUBSCRIBE_JSON' => $sBaseUrl . 'subscribe_json/',
+            'URL_SUBSCRIPTIONS' => 'page.php?i=payment-sbs-list-my',
+            'URL_HISTORY' => 'page.php?i=payment-history',
+            'URL_ORDERS' => 'page.php?i=payment-orders',
+            'URL_INVOICES' => 'page.php?i=payment-invoices',
+            'URL_DETAILS' => 'page.php?i=payment-details',
+            'URL_RETURN' => 'page.php?i=payment-cart-thank-you',
+            'URL_RETURN_DATA' => $sBaseUrl . 'finalize_checkout/',
+            'URL_NOTIFY' => $sBaseUrl . 'notify/',
+            'URL_CHECKOUT_OFFLINE' => 'page.php?i=payment-checkout-offline',
+
+            'KEY_ARRAY_PRICE_SINGLE' => 'price_single',
+            'KEY_ARRAY_PRICE_RECURRING' => 'price_recurring',
+            'KEY_ARRAY_TRIAL_RECURRING' => 'trial_recurring',
+
+            // some params
+            'PARAM_CURRENCY_EXCHANGE_API' => 'bx_payment_currency_exchange_api',
+            'PARAM_CMSN_INVOICE_ISSUE_DAY' => 'bx_payment_inv_issue_day', //some day of month
+            'PARAM_CMSN_INVOICE_LIFETIME' => 'bx_payment_inv_lifetime', //in days
+            'PARAM_CMSN_INVOICE_EXPIRATION_NOTIFY' => 'bx_payment_inv_expiraction_notify', //in days, before expiration date
+
+            // objects
+            'OBJECT_FORM_PRELISTS_CURRENCIES' => 'bx_payment_currencies',
+            'OBJECT_PP_OFFLINE' => 'offline', //Offline payment provider
+            'OBJECT_PP_CREDITS' => 'credits', //Credits payment provider
+
+            'MODULE_CREDITS' => 'bx_credits',
+
+            // some language keys
+            'T' => array(
+                'MSG_ITEM_ADDED' => '_bx_payment_msg_item_added',
+                'MSG_ITEM_DELETED' => '_bx_payment_msg_item_deleted',
+                'MSG_SINGLE_SELLER_MODE' => '_bx_payment_msg_single_seller',
+                'ERR_WRONG_DATA' => '_bx_payment_err_wrong_data',
+                'ERR_REQUIRED_LOGIN' => '_bx_payment_err_required_login',
+                'ERR_NOT_ACCEPT_PAYMENTS' => '_bx_payment_err_not_accept_payments',
+                'ERR_SELF_PURCHASE' => '_bx_payment_err_self_purchase',
+                'ERR_INACTIVE_VENDOR' => '_bx_payment_err_inactive_vendor',
+                'ERR_UNKNOWN_VENDOR' => '_bx_payment_err_unknown_vendor',
+
+                'BLOCK_TITLE_CART' => '_bx_payment_page_block_title_cart',
+
+                'POPUP_PROVIDERS_SELECT' => '_bx_payment_popup_title_crd_providers_select', 
+
+                'TXT_CART_PROVIDER' => '_bx_payment_txt_cart_'
+            )
+        ));
+
+        $this->_aHtmlIds = array(
+            'cart' => array(
+                'providers_select' => $this->_sName . '-cart-providers-select-',
+            ),
+            'history' => array(
+                'order_history_view' => $this->_sName . '-order-view-history',
+            ),
+            'subscription' => array(
+                'order_subscription_view' => $this->_sName . '-order-view-subscription',
+                'order_subscription_get_details' => $this->_sName . '-order-get-details-subscription',
+                'order_subscription_change_details' => $this->_sName . '-order-change-details-subscription',
+                'order_subscription_get_billing' => $this->_sName . '-order-get-billing-subscription',
+                'order_subscription_change_billing' => $this->_sName . '-order-change-billing-subscription',
+                'form_subscription_change_billing' => $this->_sName . '-form-change-billing-subscription',
+                'form_subscription_change_details' => $this->_sName . '-form-change-details-subscription',
+            ),
+            'pending' => array(
+                'order_pending_view' => $this->_sName . '-order-view-pending',
+                'order_pending_process' => $this->_sName . '-order-process',
+            ),
+            'processed' => array(
+                'order_processed_view' => $this->_sName . '-order-view-processed',
+                'order_processed_add' => $this->_sName . '-order-add',
+                'order_processed_client_id' => $this->_sName . '-oa-client-id',
+                'order_processed_client' => $this->_sName . '-oa-client',
+                'order_processed_items' => $this->_sName . '-oa-items',	
+            ),
+            'commission' => array(
+                'popup_add' => $this->_sName . '-commission-popup-add',
+                'popup_edit' => $this->_sName . '-commission-popup-edit',
+            ),
+            'invoice' => array(
+                'popup_edit' => $this->_sName . '-invoice-popup-edit',
+            )
+        );
+
+        $this->_aPerPage = array(
+            'orders' => 10,
+            'history' => 10
+        );
+
+        $this->_aJsClasses = array(
+            'cart' => 'BxPaymentCart',
+            'history' => 'BxPaymentOrders',
+            'subscription' => 'BxPaymentSubscriptions',
+            'pending' => 'BxPaymentOrders',
+            'processed' => 'BxPaymentOrders',
+            'chargebee_v3' => 'BxPaymentProviderChargebeeV3',
+            'stripe' => 'BxPaymentProviderStripe',
+            'stripe_v3' => 'BxPaymentProviderStripeV3'
+        );
+
+        $this->_aJsObjects = array(
+            'cart' => 'oPaymentCart',
+            'history' => 'oPaymentOrders',
+            'subscription' => 'oBxPaymentSubscriptions',
+            'pending' => 'oPaymentOrders',
+            'processed' => 'oPaymentOrders',
+            'chargebee_v3' => 'oPaymentProviderChargebeeV3',
+            'stripe' => 'oPaymentProviderStripe',
+            'stripe_v3' => 'oPaymentProviderStripeV3'
+        );
+
+        $this->_bCreditsOnly = false;
+
+        $this->_iPayAttemptsMax = 3;
+        $this->_iPayAttemptsInterval = 86400; //--- in sec
+    }
+
+    public function init(&$oDb)
+    {
+        parent::init($oDb);
+
+        $sPrefix = $this->getPrefix('options');
+
+        $this->_bCreditsOnly = $this->_oDb->getParam($sPrefix . 'credits_only') == 'on';
+    }
+
+    public function isCreditsOnly()
+    {
+        return $this->_bCreditsOnly;
+    }
+
+    public function getPayAttemptsMax()
+    {
+        return $this->_iPayAttemptsMax;
+    }
+
+    public function getPayAttemptsInterval()
+    {
+        return $this->_iPayAttemptsInterval;
+    }
+
+    public function getPrice($sType, $aItem, $iPrecision = 2)
+    {
+    	$fPrice = 0;
+
+        switch($sType) {
+            case BX_PAYMENT_TYPE_SINGLE:
+                $fPrice = $aItem[$this->getKey('KEY_ARRAY_PRICE_SINGLE')];
+                break;
+
+            case BX_PAYMENT_TYPE_RECURRING:
+                $fPrice = $aItem[$this->getKey('KEY_ARRAY_PRICE_RECURRING')];
+                break;
+        }
+
+        return round((float)$fPrice, $iPrecision);
+    }
+
+    public function getTrial($sType, $aItem)
+    {
+        $iTrial = 0;
+
+        switch($sType) {
+            case BX_PAYMENT_TYPE_RECURRING:
+                $iTrial = $aItem[$this->getKey('KEY_ARRAY_TRIAL_RECURRING')];
+                break;
+        }
+
+        return (int)$iTrial;
+    }
+
+    public function getModuleId($mixedId)
+    {
+        if(is_numeric($mixedId))
+            return (int)$mixedId;
+
+        if(is_string($mixedId)) {
+            $aInfo = $this->_oDb->getModuleByName($mixedId);
+            if(!is_array($aInfo) || empty($aInfo['id']))
+                $aInfo = $this->_oDb->getModuleByUri($mixedId);
+
+            if(is_array($aInfo) && !empty($aInfo['id']))
+                return (int)$aInfo['id'];
+        } 
+
+        return 0;
+    }
+
+    public function a2s($a)
+    {
+        return base64_encode(serialize($a));
+    }
+
+    public function s2a($s)
+    {
+        return unserialize(base64_decode($s));
+    }
+            
+    public function descriptorA2S($a) 
+    {
+    	return implode($this->getDivider('DIVIDER_DESCRIPTOR'), $a);
+    }
+
+    public function descriptorS2A($s) 
+    {
+    	return explode($this->getDivider('DIVIDER_DESCRIPTOR'), $s);
+    }
+
+    public function descriptorsA2S($a) 
+    {
+    	return implode($this->getDivider('DIVIDER_DESCRIPTORS'), $a);
+    }
+
+    /**
+     * Conver items to array with necessary structure.
+     *
+     * @param  string/array $mixed - string with cart items divided with (:) or an array of cart items.
+     * @return array        with items.
+     */
+    public function descriptorsM2A($mixed)
+    {
+        $aResults = array();
+
+        if(is_string($mixed))
+           $aItems = explode($this->getDivider('DIVIDER_DESCRIPTORS'), $mixed);
+        else if(is_array($mixed))
+           $aItems = $mixed;
+        else
+            $aItems = array();
+
+        foreach($aItems as $mixedItem) {
+            $aItem = is_array($mixedItem) ? $mixedItem : $this->descriptorS2A($mixedItem);
+
+            $aResult = array('vendor_id' => $aItem[0], 'module_id' => $aItem[1], 'item_id' => $aItem[2], 'item_count' => $aItem[3]);
+            if(isset($aItem[4]))
+                $aResult['item_addons'] = $aItem[4];
+
+            $aResults[] = $aResult;
+        }
+
+        return $aResults;
+    }
+
+    public function putCustom($mDsc, $aCustom, &$aCustoms)
+    {
+        if(empty($aCustom) || !is_array($aCustom))
+            return;
+
+        if(is_array($mDsc))
+            $mDsc = $this->descriptorA2S(array_slice($mDsc, 0, 3));
+
+        $aCustoms[$mDsc] = !empty($aCustoms[$mDsc]) && is_array($aCustoms[$mDsc]) ? array_merge($aCustoms[$mDsc], $aCustom) : $aCustom;
+    }
+
+    public function getCustom($mDsc, &$aCustoms)
+    {
+        if(is_array($mDsc))
+            $mDsc = $this->descriptorA2S(array_slice($mDsc, 0, 3));
+
+        if(empty($aCustoms) || !is_array($aCustoms) || empty($aCustoms[$mDsc]))
+            return array();
+
+        return $aCustoms[$mDsc];
+    }
+
+    public function pullCustom($mDsc, &$aCustoms)
+    {
+        if(is_array($mDsc))
+            $mDsc = $this->descriptorA2S(array_slice($mDsc, 0, 3));
+
+        if(empty($aCustoms) || !is_array($aCustoms) || empty($aCustoms[$mDsc]))
+            return array();
+
+        $aResult = $aCustoms[$mDsc];
+        unset($aCustoms[$mDsc]);           
+
+        return $aResult;
+    }
+
+    public function http2https($s)
+    {
+    	if(strncmp($s, 'https://', 8) === 0)
+    		return $s;
+
+        return 'https://' . bx_ltrim_str($s, 'http://');
+    }
+
+    public function sortByColumn($sColumn, &$aValues)
+    {
+        return usort($aValues, function($aV1, $aV2) use ($sColumn) {
+            if($aV1[$sColumn] == $aV2[$sColumn])
+                return 0;
+
+            return $aV1[$sColumn] < $aV2[$sColumn] ? -1 : 1;
+        });
+    }
+}
+
+/** @} */

+ 632 - 0
modules/boonex/payment/updates/13.0.10_13.0.11/source/classes/BxPaymentProviderStripeBasic.php

@@ -0,0 +1,632 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+define('STRP_MODE_LIVE', 1);
+define('STRP_MODE_TEST', 2);
+
+class BxPaymentProviderStripeBasic extends BxBaseModPaymentProvider
+{
+    protected $_sFormDetails;
+    protected $_sFormDisplayDetailsEdit;
+
+    protected $_sFormCard;
+    protected $_sFormDisplayCardAdd;
+
+    protected $_iMode;
+    protected $_bCheckAmount;
+    protected $_bProrate;
+
+    protected $_oCustomer;
+
+
+    function __construct($aConfig)
+    {
+    	$this->MODULE = 'bx_payment';
+
+        parent::__construct($aConfig);
+
+        $this->_aSbsStatuses = array(
+            'incomplete' => BX_PAYMENT_SBS_STATUS_UNPAID, 
+            'incomplete_expired' => BX_PAYMENT_SBS_STATUS_UNPAID, 
+            'trialing' => BX_PAYMENT_SBS_STATUS_TRIAL, 
+            'active' => BX_PAYMENT_SBS_STATUS_ACTIVE, 
+            'past_due' => BX_PAYMENT_SBS_STATUS_UNPAID,
+            'unpaid' => BX_PAYMENT_SBS_STATUS_UNPAID,
+            'canceled' => BX_PAYMENT_SBS_STATUS_CANCELED,
+        );
+
+        $this->_sFormDetails = 'bx_payment_form_strp_details';
+        $this->_sFormDisplayDetailsEdit = 'bx_payment_form_strp_details_edit';
+
+        $this->_sFormCard = 'bx_payment_form_strp_card';
+        $this->_sFormDisplayCardAdd = 'bx_payment_form_strp_card_add';
+
+        $this->_bProrate = false;
+
+        $this->_aIncludeJs = array(
+            'main.js',
+        );
+
+        $this->_aIncludeCss = array(
+            'stripe.css'
+        );
+
+        $this->_oCustomer = null;
+
+        \Stripe\Stripe::setApiKey($this->_getSecretKey());
+    }
+
+    public function initOptions($aOptions)
+    {
+    	parent::initOptions($aOptions);
+
+    	$this->_iMode = (int)$this->getOption('mode');
+    	$this->_bCheckAmount = $this->getOption('check_amount') == 'on';
+    	$this->_bUseSsl = $this->getOption('ssl') == 'on';
+    }
+
+    public function addJsCss()
+    {
+    	if(!$this->isActive())
+    		return;
+
+        $this->_oModule->_oTemplate->addJs($this->_aIncludeJs);
+        $this->_oModule->_oTemplate->addCss($this->_aIncludeCss);
+    }
+
+    public function getJsObject($aParams = array())
+    {
+        $sJsObject = $this->_oModule->_oConfig->getJsObject($this->_sName);
+        if(isset($aParams['iModuleId'], $aParams['iSellerId'], $aParams['iItemId']))
+            $sJsObject .= '_' . md5($aParams['iModuleId'] . '-' . $aParams['iSellerId'] . '-' . $aParams['iItemId']);
+        
+        return $sJsObject;
+    }
+
+    public function notify()
+    {
+        $iResult = $this->_processEvent();
+        http_response_code($iResult);
+    }
+
+    public function getButtonSingle($iClientId, $iVendorId, $aParams = array())
+    {
+        $aItems = array();
+        $aCartInfo = $this->_oModule->getObjectCart()->getInfo(BX_PAYMENT_TYPE_SINGLE, $iClientId, (int)$iVendorId, $aItems);
+        if(empty($aCartInfo) || !is_array($aCartInfo))
+            return '';
+
+    	return $this->_getButton(BX_PAYMENT_TYPE_SINGLE, $iClientId, $iVendorId, array_merge($aParams, array(
+            'sVendorName' => _t($this->_sLangsPrefix . 'txt_payment_to', $aCartInfo['vendor_name']),
+            'sVendorCurrency' => $aCartInfo['vendor_currency_code'],
+            'sVendorIcon' => $aCartInfo['vendor_avatar'],
+    	)));
+    }
+
+    public function getButtonSingleJs($iClientId, $iVendorId, $aParams = array())
+    {
+        $aItems = array();
+        $aCartInfo = $this->_oModule->getObjectCart()->getInfo(BX_PAYMENT_TYPE_SINGLE, $iClientId, (int)$iVendorId, $aItems);
+        if(empty($aCartInfo) || !is_array($aCartInfo))
+            return '';
+
+    	return $this->_getButtonJs(BX_PAYMENT_TYPE_SINGLE, $iClientId, $iVendorId, array_merge($aParams, array(
+            'sVendorName' => _t($this->_sLangsPrefix . 'txt_payment_to', $aCartInfo['vendor_name']),
+            'sVendorCurrency' => $aCartInfo['vendor_currency_code'],
+            'sVendorIcon' => $aCartInfo['vendor_avatar'],
+    	)));
+    }
+
+    public function getButtonRecurring($iClientId, $iVendorId, $aParams = array())
+    {
+    	$aVendor = $this->_oModule->getVendorInfo((int)$iVendorId);
+
+        return $this->_getButton(BX_PAYMENT_TYPE_RECURRING, $iClientId, $iVendorId, array_merge($aParams, array(
+            'sVendorName' => _t($this->_sLangsPrefix . 'txt_payment_to', $aVendor['name']),
+            'sVendorCurrency' => $aVendor['currency_code'],
+            'sVendorIcon' => $aVendor['avatar'],
+        )));
+    }
+    
+    public function getButtonRecurringJs($iClientId, $iVendorId, $aParams = array())
+    {
+    	$aVendor = $this->_oModule->getVendorInfo((int)$iVendorId);
+
+        return $this->_getButtonJs(BX_PAYMENT_TYPE_RECURRING, $iClientId, $iVendorId, array_merge($aParams, array(
+            'sVendorName' => _t($this->_sLangsPrefix . 'txt_payment_to', $aVendor['name']),
+            'sVendorCurrency' => $aVendor['currency_code'],
+            'sVendorIcon' => $aVendor['avatar'],
+        )));
+    }
+
+    public function getMenuItemsActionsRecurring($iClientId, $iVendorId, $aParams = array())
+    {
+        if(empty($aParams['order']))
+            return array();
+
+        $sJsObject = $this->_oModule->_oConfig->getJsObject(BX_PAYMENT_ORDERS_TYPE_SUBSCRIPTION);
+
+        $sPrefix = 'bx-payment-strp-';
+        return array(
+            array('id' => $sPrefix . 'details', 'name' => $sPrefix . 'details', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".getDetails(this, '" . $aParams['id'] . "')", 'target' => '_self', 'title' => _t('_bx_payment_strp_menu_item_title_details')),
+            array('id' => $sPrefix . 'details_change', 'name' => $sPrefix . 'details_change', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".changeDetails(this, '" . $aParams['id'] . "')", 'target' => '_self', 'title' => _t('_bx_payment_strp_menu_item_title_details_change')),
+            array('id' => $sPrefix . 'billing', 'name' => $sPrefix . 'billing', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".getBilling(this, '" . $aParams['id'] . "')", 'target' => '_self', 'title' => _t('_bx_payment_strp_menu_item_title_billing')),
+            array('id' => $sPrefix . 'billing_change', 'name' => $sPrefix . 'billing_change', 'class' => '', 'link' => 'javascript:void(0)', 'onclick' => "javascript:" . $sJsObject . ".changeBilling(this, '" . $aParams['id'] . "')", 'target' => '_self', 'title' => _t('_bx_payment_strp_menu_item_title_billing_change')),
+        );
+    }
+
+    public function getDetailsRecurring($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        $oSubscription = $this->_retrieveSubscription($sCustomerId, $sSubscriptionId);
+        if($oSubscription === false)
+            return '';
+
+        $aSubscription = $oSubscription->jsonSerialize();
+        if(empty($aSubscription) || !is_array($aSubscription))
+            return '';
+
+        $sName = '';
+        if(!empty($aSubscription['plan']['name']))
+            $sName = $aSubscription['plan']['name'];
+        else if(!empty($aSubscription['plan']['product'])){
+            $oProduct = $this->_retrieveProduct($aSubscription['plan']['product']);
+            if($oProduct !== false) 
+                $sName = $oProduct->name;
+        }
+
+        $sNone = _t('_bx_payment_txt_none');
+        return $this->_oModule->_oTemplate->parseHtmlByName('strp_details_recurring.html', array(
+            'plan' => $sName,
+            'cost' => _t('_bx_payment_strp_txt_cost_mask', (int)$aSubscription['plan']['amount'] / 100, $aSubscription['plan']['currency'], $aSubscription['plan']['interval']),
+            'status' => $aSubscription['status'],
+            'created' => bx_time_js($aSubscription['created'], BX_FORMAT_DATE_TIME, true),
+            'started' => !empty($aSubscription['start']) ? bx_time_js($aSubscription['start'], BX_FORMAT_DATE_TIME, true) : $sNone,
+            'trial_start' => !empty($aSubscription['trial_start']) ? bx_time_js($aSubscription['trial_start'], BX_FORMAT_DATE_TIME, true) : $sNone,
+            'trial_end' => !empty($aSubscription['trial_end']) ? bx_time_js($aSubscription['trial_end'], BX_FORMAT_DATE_TIME, true) : $sNone,
+            'cperiod_start' => !empty($aSubscription['current_period_start']) ? bx_time_js($aSubscription['current_period_start'], BX_FORMAT_DATE_TIME, true) : $sNone,
+            'cperiod_end' => !empty($aSubscription['current_period_end']) ? bx_time_js($aSubscription['current_period_end'], BX_FORMAT_DATE_TIME, true) : $sNone,
+        ));
+    }
+
+    public function changeDetailsRecurring($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        $oForm = BxDolForm::getObjectInstance($this->_sFormDetails, $this->_sFormDisplayDetailsEdit, $this->_oModule->_oTemplate);
+        $oForm->aFormAttrs['id'] = $this->_oModule->_oConfig->getHtmlIds('subscription', 'form_subscription_change_details');
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . $this->_oModule->_oConfig->getBaseUri() . 'subscription_change_details/' . $iPendingId;
+
+        $oForm->aInputs['item_id']['values'] = $this->_getDataChangeDetailsRecurring($iPendingId);
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+            $aResultError = array('code' => 1, 'message' => _t('_bx_payment_strp_err_details_changed'));
+
+            $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
+            list($iVendorId, $iModuleId, $iItemId, $iItemCount) = $this->_oModule->_oConfig->descriptorS2A($aPending['items']);
+
+            $aItem = $this->_oModule->callGetCartItem($iModuleId, array($oForm->getCleanValue('item_id'), $aPending['client_id']));
+    		if(empty($aItem) || !is_array($aItem))
+    			return $aResultError;
+
+            $oSubscription = $this->_retrieveSubscription($sCustomerId, $sSubscriptionId);
+            if($oSubscription === false)
+                return $aResultError;
+
+            $oSubscription->plan = $aItem['name'];
+            $oSubscription->prorate = $this->_bProrate;
+            $oSubscription = $oSubscription->save();
+            if(strcmp($oSubscription->plan->id, $aItem['name']) !== 0)
+                return $aResultError;
+
+            $sItems = $this->_oModule->_oConfig->descriptorA2S(array($iVendorId, $iModuleId, $aItem['id'], $iItemCount));
+            if(!$this->_oModule->_oDb->updateOrderPending($iPendingId, array('items' => $sItems)))
+                return $aResultError;
+
+            $this->_oModule->callReregisterSubscriptionItem($iModuleId, array($aPending['client_id'], $aPending['seller_id'], $iItemId, $aItem['id'], $aPending['order']));
+
+            return array('code' => 0, 'message' => _t('_bx_payment_strp_msg_details_changed'));
+        }
+
+        return $this->_oModule->_oTemplate->parseHtmlByName('strp_details_change_recuring.html', array(
+            'object' => $this->_oModule->_oConfig->getJsObject('subscription'),
+            'form' => $oForm->getCode(),
+            'form_id' => $oForm->aFormAttrs['id'],
+        ));
+    }
+
+    public function getBillingRecurring($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        $aCard = ['brand' => '', 'country' => '', 'funding' => '', 'last4' => '', 'exp_month' => '', 'exp_year' => '', 'cvc_check' => ''];
+
+        $oCard = $this->_retrieveCard($sCustomerId);
+        if($oCard === false && ($oSubscription = $this->_retrieveSubscription($sCustomerId, $sSubscriptionId)) !== false) {
+            $sPaymentMethodId = $oSubscription->default_payment_method;
+            if(!empty($sPaymentMethodId) && ($oPaymentMethod = $this->_retrievePaymentMethod($sPaymentMethodId)) !== false)
+                $oCard = $oPaymentMethod->card;
+        }
+
+        if(!empty($oCard))
+            $aCard = $oCard->jsonSerialize();
+
+        return $this->_oModule->_oTemplate->parseHtmlByName('strp_billing_recurring.html', array(
+            'brand' => $aCard['brand'],
+            'origin' => $aCard['country'],
+            'type' => $aCard['funding'],
+            'number' => _t('_bx_payment_strp_txt_card_number_mask', $aCard['last4']),
+            'expires' => _t('_bx_payment_strp_txt_card_expires_mask', $aCard['exp_month'], $aCard['exp_year']),
+            'cvc' => _t(!empty($aCard['cvc_check']) && strcmp($aCard['cvc_check'], 'pass') === 0 ? '_bx_payment_strp_txt_card_cvc_passed' : '_bx_payment_txt_none'),
+        ));
+    }
+
+    public function changeBillingRecurring($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        $oForm = BxDolForm::getObjectInstance($this->_sFormCard, $this->_sFormDisplayCardAdd, $this->_oModule->_oTemplate);
+        $oForm->aFormAttrs['id'] = $this->_oModule->_oConfig->getHtmlIds('subscription', 'form_subscription_change_billing');
+        $oForm->aFormAttrs['action'] = BX_DOL_URL_ROOT . $this->_oModule->_oConfig->getBaseUri() . 'subscription_change_billing/' . $iPendingId;
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+            $aResultError = array('code' => 1, 'message' => _t('_bx_payment_strp_err_billing_changed'));
+
+            $aMatch = array();
+            if(!preg_match('/^([0-9]{2})\D([0-9]{4})$/i', $oForm->getCleanValue('card_expire'), $aMatch))
+                return $aResultError;
+
+            list($iMonth, $iYear) = array_slice($aMatch, 1);
+
+        	$aToken = $this->_createToken(array(
+                'number' => $oForm->getCleanValue('card_number'),
+                'exp_month' => $iMonth,
+                'exp_year' => $iYear,
+                'cvc' => $oForm->getCleanValue('card_cvv')
+            ));
+            if(empty($aToken) || !is_array($aToken))
+                return $aResultError;
+
+            $aCard = $this->_createCard(BX_PAYMENT_TYPE_RECURRING, $sCustomerId, $aToken['id']);
+            if(empty($aCard) || !is_array($aCard))
+                return $aResultError;
+
+            $oCustomer = $this->_retrieveCustomer(BX_PAYMENT_TYPE_RECURRING, $sCustomerId);
+            if($oCustomer === false)
+                return $aResultError;
+
+            $oCustomer->default_source = $aCard['id'];
+            $oCustomer->save();
+
+            return array('code' => 0, 'message' => _t('_bx_payment_strp_msg_billing_changed'));
+        }
+
+        return $this->_oModule->_oTemplate->parseHtmlByName('strp_billing_change_recuring.html', array(
+            'object' => $this->_oModule->_oConfig->getJsObject('subscription'),
+            'form' => $oForm->getCode(),
+            'form_id' => $oForm->aFormAttrs['id'],
+        ));
+    }
+
+    public function cancelRecurring($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        $oSubscription = $this->_cancelSubscription($sCustomerId, $sSubscriptionId);
+        if($oSubscription === false)
+            return false;
+
+        return true;
+    }
+
+    public function getSubscription($iPendingId, $sCustomerId, $sSubscriptionId)
+    {
+        $oSubscription = $this->_retrieveSubscription($sCustomerId, $sSubscriptionId);
+        if($oSubscription === false)
+            return array();
+
+        $aSubscription = $oSubscription->jsonSerialize();
+        if(empty($aSubscription) || !is_array($aSubscription))
+            return array();
+
+        $sStatus = isset($this->_aSbsStatuses[$aSubscription['status']]) ? $this->_aSbsStatuses[$aSubscription['status']] : BX_PAYMENT_SBS_STATUS_UNKNOWN;
+
+        return array(
+            'status' => $sStatus,
+            'created' => $aSubscription['created'],
+            'started' => !empty($aSubscription['start']) ? $aSubscription['start'] : 0,
+            'trial_start' => !empty($aSubscription['trial_start']) ? $aSubscription['trial_start'] : 0,
+            'trial_end' => !empty($aSubscription['trial_end']) ? $aSubscription['trial_end'] : 0,
+            'cperiod_start' => !empty($aSubscription['current_period_start']) ? $aSubscription['current_period_start'] : 0,
+            'cperiod_end' => !empty($aSubscription['current_period_end']) ? $aSubscription['current_period_end'] : 0,
+        );
+    }
+
+    protected function _getPublicKey()
+    {
+        return $this->_iMode == STRP_MODE_LIVE ? $this->getOption('live_pub_key') : $this->getOption('test_pub_key');
+    }
+
+    protected function _getSecretKey()
+    {
+        return $this->_iMode == STRP_MODE_LIVE ? $this->getOption('live_sec_key') : $this->getOption('test_sec_key');
+    }
+
+    protected function _retrieveCustomer($sType, $sId)
+    {
+        $oCustomer = null;
+        bx_alert($this->_oModule->_oConfig->getName(), $this->_sName . '_retrieve_customer', 0, false, array(
+            'type' => $sType,
+            'customer_id' => &$sId,
+            'customer_object' => &$oCustomer
+        ));
+
+        try {
+            if(empty($oCustomer))
+                $oCustomer = \Stripe\Customer::retrieve($sId);
+        }
+        catch (Exception $oException) {
+            return $this->_processException('Retrieve Customer Error: ', $oException);
+        }
+
+        return $oCustomer;
+    }
+
+    protected function _retrieveCharge($sId)
+    {
+        $oCharge = null;
+        bx_alert($this->_oModule->_oConfig->getName(), $this->_sName . '_retrieve_charge', 0, false, array(
+            'charge_id' => &$sId,
+            'charge_object' => &$oCharge
+        ));
+
+        try {
+            if(empty($oCharge))
+                $oCharge = \Stripe\Charge::retrieve($sId);
+        }
+        catch (Exception $oException) {
+            return $this->_processException('Retrieve Charge Error: ', $oException);
+        }
+
+        return $oCharge;
+    }
+
+    protected function _retrieveSubscription($sCustomerId, $sSubscriptionId)
+    {
+        try {
+            $oCustomer = $this->_retrieveCustomer(BX_PAYMENT_TYPE_RECURRING, $sCustomerId);
+            $oSubscription = $oCustomer->subscriptions->retrieve($sSubscriptionId);
+        }
+        catch (Exception $oException) {
+            return $this->_processException('Retrieve Subscription Error: ', $oException);
+        }
+
+        return $oSubscription;
+    }
+
+    protected function _cancelSubscription($sCustomerId, $sSubscriptionId)
+    {
+        try {
+            $oSubscription = $this->_retrieveSubscription($sCustomerId, $sSubscriptionId);
+            $oSubscription = $oSubscription->cancel();
+        }
+        catch (Exception $oException) {
+                return $this->_processException('Cancel Subscription Error: ', $oException);
+        }
+
+        bx_alert($this->_oModule->_oConfig->getName(), $this->_sName . '_cancel_subscription', 0, false, array(
+            'subscription_id' => $sSubscriptionId,
+            'subscription_object' => &$oSubscription
+        ));
+
+        return $oSubscription;
+    }
+
+    protected function _retrievePaymentMethod($sPaymentMethodId)
+    {
+        return false;
+    }
+
+    protected function _retrieveProduct($sId)
+    {
+        $oProduct = null;
+        bx_alert($this->_oModule->_oConfig->getName(), $this->_sName . '_retrieve_product', 0, false, array(
+            'product_id' => &$sId,
+            'product_object' => &$oProduct
+        ));
+
+        if(!empty($oProduct))
+            return $oProduct;
+
+        try {
+            $oProduct = \Stripe\Product::retrieve($sId);
+        }
+        catch (Exception $oException) {
+            return $this->_processException('Retrieve Product Error: ', $oException);
+        }
+
+        return $oProduct;
+    }
+
+    protected function _listPlans($iLimit = 100)
+    {
+        if($iLimit <= 0)
+            $iLimit = 1;
+        if($iLimit > 100)
+            $iLimit = 100;
+
+        try {
+            $oPlans = \Stripe\Plan::all(array('limit' => $iLimit));
+        }
+        catch (Exception $oException) {
+            return $this->_processException('List Plans Error: ', $oException);
+        }
+
+        return $oPlans;
+    }
+
+    protected function _retrieveEvent($sId) {
+        try {
+            $oEvent = \Stripe\Event::retrieve($sId);
+        }
+        catch (Exception $oException) {
+            return $this->_processException('Retrieve Event Error: ', $oException);
+        }
+
+        return $oEvent;
+    }
+
+    protected function _processEvent()
+    {
+        $sInput = @file_get_contents("php://input");
+        $aEvent = json_decode($sInput, true);
+        if(empty($aEvent) || !is_array($aEvent)) 
+            return 404;
+
+        $sType = $aEvent['type'];
+        if(!in_array($sType, array('invoice.payment_succeeded', 'charge.refunded', 'customer.subscription.deleted')))
+            return 200;
+
+        $this->log('Webhooks: ' . (!empty($sType) ? $sType : ''));
+        $this->log($aEvent);
+
+        $sMethod = '_processEvent' . bx_gen_method_name($sType, array('.', '_', '-'));
+    	if(!method_exists($this, $sMethod))
+            return 200;
+
+    	return $this->$sMethod($aEvent) ? 200 : 403;
+    }
+
+    protected function _processEventInvoicePaymentSucceeded(&$aEvent)
+    {
+        $mixedResult = $this->_getData($aEvent);
+        if($mixedResult === false)
+            return false;
+
+        list($aPending, $oCharge) = $mixedResult;
+        if(empty($aPending) || !is_array($aPending) || empty($oCharge))
+            return false;
+
+        $fChargeAmount = (float)$oCharge->amount / 100;
+        $sChargeCurrency = strtoupper($oCharge->currency);
+        if($this->_bCheckAmount && ((float)$aPending['amount'] != $fChargeAmount || strcasecmp($this->_oModule->_oConfig->getDefaultCurrencyCode(), $sChargeCurrency) !== 0))
+            return false;
+
+        if($aPending['type'] == BX_PAYMENT_TYPE_RECURRING)
+            $this->_oModule->getObjectSubscriptions()->prolong($aPending);
+
+        return $this->_oModule->registerPayment($aPending);
+    }
+
+    protected function _processEventChargeRefunded(&$aEvent)
+    {
+        $mixedResult = $this->_getData($aEvent);
+        if($mixedResult === false)
+            return false;
+
+        list($aPending) = $mixedResult;
+        if(empty($aPending) || !is_array($aPending))
+            return false;
+
+        return $this->_oModule->refundPayment($aPending);
+    }
+
+    protected function _processEventCustomerSubscriptionDeleted(&$aEvent)
+    {
+        $mixedResult = $this->_getData($aEvent);
+        if($mixedResult === false)
+            return false;
+
+        list($aPending) = $mixedResult;
+        if(empty($aPending) || !is_array($aPending))
+            return true;
+
+        return $this->_oModule->getObjectSubscriptions()->cancelLocal($aPending);
+    }
+
+    protected function _processException($sMessage, &$oException)
+    {
+        $aError = $oException->getJsonBody();
+
+        $sMessage = $aError['error']['message'];
+        if(empty($sMessage))
+            $sMessage = $oException->getMessage();
+
+        $this->log($sMessage . $aError['error']['message']);
+        $this->log($aError);
+
+        return false;
+    }
+
+    protected function _getData(&$aEvent, $bRetrieve = true)
+    {
+        if($bRetrieve)
+            $oEvent = $this->_retrieveEvent($aEvent['id']);
+        else 
+            $oEvent = \Stripe\Util\Util::convertToStripeObject($aEvent, array());
+
+        if(empty($oEvent))
+            return false;
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'order', 'order' => $oEvent->data->object->subscription));
+        $oCharge = $this->_retrieveCharge($oEvent->data->object->charge);
+
+        return array($aPending, $oCharge);
+    }
+
+    protected function _getDataChangeDetailsRecurring($iPendingId)
+    {
+        $aResult = array();
+
+        $aPending = $this->_oModule->_oDb->getOrderPending(array('type' => 'id', 'id' => $iPendingId));
+        list($iVendorId, $iModuleId, $iItemId) = $this->_oModule->_oConfig->descriptorS2A($aPending['items']);
+        if(empty($iModuleId) || empty($iVendorId) || (int)$iVendorId != (int)$aPending['seller_id'])
+            return $aResult;
+
+        $aItems = $this->_oModule->callGetCartItems((int)$iModuleId, array($iVendorId));
+        if(empty($aItems) || !is_array($aItems))
+            return $aResult;
+
+        $oPlans = $this->_listPlans();
+        if($oPlans === false)
+            return $aResult;
+
+        $aPlans = $oPlans->jsonSerialize();
+        if(empty($aPlans) || !is_array($aPlans) || empty($aPlans['data']) || !is_array($aPlans['data']))
+            return $aResult;
+
+        $aPlans = $aPlans['data'];
+
+        $aPlanNames = array();
+        foreach($aPlans as $aPlan)
+            $aPlanNames[] = $aPlan['id'];
+        if(empty($aPlanNames) || !is_array($aPlanNames))
+            return $aResult;
+
+        foreach($aItems as $aItem) {
+            $fPrice = $this->_oModule->_oConfig->getPrice(BX_PAYMENT_TYPE_RECURRING, $aItem);
+            if($fPrice == 0 || (int)$aItem['id'] == (int)$iItemId || !in_array($aItem['name'], $aPlanNames))
+                continue;
+
+            $aResult[] = array('key' => $aItem['id'], 'value' => $aItem['title']);
+        }
+
+        return $aResult;
+    }
+
+    protected function _getButton($sType, $iClientId, $iVendorId, $aParams = array())
+    {
+        list($sJsCode, $sJsMethod) = $this->_getButtonJs($sType, $iClientId, $iVendorId, $aParams);
+
+    	return $this->_oModule->_oTemplate->parseHtmlByName('strp_button_' . $sType . '.html', array(
+            'type' => $sType,
+            'caption' => _t($this->_sLangsPrefix . 'strp_txt_checkout_with_' . $sType, $this->_sCaption),  
+            'onclick' => $sJsMethod,
+            'js_object' => $this->_oModule->_oConfig->getJsObject($this->_sName),
+            'js_code' => $sJsCode,
+    	));
+    }
+}
+
+/** @} */

+ 70 - 0
modules/boonex/payment/updates/13.0.10_13.0.11/source/install/config.php

@@ -0,0 +1,70 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Payment Payment
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_payment',
+    'title' => 'Payment',
+    'note' => 'Payment module.',
+    'version' => '13.0.11',
+    'vendor' => 'Boonex',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/payment/',
+    'home_uri' => 'payment',
+
+    'db_prefix' => 'bx_payment_',
+    'class_prefix' => 'BxPayment',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Payment',
+
+    /**
+     * 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(),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/profiler/updates/13.0.3_13.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' => 'Profiler',
+    'version_from' => '13.0.3',
+    'version_to' => '13.0.4',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/profiler/updates/update_13.0.3_13.0.4/',
+    'home_uri' => 'profiler_update_1303_1304',
+
+    'module_dir' => 'boonex/profiler/',
+    'module_uri' => 'profiler',
+
+    'db_prefix' => 'bx_profiler_',
+    'class_prefix' => 'BxProfiler',
+
+    /**
+     * 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 Profiler',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/profiler/updates/13.0.3_13.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 BxProfilerUpdater extends BxDolStudioUpdater
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}

+ 680 - 0
modules/boonex/profiler/updates/13.0.3_13.0.4/source/classes/BxProfiler.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    Profiler Profiler
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+define ('BX_PROFILER', true);
+define ('BX_PROFILER_DISPLAY', 1);
+
+require_once(BX_DIRECTORY_PATH_MODULES . 'boonex/profiler/install/config.php');
+$GLOBALS['bx_profiler_module'] = array (
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_profiler',
+    'title' => $aConfig['title'],
+    'vendor' => $aConfig['vendor'],
+    'path' => $aConfig['home_dir'],
+    'uri' => $aConfig['home_uri'],
+    'class_prefix' => $aConfig['class_prefix'],
+    'db_prefix' => $aConfig['db_prefix'],
+);
+
+bx_import('Template', $GLOBALS['bx_profiler_module']);
+bx_import('Config', $GLOBALS['bx_profiler_module']);
+
+class BxProfiler extends BxDol
+{
+    protected $oConfig, $oTemplate, $oTemplateAdmin;
+
+    protected $aConf = [];
+
+    protected $_iTimeStart = 0;
+
+    protected $_aQueries = [];
+    protected $_sQueryIndex = 0;
+
+    protected $_aModules = [];
+    protected $_aModulesNames = [];
+    protected $_aModulesLevel = 0;
+
+    protected $_aAlerts = [];
+    protected $_iAlertsLevel = 0;
+
+    protected $_aInjections = [];
+    protected $_sInjectionIndex = 0;
+
+    protected $_sLogDateFormat = "Y-m-d H:i:s";
+    protected $_sLogMaxArgLength = 64;
+    protected $_sLogFilename = 64;
+
+    protected $_aTemplates = [];
+    protected $_aTemplateIndexes = [];
+
+    protected $_aPages = [];
+    protected $_sPageIndex = '';
+    protected $_sPageBlockIndex = 0;
+    protected $_aPagesBlocks = [];
+    
+    protected $_aMenus = [];   
+
+    function __construct($iTimeStart)
+    {
+        parent::__construct();
+
+        $this->oConfig = new BxProfilerConfig ($GLOBALS['bx_profiler_module']);
+        $this->oTemplate = new BxProfilerTemplate ($this->oConfig);
+        $this->oTemplateAdmin = BxDolStudioTemplate::getInstance();
+
+        if (getParam ('bx_profiler_long_sql_queries_log'))
+            $this->aConf['long_query'] = getParam ('bx_profiler_long_sql_queries_time');
+        if (getParam ('bx_profiler_long_module_query_log'))
+            $this->aConf['long_module'] = getParam ('bx_profiler_long_module_query_time');
+        if (getParam ('bx_profiler_long_page_log'))
+            $this->aConf['long_page'] = getParam ('bx_profiler_long_page_time');
+
+        $this->_iTimeStart = $iTimeStart;
+    }
+
+    // output profiler debug panel
+    function output ()
+    {
+        $iPageTime = $this->_getCurrentDelay ();
+        if (isset($this->aConf['long_page']) && $iPageTime > $this->aConf['long_page'])
+            $this->logPageOpen ($iPageTime);
+
+        switch (getParam('bx_profiler_show_debug_panel')) {
+        case 'all':
+            break;
+        case 'admins':
+            if (!isset($GLOBALS['logged']) || !$GLOBALS['logged']['admin'])
+                return;
+            break;
+        case 'none':
+        default:
+            return;
+        }
+
+        $aCss = array (
+			'profiler.css',
+            BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'jush/|jush.css',
+        );
+        $aJs = array (
+            'jquery.tablesorter.min.js',
+            'profiler.js',
+            'jush/jush.js',
+        );
+
+        $sJsCss = '';
+        foreach ($aCss as $sCssPath)
+            $sJsCss .= $this->oTemplate->addCss($sCssPath, 1);
+        foreach ($aJs as $sJsPath)
+            $sJsCss .= $this->oTemplate->addJs($sJsPath, 1);
+
+        $sPlanks  = $this->_plankMain ();
+        $sPlanks .= $this->_plankMenus ();
+        $sPlanks .= $this->_plankTemplates ();
+        $sPlanks .= $this->_plankInjections ();
+        $sPlanks .= $this->_plankPagesBlocks ();
+        $sPlanks .= $this->_plankSql ();
+        $sPlanks .= $this->_plankModules ();
+        $sPlanks .= $this->_plankAlerts ();
+
+        echo ob_get_clean();
+
+        $sContentType = $this->oTemplate->getHeaderContentType();
+        if ('text/html' == $sContentType) {            
+            echo $sJsCss . $sPlanks;
+        } 
+        else if ('application/json' == $sContentType) {
+            echo $sPlanks;
+        }
+    }
+
+    function _logBegin ($s)
+    {
+        $sDate = date ($this->_sLogDateFormat);
+        return "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n" . $sDate . " " . $s . "\n" .
+        (getLoggedId() ? "User ID: " . getLoggedId()  . "\n" : '') .
+        "User role: " . ($GLOBALS['logged']['admin'] ? 'admin' : ($GLOBALS['logged']['member'] ? 'member' : 'guest')) . "\n";
+    }
+
+    function _logEnd ()
+    {
+        return "\n";
+    }
+
+    function _appendToLog ($s)
+    {
+		bx_log('bx_profiler', $s);
+    }
+
+    function logSqlQuery ($iTime, $aSqlQuery, &$oStmt)
+    {
+        $s  = $this->_logBegin ('LONG SQL QUERY: ' . $aSqlQuery['time']);
+        $s .= "Rows: " . $aSqlQuery['rows'] . "\n";
+        $s .= "Affected: " . $aSqlQuery['affected'] . "\n";
+        $s .= "Query: " . $aSqlQuery['sql'] . "\n";
+        if (getParam('bx_profiler_long_sql_queries_debug'))
+            $s .= "Backtrace: \n" . $this->_debugBackTrace (3);
+        $s .= $this->_logEnd();
+        $this->_appendToLog ($s);
+    }
+
+    function logModuleQuery ($iTime, $aModuleQuery)
+    {
+        $s  = $this->_logBegin ('LONG MODULE QUERY: ' . $aModuleQuery['time']);
+        $s .= "Module name: " . $aModuleQuery['name'] . "\n";
+        $s .= "Query type: " . $aModuleQuery['type'] . "\n";
+        $s .= "Class/file: " . $aModuleQuery['class/file'] . "\n";
+        $s .= "Method: " . $aModuleQuery['method'] . "\n";
+        if (getParam('bx_profiler_long_module_query_debug'))
+            $s .= "Backtrace: \n" . $this->_debugBackTrace (3);
+        $s .= $this->_logEnd();
+        $this->_appendToLog ($s);
+    }
+
+    function logPageOpen ($iTime)
+    {
+        $s  = $this->_logBegin ('LONG PAGE OPEN: ' . $this->_formatTime($iTime));
+        $s .= "Request method: " . $_SERVER['REQUEST_METHOD'] . "\n";
+        $s .= "Query string: " . $_SERVER['QUERY_STRING'] . "\n";
+        $s .= "Request URI: " . $_SERVER['REQUEST_URI'] . "\n";
+        $s .= "Script name: " . $_SERVER['SCRIPT_NAME'] . "\n";
+        $s .= "PHP self: " . $_SERVER['PHP_SELF'] . "\n";
+        if (getParam('bx_profiler_long_page_debug'))
+            $s .= "All server vars: \n" . print_r ($_SERVER, true);
+        $s .= $this->_logEnd();
+        $this->_appendToLog ($s);
+    }
+
+    function beginModule($sType, $sHash, &$aModule, $sClassFile, $sMethod = '' )
+    {
+        ++$this->_aModulesLevel;
+        $this->_aModulesNames[$aModule['title']] = isset($this->_aModulesNames[$aModule['title']]) ? $this->_aModulesNames[$aModule['title']] + 1 : 1;
+        $this->_aModules[$sHash] = array (
+            'name' => str_repeat("--", $this->_aModulesLevel-1) . $aModule['title'],
+            'type' => $sType,
+            'class/file' => $sClassFile,
+            'method' => $sMethod,
+            'begin' => microtime (),
+            'time' => -1,
+        );
+    }
+
+    function endModule($sType, $sHash)
+    {
+        --$this->_aModulesLevel;
+        $iTime = $this->_calcTime ($this->_aModules[$sHash]['begin']);
+        unset ($this->_aModules[$sHash]['begin']);
+        $this->_aModules[$sHash]['time'] = $this->_formatTime($iTime);
+        $this->_aModules[$sHash]['raw_time'] = $iTime;
+        if (isset($this->aConf['long_module']) && $iTime > $this->aConf['long_module'])
+            $this->logModuleQuery ($iTime, $this->_aModules[$sHash]);
+    }
+
+    function beginAlert($sUnit, $sAction, $sHandler)
+    {
+        $sHash = md5($sUnit . $sAction . $sHandler . $this->_iAlertsLevel);
+        ++$this->_iAlertsLevel;
+        $this->_aAlerts[$sHash] = array (
+            'name' => str_repeat("--", $this->_iAlertsLevel-1) . $sHandler,
+            'unit' => $sUnit,
+            'action' => $sAction,
+            'handler' => $sHandler,
+            'begin' => microtime (),
+            'time' => -1,
+        );
+    }
+
+    function endAlert($sUnit, $sAction, $sHandler)
+    {
+        --$this->_iAlertsLevel;
+        $sHash = md5($sUnit . $sAction . $sHandler . $this->_iAlertsLevel);
+        $iTime = $this->_calcTime ($this->_aAlerts[$sHash]['begin']);
+        unset ($this->_aAlerts[$sHash]['begin']);
+        $this->_aAlerts[$sHash]['time'] = $this->_formatTime($iTime);
+        $this->_aAlerts[$sHash]['raw_time'] = $iTime;
+    }
+
+    function beginInjection ($sId)
+    {
+        $this->_sInjectionIndex = $sId;
+        $this->_aInjections[$sId]['begin'] = microtime ();
+    }
+
+    function endInjection ($sId, $aInjection)
+    {
+        if (!isset($this->_aInjections[$sId]))
+            return;
+        $iTime = $this->_calcTime ($this->_aInjections[$sId]['begin']);
+        unset ($this->_aInjections[$sId]['begin']);
+        $this->_aInjections[$sId]['name'] = isset($aInjection['name']) ? $aInjection['name'] : 'no-name';
+        $this->_aInjections[$sId]['key'] = $aInjection['key'];
+        $this->_aInjections[$sId]['replace'] = $aInjection['replace'] ? 'yes' : 'no';
+        $this->_aInjections[$sId]['time'] = $this->_formatTime($iTime);
+        $this->_aInjections[$sId]['raw_time'] = $iTime;
+    }
+
+    function beginPageBlock ($sName, $iBlockId)
+    {
+        $this->_sPageBlockIndex = $iBlockId;
+        $this->_aPagesBlocks[$this->_sPageBlockIndex]['name'] = $sName;
+        $this->_aPagesBlocks[$this->_sPageBlockIndex]['begin'] = microtime ();
+    }
+
+    function endPageBlock ($iBlockId, $isEmpty, $isCached)
+    {
+        if (!$this->_sPageBlockIndex)
+            return;
+        $iTime = $this->_calcTime ($this->_aPagesBlocks[$this->_sPageBlockIndex]['begin']);
+        unset ($this->_aPagesBlocks[$this->_sPageBlockIndex]['begin']);
+        $this->_aPagesBlocks[$this->_sPageBlockIndex]['cached'] = $isCached ? 'yes' : 'no';
+        $this->_aPagesBlocks[$this->_sPageBlockIndex]['empty'] = $isEmpty ? 'yes' : 'no';
+        $this->_aPagesBlocks[$this->_sPageBlockIndex]['time'] = $this->_formatTime($iTime);
+        $this->_aPagesBlocks[$this->_sPageBlockIndex]['raw_time'] = $iTime;
+    }
+
+    function beginPage ($sName)
+    {
+        $this->_sPageIndex = md5 ($sName.time().rand());
+        $this->_aPages[$this->_sPageIndex]['name'] = $sName;
+        $this->_aPages[$this->_sPageIndex]['begin'] = microtime ();
+    }
+
+    function endPage (&$sContent)
+    {
+        if (!$this->_sPageIndex || !isset($this->_aPages[$this->_sPageIndex]['begin']))
+            return;
+        $iTime = $this->_calcTime ($this->_aPages[$this->_sPageIndex]['begin']);
+        unset ($this->_aPages[$this->_sPageIndex]['begin']);
+        $this->_aPages[$this->_sPageIndex]['time'] = $this->_formatTime($iTime);
+        $this->_aPages[$this->_sPageIndex]['raw_time'] = $iTime;
+    }
+
+    function beginTemplate ($sName, $sRand)
+    {
+        $this->_aTemplateIndexes[$sName.$sRand] = 1;
+        $this->_aTemplates[$sName.$sRand]['name'] = $sName;
+        $this->_aTemplates[$sName.$sRand]['begin'] = microtime ();
+    }
+
+    function endTemplate ($sName, $sRand, &$sContent, $isCached)
+    {
+        if (!isset($this->_aTemplateIndexes[$sName.$sRand]))
+            return;
+        $iTime = $this->_calcTime ($this->_aTemplates[$sName.$sRand]['begin']);
+        unset ($this->_aTemplates[$sName.$sRand]['begin']);
+        $this->_aTemplates[$sName.$sRand]['cached'] = $isCached ? 'yes' : 'no';
+        $this->_aTemplates[$sName.$sRand]['time'] = $this->_formatTime($iTime);
+        $this->_aTemplates[$sName.$sRand]['raw_time'] = $iTime;
+    }
+
+    function beginQuery ($sSql)
+    {
+        $this->_sQueryIndex = md5 ($sSql.time().rand());
+        $this->_aQueries[$this->_sQueryIndex]['sql'] = $sSql;
+        $this->_aQueries[$this->_sQueryIndex]['begin'] = microtime ();
+    }
+
+    function endQuery (&$oStmt)
+    {
+        if (!$this->_sQueryIndex)
+            return;
+        $iTime = $this->_calcTime ($this->_aQueries[$this->_sQueryIndex]['begin']);
+        unset ($this->_aQueries[$this->_sQueryIndex]['begin']);
+        $this->_aQueries[$this->_sQueryIndex]['time'] = $this->_formatTime($iTime);
+        $this->_aQueries[$this->_sQueryIndex]['raw_time'] = $iTime;
+        $this->_aQueries[$this->_sQueryIndex]['rows'] = $oStmt ? BxDolDb::getInstance()->getNumRows($oStmt) : '';
+        $this->_aQueries[$this->_sQueryIndex]['affected'] = $oStmt ? BxDolDb::getInstance()->getAffectedRows($oStmt) : '';
+        if (isset($this->aConf['long_query']) && $iTime > $this->aConf['long_query'])
+            $this->logSqlQuery ($iTime, $this->_aQueries[$this->_sQueryIndex], $oStmt);
+    }
+
+    function beginMenu ($sName)
+    {
+        $this->_aMenus[$sName]['name'] = $sName;
+        $this->_aMenus[$sName]['begin'] = microtime ();
+    }
+
+    function endMenu ($sName)
+    {
+        if (!isset($this->_aMenus[$sName]))
+            return;
+        $iTime = $this->_calcTime ($this->_aMenus[$sName]['begin']);
+        unset ($this->_aMenus[$sName]['begin']);
+        $this->_aMenus[$sName]['time'] = $this->_formatTime($iTime);
+        $this->_aMenus[$sName]['raw_time'] = $iTime;
+    }
+
+    function _getCurrentDelay ()
+    {
+        $i1 = explode(' ', microtime ());
+        $i2 = explode(' ', $this->_iTimeStart);
+        return ($i1[0]+$i1[1]) - ($i2[0]+$i2[1]);
+    }
+
+    function _plankMain ()
+    {
+        $sTime = $this->_formatTime($this->_getCurrentDelay (), 3);
+        if (function_exists('memory_get_usage'))
+            $sMemory = $this->_formatBytes(memory_get_usage(true)) . ' of ' . ini_get('memory_limit') . ' allowed';
+
+        return $this->oTemplate->plank(
+            $this->oTemplate->nameValue('Time:', $sTime) .
+            (function_exists('memory_get_usage') ? $this->oTemplate->nameValue('Memory:', $sMemory) : '') .
+            $this->oTemplate->nameValue('PHP:', phpversion()) .
+            $this->oTemplate->nameValue('SAPI:', php_sapi_name()) .
+            $this->oTemplate->nameValue('OS:', php_uname('s r m'))
+        );
+    }
+
+    function _plankTemplates ()
+    {
+        if (empty($GLOBALS['bx_profiler']->_aPages) && empty($GLOBALS['bx_profiler']->_aTemplates))
+            return;
+
+        $sPages = '';
+        if (!empty($GLOBALS['bx_profiler']->_aPages)) {
+            $iTimePages = 0;
+            foreach ($GLOBALS['bx_profiler']->_aPages as $k => $r) {
+                if (!isset($r['raw_time']))
+                    continue;
+                $iTimePages += $r['raw_time'];
+                unset ($GLOBALS['bx_profiler']->_aPages[$k]['raw_time']);
+            }
+            $sPages = count($GLOBALS['bx_profiler']->_aPages) . ' (' . $this->_formatTime($iTimePages, 3) . ')';
+        }
+
+        $sTemplatesCached = '';
+        $sTemplatesNotCached = '';
+        if ($GLOBALS['bx_profiler']->_aTemplates) {
+            $iTimeTemplatesCached = 0;
+            $iTimeTemplatesNotCached = 0;
+            $sTemplatesCached = 0;
+            $sTemplatesNotCached = 0;
+            foreach ($GLOBALS['bx_profiler']->_aTemplates as $k => $r) {
+                if (!isset($r['raw_time']))
+                    continue;
+                if ('yes' == $r['cached']) {
+                    $iTimeTemplatesCached += $r['raw_time'];
+                    ++$sTemplatesCached;
+                } else {
+                    $iTimeTemplatesNotCached += $r['raw_time'];
+                    ++$sTemplatesNotCached;
+                }
+                unset ($GLOBALS['bx_profiler']->_aTemplates[$k]['raw_time']);
+            }
+            if ($sTemplatesCached)
+                $sTemplatesCached .= ' (' . $this->_formatTime($iTimeTemplatesCached, 3) . ')';
+            if ($sTemplatesNotCached)
+                $sTemplatesNotCached .= ' (' . $this->_formatTime($iTimeTemplatesNotCached, 3) . ')';
+        }
+
+        return $this->oTemplate->plank(
+            ($sPages ? $this->oTemplate->nameValue('Pages:', $sPages) : '') .
+            ($sTemplatesCached ? $this->oTemplate->nameValue('Templates Cached:', $sTemplatesCached) : '') .
+            ($sTemplatesNotCached ? $this->oTemplate->nameValue('Templates Not Cached:', $sTemplatesNotCached) : ''),
+            $this->oTemplate->table($GLOBALS['bx_profiler']->_aTemplates)
+        );
+    }
+
+    function _plankInjections ()
+    {
+        if (!$GLOBALS['bx_profiler']->_aInjections)
+            return;
+
+        $iTimeInjections = 0;
+        foreach ($GLOBALS['bx_profiler']->_aInjections as $k => $r) {
+            if (!isset($r['raw_time']))
+                continue;
+            $iTimeInjections += $r['raw_time'];
+            unset ($GLOBALS['bx_profiler']->_aInjections[$k]['raw_time']);
+        }
+
+        $sInjections = count($GLOBALS['bx_profiler']->_aInjections) . ' injection (' . $this->_formatTime($iTimeInjections, 3) . ')';
+
+        return $this->oTemplate->plank(
+            $this->oTemplate->nameValue('Injections:', $sInjections),
+            $this->oTemplate->table($GLOBALS['bx_profiler']->_aInjections)
+        );
+    }
+
+    function _plankPagesBlocks ()
+    {
+        if (empty($GLOBALS['bx_profiler']->_aPagesBlocks))
+            return;
+
+        $iTimeBlocks = 0;
+        $iTimeBlocksCached = 0;
+        $iTimeBlocksNotCached = 0;
+        $iTimeBlocksEmpty = 0;
+        $iTimeBlocksNotEmpty = 0;
+
+        $iCountBlocksCached = 0;
+        $iCountBlocksNotCached = 0;
+        $iCountBlocksEmpty = 0;
+        $iCountBlocksNotEmpty = 0;
+
+        foreach ($GLOBALS['bx_profiler']->_aPagesBlocks as $k => $r) {
+            if (!isset($r['raw_time']))
+                continue;
+            $iTimeBlocks += $r['raw_time'];
+            if ($r['cached'] == 'yes') {
+                $iTimeBlocksCached += $r['raw_time'];
+                ++$iCountBlocksCached;
+            } else {
+                $iTimeBlocksNotCached += $r['raw_time'];
+                ++$iCountBlocksNotCached;
+            }
+            if ($r['empty'] == 'yes') {
+                $iTimeBlocksEmpty += $r['raw_time'];
+                ++$iCountBlocksEmpty;
+            } else {
+                $iTimeBlocksNotEmpty += $r['raw_time'];
+                ++$iCountBlocksNotEmpty;
+            }
+            unset ($GLOBALS['bx_profiler']->_aPagesBlocks[$k]['raw_time']);
+        }
+
+        $sBlocks = count($GLOBALS['bx_profiler']->_aPagesBlocks) . ' (' . $this->_formatTime($iTimeBlocks, 3) . ')';
+        if ($iCountBlocksCached)
+            $sBlocksCached = $iCountBlocksCached . ' (' . $this->_formatTime($iTimeBlocksCached, 3) . ')';
+        if ($iCountBlocksNotCached)
+            $sBlocksNotCached = $iCountBlocksNotCached . ' (' . $this->_formatTime($iTimeBlocksNotCached, 3) . ')';
+        if ($iCountBlocksEmpty)
+            $sBlocksEmpty = $iCountBlocksEmpty . ' (' . $this->_formatTime($iTimeBlocksEmpty, 3) . ')';
+        if ($iCountBlocksNotEmpty)
+            $sBlocksNotEmpty = $iCountBlocksNotEmpty . ' (' . $this->_formatTime($iTimeBlocksNotEmpty, 3) . ')';
+
+        return $this->oTemplate->plank(
+            $this->oTemplate->nameValue('Pages Blocks:', $sBlocks) .
+            ($iCountBlocksCached ? $this->oTemplate->nameValue('Cached:', $sBlocksCached) : '') .
+            ($iCountBlocksNotCached ? $this->oTemplate->nameValue('Not Cached:', $sBlocksNotCached) : '') .
+            ($iCountBlocksEmpty ? $this->oTemplate->nameValue('Empty:', $sBlocksEmpty) : '') .
+            ($iCountBlocksNotEmpty ? $this->oTemplate->nameValue('Not Empty:', $sBlocksNotEmpty) : ''),
+            $this->oTemplate->table($GLOBALS['bx_profiler']->_aPagesBlocks)
+        );
+    }
+
+    function _plankMenus ()
+    {
+        if (empty($GLOBALS['bx_profiler']->_aMenus))
+            return;
+
+        $iTimeMenus = 0;
+        foreach ($GLOBALS['bx_profiler']->_aMenus as $k => $r) {
+            if (!isset($r['raw_time']))
+                continue;
+            $iTimeMenus += $r['raw_time'];
+            unset ($GLOBALS['bx_profiler']->_aMenus[$k]['raw_time']);
+        }
+
+        $sMenus = count($GLOBALS['bx_profiler']->_aMenus) . ' menus (' . $this->_formatTime($iTimeMenus, 3) . ')';
+
+        return $this->oTemplate->plank(
+            $this->oTemplate->nameValue('Menus:', $sMenus),
+            $this->oTemplate->table($GLOBALS['bx_profiler']->_aMenus)
+        );
+    }
+
+    function _plankSql ()
+    {
+        if (empty($GLOBALS['bx_profiler']->_aQueries))
+            return;
+
+        $iTimeQueries = 0;
+        foreach ($GLOBALS['bx_profiler']->_aQueries as $k => $r) {
+            if (!isset($r['raw_time']))
+                continue;
+            $iTimeQueries += $r['raw_time'];
+            unset ($GLOBALS['bx_profiler']->_aQueries[$k]['raw_time']);
+        }
+
+        $sQueries = count($GLOBALS['bx_profiler']->_aQueries) . ' queries (' . $this->_formatTime($iTimeQueries, 3) . ')';
+
+        return $this->oTemplate->plank(
+            $this->oTemplate->nameValue('SQL:', $sQueries),
+            $this->oTemplate->table($GLOBALS['bx_profiler']->_aQueries, 'sql')
+        );
+    }
+
+    function _plankModules ()
+    {
+        if (empty($GLOBALS['bx_profiler']->_aModules))
+            return;
+
+        $iTimeModules = 0;
+        foreach ($GLOBALS['bx_profiler']->_aModules as $k => $r) {
+            if (!isset($r['raw_time']))
+                continue;
+            $iTimeModules += $r['raw_time'];
+            unset ($GLOBALS['bx_profiler']->_aModules[$k]['raw_time']);
+        }
+
+        $sModules = count($GLOBALS['bx_profiler']->_aModulesNames) . ' modules loaded';
+        $sModulesQueries = count($GLOBALS['bx_profiler']->_aModules) . ' modules queries (' . $this->_formatTime($iTimeModules, 3) . ')';
+
+        return $this->oTemplate->plank(
+            $this->oTemplate->nameValue('Modules:', $sModules) .
+            $this->oTemplate->nameValue('Modules Queries:', $sModulesQueries),
+            $this->oTemplate->table($GLOBALS['bx_profiler']->_aModules)
+        );
+    }
+
+    function _plankAlerts ()
+    {
+        if (empty($GLOBALS['bx_profiler']->_aAlerts))
+            return;
+
+        $iTimeAlerts = 0;
+        foreach ($GLOBALS['bx_profiler']->_aAlerts as $k => $r) {
+            if (!isset($r['raw_time']))
+                continue;
+            $iTimeAlerts += $r['raw_time'];
+            unset ($GLOBALS['bx_profiler']->_aAlerts[$k]['raw_time']);
+        }
+
+        $sAlerts = count($GLOBALS['bx_profiler']->_aAlerts) . ' alerts responses (' . $this->_formatTime($iTimeAlerts, 3) . ')';
+
+        return $this->oTemplate->plank(
+            $this->oTemplate->nameValue('Alerts:', $sAlerts),
+            $this->oTemplate->table($GLOBALS['bx_profiler']->_aAlerts)
+        );
+    }
+
+    function _formatTime ($i, $iPrecision = 4)
+    {
+        return round($i, $iPrecision) . ' sec';
+    }
+
+    function _formatBytes ($i)
+    {
+        if ($i > 1024*1024)
+            return round($i/1024/1024, 1) . 'M';
+        elseif ($i > 1024)
+            return round($i/1024, 1) . 'K';
+        else
+            return $i . 'B';
+    }
+
+    function _isProfilerDisabled()
+    {
+        if (isset($GLOBALS['bx_profiler_disable']) || isset($_GET['bx_profiler_disable']))
+            return true;
+
+        return false;
+    }
+
+    function _calcTime ($begin)
+    {
+        $i1 = explode(' ', microtime ());
+        $i2 = explode(' ', $begin);
+        return ($i1[0]+$i1[1]) - ($i2[0]+$i2[1]);
+    }
+
+    function _debugPrintArray ($mixed)
+    {
+        $sArgs .= 'Array(';
+        foreach ($mixed as $mixed2)
+            $sArgs .= (is_object($mixed2) ? $this->_debugPrintObject($mixed2) : $this->_debugPrintAny($mixed2)) . ',';
+        $sArgs = substr($sArgs, 0, -1);
+        $sArgs .= ')';
+        return $sArgs;
+    }
+
+    function _debugPrintObject ($mixed)
+    {
+        return get_class($mixed) . ' instance';
+    }
+
+    function _debugPrintAny ($mixed)
+    {
+        if (is_string)
+            return "'" . (strlen($mixed) > $this->_sLogMaxArgLength ? substr($mixed, 0, $this->_sLogMaxArgLength) . '...' : $mixed) . "'";
+        else
+            return $mixed;
+    }
+
+    function _debugBackTrace ($iShifts = 0)
+    {
+        $a = debug_backtrace();
+        while (--$iShifts > -1)
+            array_shift($a);
+
+        $s = '';
+        foreach ($a as $r) {
+
+            $sArgs = '';
+            foreach ($r['args'] as $mixed) {
+                switch (true) {
+                case is_array($mixed):
+                    $sArgs .= $this->_debugPrintArray($mixed);
+                    break;
+                case is_object($mixed):
+                    $sArgs .= $this->_debugPrintObject($mixed);
+                    break;
+                default:
+                    $sArgs .= $this->_debugPrintAny($mixed);
+                }
+                $sArgs .= ',';
+            }
+            $sArgs = substr($sArgs, 0, -1);
+
+            $s .= "--------------------------------------\n";
+            $s .= "{$r['line']} {$r['file']}\n";
+            $s .= "{$r['class']}{$r['type']}{$r['function']} ({$sArgs});\n";
+        }
+        return $s;
+    }
+}
+
+$GLOBALS['bx_profiler'] = new BxProfiler(BX_DOL_START);
+if (!$GLOBALS['bx_profiler']->_isProfilerDisabled())
+    register_shutdown_function (array ($GLOBALS['bx_profiler'], 'output'));
+
+/** @} */

+ 68 - 0
modules/boonex/profiler/updates/13.0.3_13.0.4/source/install/config.php

@@ -0,0 +1,68 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Profiler Profiler
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_profiler',
+    'title' => 'Profiler',
+    'note' => 'Measure different site timings.',
+    'version' => '13.0.4',
+    'vendor' => 'Boonex',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/profiler/',
+    'home_uri' => 'profiler',
+
+    'db_prefix' => 'bx_profiler_',
+    'class_prefix' => 'BxProfiler',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Boonex Profiler',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 51 - 0
modules/boonex/tasks/updates/13.0.7_13.0.8/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' => 'Tasks',
+    'version_from' => '13.0.7',
+    'version_to' => '13.0.8',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/tasks/updates/update_13.0.7_13.0.8/',
+    'home_uri' => 'tasks_update_1307_1308',
+
+    'module_dir' => 'boonex/tasks/',
+    'module_uri' => 'tasks',
+
+    'db_prefix' => 'bx_tasks_',
+    'class_prefix' => 'BxTasks',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Tasks',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/tasks/updates/13.0.7_13.0.8/install/installer.php

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

+ 22 - 0
modules/boonex/tasks/updates/13.0.7_13.0.8/source/classes/BxTasksFormListEntry.php

@@ -0,0 +1,22 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT 
+ * @defgroup    Tasks Tasks
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Create/Edit list entry form
+ */
+class BxTasksFormListEntry extends BxTemplFormView
+{
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        parent::__construct($aInfo, $oTemplate);
+    }
+}
+
+/** @} */

+ 149 - 0
modules/boonex/tasks/updates/13.0.7_13.0.8/source/install/config.php

@@ -0,0 +1,149 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT 
+ * @defgroup    Tasks Tasks
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_tasks',
+    'title' => 'Tasks',
+    'note' => 'Tasks module.',
+    'version' => '13.0.8',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/tasks/',
+    'home_uri' => 'tasks',
+
+    'db_prefix' => 'bx_tasks_',
+    'class_prefix' => 'BxTasks',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Tasks',
+
+    /**
+     * 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_tasks_covers',
+    	'bx_tasks_photos',
+        'bx_tasks_videos',
+        'bx_tasks_files'
+    ),
+
+    /**
+     * Transcoders.
+     */
+    'transcoders' => array(
+        'bx_tasks_preview',
+        'bx_tasks_gallery',
+        'bx_tasks_cover',
+
+        'bx_tasks_preview_photos',
+        'bx_tasks_gallery_photos', 
+
+        'bx_tasks_videos_poster',
+        'bx_tasks_videos_poster_preview',
+        'bx_tasks_videos_mp4',
+        'bx_tasks_videos_mp4_hd',
+
+        'bx_tasks_preview_files',
+        'bx_tasks_gallery_files'
+    ),
+
+    /**
+     * Extended Search Forms.
+     */
+    'esearches' => array(
+        'bx_tasks',
+    	'bx_tasks_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/timeline/updates/13.0.14_13.0.15/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' => 'Timeline',
+    'version_from' => '13.0.14',
+    'version_to' => '13.0.15',
+    'vendor' => 'BoonEx',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/timeline/updates/update_13.0.14_13.0.15/',
+    'home_uri' => 'timeline_update_13014_13015',
+
+    'module_dir' => 'boonex/timeline/',
+    'module_uri' => 'timeline',
+
+    'db_prefix' => 'bx_timeline_',
+    'class_prefix' => 'BxTimeline',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 0,
+        'update_files' => 1,
+        'update_languages' => 0,
+        'clear_db_cache' => 0,
+    ),
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Timeline',
+
+    /**
+     * Files Section
+     */
+    'delete_files' => array(),
+);

+ 13 - 0
modules/boonex/timeline/updates/13.0.14_13.0.15/install/installer.php

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

+ 983 - 0
modules/boonex/timeline/updates/13.0.14_13.0.15/source/classes/BxTimelineConfig.php

@@ -0,0 +1,983 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Timeline Timeline
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+bx_import('BxDolPrivacy');
+
+class BxTimelineConfig extends BxBaseModNotificationsConfig
+{
+    protected $_aTypeToFormDisplay;
+
+    protected $_bAllowEdit;
+    protected $_bAllowDelete;
+    protected $_bShowAll;
+    protected $_bCountAllViews;
+    protected $_bRepostOwnActions;
+    protected $_bHideUponDelete;
+
+    protected $_bCacheItem;
+    protected $_sCacheItemEngine;
+    protected $_iCacheItemLifetime;
+    protected $_bCacheTable;
+    protected $_iCacheTableInterval;
+    protected $_aCacheTableCheckFields;
+
+    protected $_bInfScroll;
+    protected $_iInfScrollPerPreload;
+    protected $_iInfScrollAutoPreloads;
+
+    protected $_iRssLength;
+    protected $_iLiveUpdateLength;
+    protected $_iCharsDisplayMinTitle;
+    protected $_iCharsDisplayMaxTitle;
+
+    protected $_bBriefCards;
+    protected $_aBriefCardsTags;
+    protected $_sVideosPreload;
+    protected $_sVideosAutoplay;
+    protected $_iPreloadComments;
+    protected $_iPreloadCommentsMax;
+    protected $_bJumpTo;
+    protected $_bSortByReaction;
+    protected $_bSortByUnread;
+    protected $_sAttachmentsLayout;
+
+    protected $_bHot;
+    protected $_iHotInterval;
+    protected $_iHotThresholdContent;
+    protected $_iHotThresholdComment;
+    protected $_iHotThresholdVote;
+    protected $_aHotSources;
+    protected $_aHotSourcesList;
+    protected $_aHotList;
+
+    protected $_bEditorToolbar;
+    protected $_bEditorAutoAttach;
+    protected $_bEnableMediaPriority;
+    protected $_iLimitAttachLinks;
+
+    protected $_bUnhideRestored;
+
+    protected $_sStorageObject;
+    protected $_sTranscoderObjectPreview;
+    protected $_sTranscoderObjectView;
+    protected $_aPhotoUploaders;
+    protected $_aVideoUploaders;
+    protected $_aFilesUploaders;
+    protected $_aItemToUploader;
+
+    protected $_sMenuItemManage;
+    protected $_sMenuItemActions;
+
+    protected $_aRepostDefaults;
+
+    protected $_iTimelineVisibilityThreshold;
+    protected $_aPregPatterns;
+
+    protected $_sSessionKeyType;
+
+    /**
+     * Constructor
+     */
+    public function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->CNF = array (
+            'ICON' => 'far clock col-green1',
+
+            // database tables
+            'TABLE_ENTRIES' => $aModule['db_prefix'] . 'events',
+
+            // database fields
+            'FIELD_ID' => 'id',
+            'FIELD_AUTHOR' => 'object_owner_id',
+            'FIELD_OWNER_ID' => 'owner_id',
+            'FIELD_SYSTEM' => 'system',
+            'FIELD_OBJECT_ID' => 'object_id', //Note. For 'Direct Timeline Posts' ('system' db field == 0) this field contains post's author profile ID.
+            'FIELD_OBJECT_OWNER_ID' => 'object_owner_id',
+            'FIELD_OBJECT_PRIVACY_VIEW' => 'object_privacy_view',
+            'FIELD_CF' => 'object_cf',
+            'FIELD_SOURCE' => 'source',
+            'FIELD_ADDED' => 'date',
+            'FIELD_PUBLISHED' => 'published',
+            'FIELD_TITLE' => 'title',
+            'FIELD_TEXT' => 'description',
+            'FIELD_ATTACHMENTS' => 'attachments',
+            'FIELD_LINK' => 'link',
+            'FIELD_PHOTO' => 'photo',
+            'FIELD_VIDEO' => 'video',
+            'FIELD_FILE' => 'file',
+            'FIELD_STATUS' => 'status',
+            'FIELD_STATUS_ADMIN' => 'status_admin',
+            'FIELD_STICKED' => 'sticked',
+            'FIELD_DATE' => 'date',
+            'FIELD_REACTED' => 'reacted',
+            'FIELD_ANONYMOUS' => 'anonymous',
+            'FIELD_CONTROLS' => 'controls',
+            'FIELD_LOCATION' => 'location',
+            'FIELD_LOCATION_PREFIX' => 'location',
+            'FIELD_LABELS' => 'labels',
+            'FIELDS_DELAYED_PROCESSING' => 'video', // can be array of fields or comma separated string of field names
+
+            // page URIs
+            'URI_VIEW_LIST' => 'timeline-view',
+            'URI_VIEW_ENTRY' => 'item',
+
+            'URL_HOME' => 'page.php?i=timeline-view-home',
+
+            // some params
+            'PARAM_AUTO_APPROVE' => $this->_sName . '_enable_auto_approve',
+
+            // objects
+            'OBJECT_STORAGE' => $this->_sName . '_photos',
+            'OBJECT_STORAGE_VIDEOS' => $this->_sName . '_videos',
+            'OBJECT_STORAGE_FILES' => $this->_sName . '_files',
+            'OBJECT_IMAGES_TRANSCODER_PREVIEW' => $this->_sName . '_photos_preview',
+            'OBJECT_VIDEOS_TRANSCODER_PREVIEW' => $this->_sName . '_proxy_preview',
+            'OBJECT_VIDEOS_TRANSCODERS' => array(
+                'poster' => 'bx_timeline_videos_poster_view', 
+                'poster_preview' => 'bx_timeline_videos_poster_preview', 
+            	'mp4' => 'bx_timeline_videos_mp4', 
+            	'mp4_hd' => 'bx_timeline_videos_mp4_hd'
+            ),
+            'OBJECT_UPLOADER_PHOTO_SIMPLE' => $this->_sName . '_simple_photo',
+            'OBJECT_UPLOADER_PHOTO_HTML5' => $this->_sName . '_html5_photo',
+            'OBJECT_UPLOADER_VIDEO_SIMPLE' => $this->_sName . '_simple_video',
+            'OBJECT_UPLOADER_VIDEO_HTML5' => $this->_sName . '_html5_video',
+            'OBJECT_UPLOADER_VIDEO_RECORD' => $this->_sName . '_record_video',
+            'OBJECT_UPLOADER_FILE_HTML5' => $this->_sName . '_html5_file',
+            'OBJECT_UPLOADER_FILE_SIMPLE' => $this->_sName . '_simple_file',
+            'OBJECT_GRID_ADMINISTRATION' => $this->_sName . '_administration',
+            'OBJECT_GRID_COMMON' => $this->_sName . '_common',
+            'OBJECT_GRID_MUTE' => $this->_sName . '_mute',
+            'OBJECT_MENU_ENTRY_ATTACHMENTS' => $this->_sName . '_menu_post_attachments',
+            'OBJECT_METATAGS' => $this->_sName,
+            'OBJECT_COMMENTS' => $this->_sName,
+            'OBJECT_NOTES' => $this->_sName . '_notes',
+            'OBJECT_VIEWS' => $this->_sName,
+            'OBJECT_VOTES' => $this->_sName,
+            'OBJECT_REACTIONS' => $this->_sName . '_reactions',
+            'OBJECT_SCORES' => $this->_sName,
+            'OBJECT_REPORTS' => $this->_sName,
+            'OBJECT_PRIVACY_VIEW' => $this->_sName . '_privacy_view',
+            'OBJECT_CONNECTIONS_MUTE' => $this->_sName . '_mute',
+
+            // some language keys
+            'T' => array (
+                'txt_status_deleted' => '_bx_timeline_txt_status_deleted',
+                'txt_sample_single' => '_bx_timeline_txt_sample',
+                'txt_sample_single_ext' => '_bx_timeline_txt_sample_ext',
+            	'txt_sample_comment_single' => '_bx_timeline_txt_sample_comment_single',
+                'txt_sample_vote_single' => '_bx_timeline_txt_sample_vote_single',
+                'txt_sample_reaction_single' => '_bx_timeline_txt_sample_reaction_single',
+                'txt_sample_score_up_single' => '_bx_timeline_txt_sample_score_up_single',
+                'txt_sample_score_down_single' => '_bx_timeline_txt_sample_score_down_single',
+                'txt_sample_with_link' => '_bx_timeline_txt_sample_with_link',
+                'txt_sample_with_image' => '_bx_timeline_txt_sample_with_image',
+                'txt_sample_with_video' => '_bx_timeline_txt_sample_with_video',
+                'txt_sample_with_file' => '_bx_timeline_txt_sample_with_file',
+                'txt_sample_with_media' => '_bx_timeline_txt_sample_with_media',
+                'grid_action_err_delete' => '_bx_timeline_grid_action_err_delete', 
+                'grid_txt_account_manager' => '_bx_timeline_grid_txt_account_manager',
+                'filter_item_active' => '_bx_timeline_grid_filter_item_title_adm_active',
+            	'filter_item_hidden' => '_bx_timeline_grid_filter_item_title_adm_hidden',
+                'filter_item_pending' => '_bx_timeline_grid_filter_item_title_adm_pending',
+            	'filter_item_select_one_filter1' => '_bx_timeline_grid_filter_item_title_adm_select_one_filter1',
+                'form_input_title_object_privacy_view' => '_bx_timeline_form_post_input_object_privacy_view',
+                'option_vp_auto' => '_bx_timeline_option_videos_preload_auto',
+                'option_vp_metadata' => '_bx_timeline_option_videos_preload_metadata',
+                'option_vp_none' => '_bx_timeline_option_videos_preload_none',
+                'option_vap_off' => '_bx_timeline_option_videos_autoplay_off',
+                'option_vap_on_mute' => '_bx_timeline_option_videos_autoplay_on_mute',
+                'option_vap_on' => '_bx_timeline_option_videos_autoplay_on',
+                'option_al_gallery' => '_bx_timeline_option_attachments_layout_gallery',
+                'option_al_showcase' => '_bx_timeline_option_attachments_layout_showcase',
+                'option_hs_content' => '_bx_timeline_option_hot_sources_content',
+                'option_hs_comment' => '_bx_timeline_option_hot_sources_comment',
+                'option_hs_vote' => '_bx_timeline_option_hot_sources_vote',
+            ),
+        );
+
+        $this->_aTypeToFormDisplay = array(
+            BX_BASE_MOD_NTFS_TYPE_OWNER => 'form_display_post_add_profile',
+            BX_BASE_MOD_NTFS_TYPE_PUBLIC => 'form_display_post_add_public',
+            BX_TIMELINE_TYPE_CHANNELS => 'form_display_post_add',
+            BX_TIMELINE_TYPE_FEED => 'form_display_post_add',
+            BX_TIMELINE_TYPE_OWNER_AND_CONNECTIONS => 'form_display_post_add'
+        );
+
+        $this->_aPrefixes = array(
+            'style' => 'bx-tl',
+            'language' => '_bx_timeline',
+            'option' => 'bx_timeline_',
+            'common_post' => 'timeline_common_',
+            'cache_list_hot' => 'bx_timeline_list_hot',
+            'cache_item' => 'bx_timeline_item_'
+        );
+
+        $this->_aObjects = array_merge($this->_aObjects, array(
+            'comment' => $this->CNF['OBJECT_COMMENTS'],
+            'view' => $this->CNF['OBJECT_VIEWS'],
+            'vote' => $this->CNF['OBJECT_VOTES'],
+            'reaction' => $this->CNF['OBJECT_REACTIONS'],
+            'score' => $this->CNF['OBJECT_SCORES'],
+            'report' => $this->CNF['OBJECT_REPORTS'],
+            'privacy_view' => $this->CNF['OBJECT_PRIVACY_VIEW'],
+            'metatags' => $this->_sName,
+
+            'storage_photos' => $this->CNF['OBJECT_STORAGE'],
+            'storage_videos' => $this->CNF['OBJECT_STORAGE_VIDEOS'],
+            'storage_files' => $this->CNF['OBJECT_STORAGE_FILES'],
+            'transcoder_photos_preview' => $this->CNF['OBJECT_IMAGES_TRANSCODER_PREVIEW'],
+            'transcoder_photos_view' => $this->_sName . '_photos_view',
+            'transcoder_photos_medium' => $this->_sName . '_photos_medium',
+            'transcoder_photos_big' => $this->_sName . '_photos_big',
+            'transcoder_videos_preview' => $this->CNF['OBJECT_VIDEOS_TRANSCODER_PREVIEW'],
+            'transcoder_videos_poster' => $this->CNF['OBJECT_VIDEOS_TRANSCODERS']['poster'],
+            'transcoder_videos_mp4' => $this->CNF['OBJECT_VIDEOS_TRANSCODERS']['mp4'],
+            'transcoder_videos_mp4_hd' => $this->CNF['OBJECT_VIDEOS_TRANSCODERS']['mp4_hd'],
+            'transcoder_videos_photo_view' => $this->_sName . '_videos_photo_view',
+            'transcoder_videos_photo_big' => $this->_sName . '_videos_photo_big',
+
+            'page_item_brief' => $this->_sName . '_item_brief',
+
+            'menu_view' => $this->_sName . '_menu_view',
+            'menu_feeds' => $this->_sName . '_menu_feeds',
+            'menu_set_feeds' => $this->_sName . '_menu_feeds',
+            'menu_item_manage' => $this->_sName . '_menu_item_manage',
+            'menu_item_actions' => $this->_sName . '_menu_item_actions',
+            'menu_item_actions_all' => $this->_sName . '_menu_item_actions_all',
+            'menu_item_counters' => $this->_sName . '_menu_item_counters',
+            'menu_item_meta' => $this->_sName . '_menu_item_meta',
+            'menu_post_attachments' => $this->CNF['OBJECT_MENU_ENTRY_ATTACHMENTS'],
+
+            'form_post' => $this->_sName . '_post',
+            'form_attach_link' => $this->_sName . '_attach_link',
+            'form_repost' => $this->_sName . '_repost',
+            'form_display_post_add' => $this->_sName . '_post_add',
+            'form_display_post_add_public' => $this->_sName . '_post_add_public',
+            'form_display_post_add_profile' => $this->_sName . '_post_add_profile',
+            'form_display_post_edit' => $this->_sName . '_post_edit',
+            'form_display_post_view' => $this->_sName . '_post_view',
+            'form_display_attach_link_add' => $this->_sName . '_attach_link_add',
+            'form_display_repost_with' => $this->_sName . '_repost_with',
+            'form_display_repost_to' => $this->_sName . '_repost_to',
+
+            'grid_mute' => $this->CNF['OBJECT_GRID_MUTE'],
+
+            'connection_mute' => $this->CNF['OBJECT_CONNECTIONS_MUTE'],
+        ));
+
+        $this->_aHandlerDescriptor = array('module_name' => '', 'module_method' => '', 'module_class' => '', 'groupable' => '', 'group_by' => '');
+        $this->_sHandlersMethod = 'get_timeline_data';
+
+        $this->_aPhotoUploaders = array($this->CNF['OBJECT_UPLOADER_PHOTO_SIMPLE']);
+        $this->_aVideoUploaders = array($this->CNF['OBJECT_UPLOADER_VIDEO_SIMPLE']);
+        $this->_aFilesUploaders = array($this->CNF['OBJECT_UPLOADER_FILE_SIMPLE']);
+
+        $this->_aItemToUploader = array(
+            'add-photo-simple' => $this->CNF['OBJECT_UPLOADER_PHOTO_SIMPLE'],
+            'add-photo-html5' => $this->CNF['OBJECT_UPLOADER_PHOTO_HTML5'],
+            'add-video-simple' => $this->CNF['OBJECT_UPLOADER_VIDEO_SIMPLE'],
+            'add-video-html5' => $this->CNF['OBJECT_UPLOADER_VIDEO_HTML5'],
+            'add-video-record' => $this->CNF['OBJECT_UPLOADER_VIDEO_RECORD'],
+            'add-file-simple' => $this->CNF['OBJECT_UPLOADER_FILE_SIMPLE'],
+            'add-file-html5' => $this->CNF['OBJECT_UPLOADER_FILE_HTML5'],
+        );
+
+        $this->_aJsClasses = array(
+            'main' => 'BxTimelineMain',
+            'view' => 'BxTimelineView',
+            'post' => 'BxTimelinePost',
+            'repost' => 'BxTimelineRepost',
+            'manage_tools' => 'BxTimelineManageTools'
+        );
+        $this->_aJsObjects = array(
+            'view' => 'oTimelineView',
+            'post' => 'oTimelinePost',
+            'repost' => 'oTimelineRepost',
+            'manage_tools' => 'oBxTimelineManageTools'
+        );
+
+        $this->_aGridObjects = array(
+            'administration' => $this->CNF['OBJECT_GRID_ADMINISTRATION'],
+            'common' => $this->CNF['OBJECT_GRID_COMMON']
+        );
+
+        $sHp = str_replace('_', '-', $this->_sName);
+        $sHpT = BX_TIMELINE_VIEW_TIMELINE;
+        $sHpO = BX_TIMELINE_VIEW_OUTLINE;
+        $this->_aHtmlIds = array(
+            'view' => array(
+                'edit_form' => $sHp . '-edit-',
+                'attach_link_form_field' => $sHp . '-attach-link-form-field-',
+
+                'menu_popup' => $sHp . '-menu-popup-',
+
+                'video_iframe' => $sHp . '-video-iframe-',
+                'video' => $sHp . '-video-',
+
+                'live_update_popup' => $sHp . '-live-update-popup-',
+            ),
+            'post' => array(
+                'attach_link_popup' =>  $sHp . '-attach-link-popup',
+                'attach_link_form_field' => $sHp . '-attach-link-form-field-',
+                'attach_link_item' => $sHp . '-attach-link-item-',
+                'textarea' => $sHp . '-textarea-',
+
+                'main_' . $sHpT => $sHp . '-' . $sHpT,
+                'main_' . $sHpO => $sHp . '-' . $sHpO,
+            ),
+            'repost' => array(
+                'main' => $sHp . '-repost-',
+                'counter' => $sHp . '-repost-counter-',
+                'by_popup' => $sHp . '-repost-by-',
+                'with_popup' => $sHp . '-repost-with',
+                'to_popup' => $sHp . '-repost-to',
+            )
+        );
+
+        $this->_aRepostDefaults = array(
+            'do' => 'repost',
+
+            'show_do_repost_as_button' => false,
+            'show_do_repost_as_button_small' => false,
+            'show_do_repost_icon' => true,
+            'show_do_repost_text' => false,
+
+            //--- Counter
+            'show_counter_label_icon' => false,
+            'show_counter_label_text' => true,
+            'show_counter' => true,
+
+            //--- JS script
+            'show_script' => true,
+
+            //--- Icons
+            'icon_do_repost' => 'redo',
+            'icon_do_repost_with' => 'redo',
+            'icon_do_repost_to' => 'redo',
+
+            //--- Texts
+            'text_do_repost' => '_bx_timeline_txt_do_repost',
+            'text_do_repost_with' => '_bx_timeline_txt_do_repost_with',
+            'text_do_repost_to' => '_bx_timeline_txt_do_repost_to',
+
+            //--- Templates
+            'template_do_repost_label' => '',
+            'template_do_repost_label_name' => 'repost_do_repost_label.html',
+        );
+
+        $this->_iTimelineVisibilityThreshold = 0;
+
+        $this->_aPregPatterns = array(
+            "meta_title" => "/<title>(.*)<\/title>/",
+            "meta_description" => "/<meta[\s]+[^>]*?name[\s]?=[\s\"\']+description[\s\"\']+content[\s]?=[\s\"\']+(.*?)[\"\']+.*?>/",
+            "url" => "/(([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%#\/\.\w\-_!\(\)]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?/"
+        );
+
+        $this->_aBriefCardsTags = array('a', 'b', 'i');
+
+        $this->_sSessionKeyType = $this->_sName . '_type_';
+
+        $this->_aHotSourcesList = [
+            BX_TIMELINE_HFS_CONTENT, 
+            BX_TIMELINE_HFS_COMMENT,
+            BX_TIMELINE_HFS_VOTE
+        ];
+    }
+
+    public function init(&$oDb)
+    {
+    	parent::init($oDb);
+
+    	$sOptionPrefix = $this->getPrefix('option');
+    	$this->_bAllowEdit = getParam($sOptionPrefix . 'enable_edit') == 'on';
+        $this->_bAllowDelete = getParam($sOptionPrefix . 'enable_delete') == 'on';
+        $this->_bShowAll = getParam($sOptionPrefix . 'enable_show_all') == 'on';
+        $this->_bCountAllViews = getParam($sOptionPrefix . 'enable_count_all_views') == 'on';
+        $this->_bRepostOwnActions = getParam($sOptionPrefix . 'enable_repost_own_actions') == 'on';
+        $this->_bHideUponDelete = getParam($sOptionPrefix . 'enable_hide_upon_delete') == 'on';
+
+        $this->_bCacheItem = getParam($sOptionPrefix . 'enable_cache_item') == 'on';
+        $this->_sCacheItemEngine = getParam($sOptionPrefix . 'cache_item_engine');
+        $this->_iCacheItemLifetime = (int)getParam($sOptionPrefix . 'cache_item_lifetime');
+        $this->_bCacheTable = getParam($sOptionPrefix . 'enable_cache_table') == 'on';
+        $this->_iCacheTableInterval = (int)getParam($sOptionPrefix . 'cache_table_interval');
+        $this->_aCacheTableCheckFields = [
+            'date' => 'date',
+            'reacted' => 'date',
+            'sticked' => 'flag'
+        ];
+
+        $iPerPagePublic = (int)getParam($sOptionPrefix . 'events_per_page_home');
+        $iPerPageContext = (int)getParam($sOptionPrefix . 'events_per_page_profile');
+        $iPerPageFeed = (int)getParam($sOptionPrefix . 'events_per_page_account');
+
+        $this->_aPerPage = [
+            'default' => (int)getParam($sOptionPrefix . 'events_per_page'),
+            BX_BASE_MOD_NTFS_TYPE_PUBLIC => $iPerPagePublic,
+            BX_TIMELINE_TYPE_HOT => $iPerPagePublic,
+            BX_BASE_MOD_NTFS_TYPE_OWNER => $iPerPageContext,
+            BX_TIMELINE_TYPE_OWNER_AND_CONNECTIONS => $iPerPageFeed,
+            BX_TIMELINE_TYPE_FEED => $iPerPageFeed,
+            BX_TIMELINE_TYPE_FEED_AND_HOT => $iPerPageFeed,
+            BX_TIMELINE_TYPE_CHANNELS => $iPerPageFeed,
+    	];
+
+        $this->_bInfScroll = getParam($sOptionPrefix . 'enable_infinite_scroll') == 'on';
+        $this->_iInfScrollPerPreload = (int)getParam($sOptionPrefix . 'events_per_preload');
+        $this->_iInfScrollAutoPreloads = (int)getParam($sOptionPrefix . 'auto_preloads');
+        
+
+        $this->_iRssLength = (int)getParam($sOptionPrefix . 'rss_length');
+        $this->_iLiveUpdateLength = (int)getParam($sOptionPrefix . 'live_updates_length');
+
+        $this->_bBriefCards = getParam($sOptionPrefix . 'enable_brief_cards') == 'on';
+
+        $this->_iCharsDisplayMinTitle = (int)getParam($sOptionPrefix . 'title_chars_short');
+        $this->_iCharsDisplayMaxTitle = (int)getParam($sOptionPrefix . 'title_chars');
+
+        $this->_sVideosPreload = getParam($sOptionPrefix . 'videos_preload');
+        $this->_sVideosAutoplay = getParam($sOptionPrefix . 'videos_autoplay');
+
+        $this->_iPreloadCommentsMax = 7;
+        $this->_iPreloadComments = (int)getParam($sOptionPrefix . 'preload_comments');
+        if($this->_iPreloadComments > $this->_iPreloadCommentsMax)
+            $this->_iPreloadComments = $this->_iPreloadCommentsMax;
+
+        $this->_bJumpTo = getParam($sOptionPrefix . 'enable_jump_to_switcher') == 'on';
+        $this->_bSortByReaction = getParam($sOptionPrefix . 'enable_sort_by_reaction') == 'on';
+        $this->_bSortByUnread = getParam($sOptionPrefix . 'enable_sort_by_unread') == 'on';
+        $this->_sAttachmentsLayout = getParam($sOptionPrefix . 'attachments_layout');
+
+        $this->_bHot = getParam($sOptionPrefix . 'enable_hot') == 'on';
+        $this->_iHotThresholdContent = (int)getParam($sOptionPrefix . 'hot_threshold_age');
+        $this->_iHotThresholdComment = (int)getParam($sOptionPrefix . 'hot_threshold_comment');
+        $this->_iHotThresholdVote = (int)getParam($sOptionPrefix . 'hot_threshold_vote');
+        $this->_iHotInterval = (int)getParam($sOptionPrefix . 'hot_interval');
+        $this->_aHotSources = explode(',', getParam($sOptionPrefix . 'hot_sources'));
+        $this->_aHotList = $this->_bHot ? $this->_oDb->getHot() : array();
+
+        $this->_bEditorToolbar = getParam($sOptionPrefix . 'enable_editor_toolbar') == 'on';
+        $this->_bEditorAutoAttach = getParam($sOptionPrefix . 'editor_auto_attach_insertion') == 'on';
+        $this->_bEnableMediaPriority = getParam($sOptionPrefix . 'enable_media_priority') == 'on';
+        $this->_iLimitAttachLinks = (int)getParam($sOptionPrefix . 'limit_attach_links');
+
+        $this->_bUnhideRestored = false;
+    }
+
+    /**
+     * Generate name from parameters.
+     * 
+     * @param type $aParams - parameters whose values will be used during generation.
+     * @param type $aRules - generation rules.
+     * @return string
+     */
+    protected function getNameView($aParams, $aRules = array())
+    {
+        $bWithView = !isset($aRules['with_view']) || $aRules['with_view'] === true;
+        $bWithType = !isset($aRules['with_type']) || $aRules['with_type'] === true;
+        $bWithOwner = isset($aRules['with_owner']) && $aRules['with_owner'] === true;
+        $sGlue = !empty($aRules['glue']) ? $aRules['glue'] : '_';
+
+        $aAddons = array();
+        if(!empty($aParams['name']))
+            $aAddons[] = bx_process_output($aParams['name']);
+        else {
+            if($bWithView && !empty($aParams['view']))
+                $aAddons[] = bx_process_output($aParams['view']);
+
+            if($bWithType && !empty($aParams['type']))
+                $aAddons[] = bx_process_output($aParams['type']);
+        }
+
+        if($bWithOwner)
+            $aAddons[] = (int)$aParams['owner_id'];
+
+        return !empty($aAddons) ? implode($sGlue, $aAddons) : '';
+    }
+
+    /**
+     * Generates unique JS object name for View events block.
+     * 
+     * @param array $aParams - an array with browsing params received in View block service method
+     * @return string with JS object name
+     */
+    public function getJsObjectView($aParams = array())
+    {
+        return parent::getJsObject('view') . bx_gen_method_name($this->getNameView($aParams));
+    }
+
+    public function getHtmlIdView($sKey, $aParams, $aRules = array())
+    {
+        $bWhole = !isset($aRules['whole']) || $aRules['whole'] === true;
+        $sGlue = !empty($aRules['glue']) ? $aRules['glue'] : '_';
+
+        return str_replace($sGlue, '-', $this->_sName  . $sGlue . $sKey . $sGlue . $this->getNameView($aParams, $aRules) . (!$bWhole ? $sGlue : ''));
+    }
+
+    public function isAllowEdit()
+    {
+        return $this->_bAllowEdit;
+    }
+
+    public function isAllowDelete()
+    {
+        return $this->_bAllowDelete;
+    }
+
+    public function isInfiniteScroll()
+    {
+        return $this->_bInfScroll;
+    }
+
+    public function isShowAll()
+    {
+        return $this->_bShowAll;
+    }
+
+    public function isCountAllViews()
+    {
+        return $this->_bCountAllViews;
+    }
+
+    public function isRepostOwnActions()
+    {
+        return $this->_bRepostOwnActions;
+    }
+
+    public function isHideUponDelete()
+    {
+        return $this->_bHideUponDelete;
+    }
+
+    public function isBriefCards()
+    {
+        return $this->_bBriefCards;
+    }
+
+    public function isJumpTo()
+    {
+        return $this->_bJumpTo;
+    }
+
+    public function isSortByReaction()
+    {
+        return $this->_bSortByReaction;
+    }
+
+    public function isSortByUnread()
+    {
+        return $this->_bSortByUnread;
+    }
+
+    public function isHot()
+    {
+        return $this->_bHot;
+    }
+
+    public function getHotSourcesList()
+    {
+        return $this->_aHotSourcesList;
+    }
+
+    public function getHotSources()
+    {
+        return $this->_aHotSources;
+    }
+
+    public function isHotSource($sName)
+    {
+        return in_array($sName, $this->_aHotSources);
+    }
+
+    public function getHotThreshold($sSource)
+    {
+        if(!in_array($sSource, [BX_TIMELINE_HFS_CONTENT, BX_TIMELINE_HFS_COMMENT, BX_TIMELINE_HFS_VOTE]))
+            return false;
+
+        return $this->{'_iHotThreshold' . ucfirst($sSource)};
+    }
+
+    public function isHotEvent($iEventId)
+    {
+        return in_array($iEventId, $this->_aHotList);
+    }
+
+    public function isEditorToolbar()
+    {
+    	return $this->_bEditorToolbar;
+    }
+
+    public function isEditorAutoAttach()
+    {
+    	return $this->_bEditorAutoAttach;
+    }
+
+    public function isMediaPriority()
+    {
+        return $this->_bEnableMediaPriority;
+    }
+
+    public function isUnhideRestored()
+    {
+        return $this->_bUnhideRestored;
+    }
+
+    public function isCacheItem()
+    {
+        return $this->_bCacheItem;
+    }
+
+    public function isCacheTable()
+    {
+        return $this->_bCacheTable;
+    }
+
+    public function getCacheItemEngine()
+    {
+        return $this->_sCacheItemEngine;
+    }
+
+    public function getCacheItemLifetime()
+    {
+        return $this->_iCacheItemLifetime;
+    }
+
+    public function getCacheItemKey($iId, $sPostfix = '')
+    {
+        return $this->getPrefix('cache_item') . $iId . (bx_is_mobile() ? '_m' : '') . '_r' . bx_get_device_pixel_ratio() . (!empty($sPostfix) ? '_' . $sPostfix : '') . '_' . bx_site_hash() . '.php';
+    }
+
+    public function getCacheItemKeys($iId, $sPostfix = '')
+    {
+        $aVariants = ['_r1', '_r2', '_m_r1', '_m_r2'];
+
+        return array_map(function($sValue) use ($iId, $sPostfix) {
+            return $this->getPrefix('cache_item') . $iId . $sValue . (!empty($sPostfix) ? '_' . $sPostfix : '') . '_' . bx_site_hash() . '.php';
+        }, $aVariants);
+    }
+
+    public function getCacheHotKey()
+    {
+        return $this->getPrefix('cache_list_hot');
+    }
+    
+    public function getCacheTableInterval()
+    {
+        return 86400 * $this->_iCacheTableInterval;
+    }
+
+    public function getCacheTableCheckFields()
+    {
+        $mixedResult = null;
+        bx_alert($this->getName(), 'cache_table_check_fields', 0, 0, ['check_fields' => $this->_aCacheTableCheckFields, 'override_result' => &$mixedResult]);
+        if($mixedResult !== null)
+            return $mixedResult;
+        
+        return $this->_aCacheTableCheckFields;
+    }
+
+    public function getPostFormDisplay($sType)
+    {
+        if(empty($sType) || !array_key_exists($sType, $this->_aTypeToFormDisplay))
+            $sType = BX_TIMELINE_TYPE_DEFAULT;
+
+        return $this->_aTypeToFormDisplay[$sType];
+    }
+
+    public function getUploaders($sField)
+    {
+        $aResult = array();
+
+        switch($sField) {
+            case 'photo':
+                $aResult = $this->_aPhotoUploaders;
+                break;
+            case 'video':
+                $aResult = $this->_aVideoUploaders;
+                break;
+            case 'file':
+                $aResult = $this->_aFilesUploaders;
+                break;
+        }
+
+        return $aResult;
+    }
+
+    public function getUploaderByMenuItem($sMenuItem)
+    {
+        if(!isset($this->_aItemToUploader[$sMenuItem]))
+            return false;
+    
+        return $this->_aItemToUploader[$sMenuItem];
+    }
+
+    public function getExtenalsEvery($sType = 'default')
+    {
+        if($this->isInfiniteScroll())
+            $sType .= '_preload';
+
+        $iExtenalsEvery = (int)getParam($this->getPrefix('option') . 'extenals_every_' . $sType);
+        if(!$iExtenalsEvery)
+            return $iExtenalsEvery;
+
+        $iPerPage = $this->getPerPage($sType);
+        if(empty($iPerPage))
+            $iPerPage = $this->getPerPage();
+
+        if($iExtenalsEvery > $iPerPage)
+            $iExtenalsEvery = $iPerPage;
+
+        return $iExtenalsEvery;
+    }
+    public function getPerPreload()
+    {
+        return $this->_iInfScrollPerPreload;
+    }
+    public function getAutoPreloads()
+    {
+        return $this->_iInfScrollAutoPreloads;
+    }
+
+    public function getRssLength()
+    {
+        return $this->_iRssLength;
+    }
+
+    public function getLiveUpdateLength()
+    {
+        return $this->_iLiveUpdateLength;
+    }
+
+    public function getCharsDisplayMinTitle()
+    {
+        return $this->_iCharsDisplayMinTitle;
+    }
+
+    public function getCharsDisplayMaxTitle()
+    {
+        return $this->_iCharsDisplayMaxTitle;
+    }
+
+    public function getBriefCardsTags($bAsString = false)
+    {
+        return !$bAsString ? $this->_aBriefCardsTags : '<' . implode('><', $this->_aBriefCardsTags) . '>';
+    }
+
+    public function getVideosPreload()
+    {
+        return $this->_sVideosPreload;
+    }
+
+    public function getVideosAutoplay()
+    {
+        return $this->_sVideosAutoplay;
+    }
+
+    public function getPreloadComments()
+    {
+        return $this->_iPreloadComments;
+    }
+
+    public function getAttachmentsLayout()
+    {
+        return $this->_sAttachmentsLayout;
+    }
+
+    public function getHotInterval()
+    {
+        return $this->_iHotInterval;
+    }
+
+    public function getLimitAttachLinks()
+    {
+        return $this->_iLimitAttachLinks;
+    }
+
+    public function getRepostDefaults()
+    {
+        return $this->_aRepostDefaults;
+    }
+
+    public function getPregPattern($sType)
+    {
+        return $this->_aPregPatterns[$sType];
+    }
+
+    /**
+     * Ancillary functions
+     */
+    public function getTitle($s, $mixedProfile = false, $sMethodLength = 'getCharsDisplayMaxTitle')
+    {
+        if(get_mb_substr($s, 0, 1) == '_' && strcmp($s, _t($s)) != 0)
+            return $s;
+
+        if($mixedProfile !== false) {
+            if(is_numeric($mixedProfile))
+                $mixedProfile = BxDolProfile::getInstanceMagic((int)$mixedProfile);
+
+            if($mixedProfile instanceof BxDolProfile)
+                $s = bx_replace_markers($s, array(
+                    'profile_name' => $mixedProfile->getDisplayName()
+                ));
+        }
+
+        if(!method_exists($this, $sMethodLength))
+            $sMethodLength = 'getCharsDisplayMaxTitle';
+
+        return strmaxtextlen($s, $this->$sMethodLength(), '...');
+    }
+
+    public function getTitleShort($s, $mixedProfile = false)
+    {
+        return $this->getTitle($s, $mixedProfile, 'getCharsDisplayMinTitle');
+    }
+
+    public function getTitleDefault($bL, $bP, $bV, $bF)
+    {
+        $sResult = '';
+
+        if($bL && !$bP && !$bV && !$bF)
+            $sResult = 'link';
+        else if(!$bL && $bP && !$bV && !$bF)
+            $sResult = 'image';
+        else if(!$bL && !$bP && $bV && !$bF)
+            $sResult = 'video';
+        else if(!$bL && !$bP && !$bV && $bF)
+            $sResult = 'file';
+        else 
+            $sResult = 'media';
+
+        return $this->CNF['T']['txt_sample_with_' . $sResult];
+    }
+
+    public function getViewUrl($iOwnerId, $bAbsolute = true)
+    {
+        $sUrl = BxDolPermalinks::getInstance()->permalink('page.php?i=' . $this->CNF['URI_VIEW_LIST'], ['id' => $iOwnerId]);
+        return $bAbsolute ? bx_absolute_url($sUrl) : $sUrl;
+    }
+
+    public function getHomeViewUrl($bAbsolute = true)
+    {
+        $sUrl = BxDolPermalinks::getInstance()->permalink($this->CNF['URL_HOME']);
+        return $bAbsolute ? bx_absolute_url($sUrl) : $sUrl;
+    }
+
+    public function getItemViewUrl($aEvent, $bAbsolute = true)
+    {
+        $sUrl = BxDolPermalinks::getInstance()->permalink('page.php?i=' . $this->CNF['URI_VIEW_ENTRY'], ['id' => $aEvent['id']]);
+        return $bAbsolute ? bx_absolute_url($sUrl) : $sUrl;
+    }
+
+    public function getLiveUpdateKey($aParams)
+    {
+        return $this->getName() . '_live_update_' . $this->getNameView($aParams, array('with_owner' => true));
+    }
+
+    public function isCommon($sType, $sAction)
+    {
+        return !$this->isSystem($sType, $sAction);
+    }
+
+    public function isSystem($sType, $sAction)
+    {
+        $sPrefix = $this->getPrefix('common_post');
+        return strpos($sType, $sPrefix) === false && !empty($sAction);
+    }
+
+    public function getSystemData(&$aEvent, $aBrowseParams = array())
+    {
+        $aHandler = $this->getHandler($aEvent);
+        if($aHandler === false)
+            return false;
+
+        return BxDolService::call($aHandler['module_name'], $aHandler['module_method'], array($aEvent, $aBrowseParams), $aHandler['module_class']);
+    }
+
+    public function getSystemDataByDescriptor($sType, $sAction, $iObjectId)
+    {
+    	$aDescriptor = array(
+            'type' => $sType, 
+            'action' => $sAction,
+            'object_id' => $iObjectId
+    	);
+    	return $this->getSystemData($aDescriptor);
+    }
+
+    public function isEqualUrls($sUrl1, $sUrl2)
+    {
+        $sUrl1 = trim($sUrl1, "/");
+        $sUrl2 = trim($sUrl2, "/");
+
+        return strncmp($sUrl1, $sUrl2, strlen($sUrl1)) === 0;
+    }
+
+    public function addBrowseParams($sUrl, $aParams, $sKey = 'bp')
+    {
+        return bx_append_url_params($sUrl, [$sKey => base64_encode(json_encode($aParams))]);
+    }
+
+    public function getBrowseParams($sValue)
+    {
+        return json_decode(base64_decode(urldecode($sValue)), true);
+    }
+
+    public function setUserChoice($aChoices = array())
+    {
+        if(!isLogged() || empty($aChoices))
+            return;
+
+        $iUserId = bx_get_logged_profile_id();
+
+        $oSession = BxDolSession::getInstance();
+        foreach($aChoices as $sKey => $mixedValue) {
+            $sField = '_sSessionKey' . bx_gen_method_name($sKey);
+            if(isset($this->$sField) && !empty($mixedValue))
+                $oSession->setValue($this->$sField . $iUserId, $mixedValue);
+        }
+    }
+
+    public function getUserChoice($sKey, $iUserId = 0)
+    {
+        $sField = '_sSessionKey' . bx_gen_method_name($sKey);
+        if(!isLogged() || !isset($this->$sField))
+            return false;
+
+        if(!$iUserId)
+            $iUserId = bx_get_logged_profile_id();
+
+        return BxDolSession::getInstance()->getValue($this->$sField . $iUserId);
+    }
+
+    public function prepareParam($sName, $sPattern = "/^[\d\w_]+$/")
+    {
+        return $this->processParam(bx_get($sName), $sPattern);
+    }
+
+    public function prepareParamWithDefault($sName, $sDefault, $sPattern = "/^[\d\w_]+$/")
+    {
+        return ($sValue = $this->prepareParam($sName, $sPattern)) != '' ? $sValue : $sDefault;
+    }
+
+    public function processParam($sValue, $sPattern = "/^[\d\w_]+$/")
+    {
+        return bx_process_url_param($sValue, $sPattern);
+    }
+
+    public function processParamWithDefault($sValue, $sDefault, $sPattern = "/^[\d\w_]+$/")
+    {
+        return ($sValue = $this->processParam($sValue, $sPattern)) != '' ? $sValue : $sDefault;
+    }
+}
+
+/** @} */

+ 31 - 0
modules/boonex/timeline/updates/13.0.14_13.0.15/source/classes/BxTimelineMenuView.php

@@ -0,0 +1,31 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Timeline Timeline
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * View block menu.
+ */
+class BxTimelineMenuView extends BxTemplMenuCustom
+{
+    protected $_oModule;
+
+    protected $_bShowDivider;
+
+    public function __construct($aObject, $oTemplate = false)
+    {
+        $this->_oModule = BxDolModule::getInstance('bx_timeline');
+
+        parent::__construct($aObject, $this->_oModule->_oTemplate);
+
+        $this->_bShowDivider = false;
+    }
+}
+
+/** @} */

+ 3248 - 0
modules/boonex/timeline/updates/13.0.14_13.0.15/source/classes/BxTimelineTemplate.php

@@ -0,0 +1,3248 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Timeline Timeline
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxTimelineTemplate extends BxBaseModNotificationsTemplate
+{
+    protected static $_aMemoryCacheItems;
+    protected static $_aMemoryCacheItemsData;
+    protected static $_sMemoryCacheItemsKeyMask;
+
+    protected static $_sTmplContentItemItem;
+    protected static $_sTmplContentItemOutline;
+    protected static $_sTmplContentItemOutlineSample;
+    protected static $_sTmplContentItemTimeline;
+    protected static $_sTmplContentItemTimelineSample;
+    protected static $_sTmplContentItemSearch;
+    protected static $_sTmplContentTypePost;
+    protected static $_sTmplContentTypeRepost;
+
+    protected $_aAclId2Name;
+
+    function __construct(&$oConfig, &$oDb)
+    {
+        parent::__construct($oConfig, $oDb);
+
+        $this->_aAclId2Name = array();
+
+        bx_import('BxTemplAcl');
+        $aAclLevels = BxDolAcl::getInstance()->getMemberships(false, false, false);
+        foreach($aAclLevels as $iAclId => $sAclName)
+            $this->_aAclId2Name[$iAclId] = str_replace('_', '-', str_replace('_adm_prm_txt_level_', '', $sAclName));
+    }
+
+    public function init()
+    {
+        parent::init();
+
+        self::$_aMemoryCacheItems = array();
+        self::$_aMemoryCacheItemsData = array();
+        self::$_sMemoryCacheItemsKeyMask = "%s_%d";
+    }
+
+    public function getAddedCss($sType = '', $bDynamic = false)
+    {
+        $aCss = array();
+        switch($sType) {
+            case 'view':
+                $aCss = array(
+                    BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'flickity/|flickity.css',
+                    'cmts.css',
+                    'view.css',
+                    'view-media-tablet.css',
+                    'view-media-desktop.css',
+                    'repost.css',
+                );
+
+                if($this->_oConfig->isJumpTo()) {
+                    list($aCssCalendar, $aJsCalendar) = BxBaseFormView::getCssJsCalendar();
+                    $aCss = array_merge($aCss, $aCssCalendar);
+                }
+                break;
+
+            case 'post':
+                $aCss[] = 'post.css';
+                break;
+        }
+
+        $mixedResult = $this->addCss($aCss, $bDynamic);
+        if($bDynamic)
+            return $mixedResult; 
+    }
+
+    public function getAddedJs($sType = '', $bDynamic = false)
+    {
+        $aJs = array(
+            'autosize.min.js',
+            'jquery.anim.js',
+            'main.js',
+        );
+        switch($sType) {
+            case 'view':
+                $aJs = array_merge($aJs, array(
+                    'masonry.pkgd.min.js',
+                    'flickity/flickity.pkgd.min.js',
+                    'embedly-player.min.js',
+                    'BxDolCmts.js',
+                    'view.js',
+                    'repost.js',
+                ));
+
+                if ($this->_oConfig->isJumpTo()) {
+                    list($aCssCalendar, $aJsCalendar) = BxBaseFormView::getCssJsCalendar();
+                    $aJs = array_merge($aJs, $aJsCalendar);
+                }
+                break;
+
+            case 'post':
+                $aJs = array_merge($aJs, array(
+                    'jquery.form.min.js',
+                    'post.js',
+                ));
+                break;
+        }
+
+        $mixedResult = $this->addJs($aJs, $bDynamic);
+        if($bDynamic)
+            return $mixedResult; 
+    }
+
+    public function getJsCodeView($aParams = [], $bWrap = true, $bDynamic = false)
+    {
+        $bInfiniteScroll = $this->_oConfig->isInfiniteScroll();
+        if($bInfiniteScroll && isset($aParams['oRequestParams']['per_page']))
+            $aParams['oRequestParams']['per_page'] = $this->_oConfig->getPerPreload();
+
+        $aParams = array_merge([
+            'sObjNameMenuFeeds' => $this->_oConfig->getObject('menu_feeds'),
+            'bInfScroll' => $bInfiniteScroll,
+            'iInfScrollAutoPreloads' => $this->_oConfig->getAutoPreloads(),
+            'iLimitAttachLinks' => $this->_oConfig->getLimitAttachLinks(),
+            'sLimitAttachLinksErr' => bx_js_string(_t('_bx_timeline_txt_err_attach_links')),
+            'oAttachedLinks' => []
+        ], $aParams);
+
+        return parent::getJsCode('view', $aParams, $bWrap, $bDynamic);
+    }
+
+    public function getJsCodePost($iOwnerId, $aParams = array(), $bWrap = true, $bDynamic = false)
+    {
+        $aGeneralParams = [];
+        $aRequestParams = $aParams;
+        if(isset($aParams['gparams'], $aParams['rparams'])) {
+            $aGeneralParams = $aParams['gparams'];
+            $aRequestParams = $aParams['rparams'];
+        }
+
+        return $this->getJsCode('post', array_merge(array(
+            'bAutoAttach' => $this->_oConfig->isEditorAutoAttach(),
+            'bMediaPriority' => $this->_oConfig->isMediaPriority(),
+            'iLimitAttachLinks' => $this->_oConfig->getLimitAttachLinks(),
+            'sLimitAttachLinksErr' => bx_js_string(_t('_bx_timeline_txt_err_attach_links')),
+            'oAttachedLinks' => $this->_oDb->getLinksBy(array(
+                'type' => 'unused', 
+                'profile_id' => $this->getModule()->getUserId(), 
+                'short' => true
+            )),
+            'sVideosAutoplay' => $this->_oConfig->getVideosAutoplay(),
+            'oRequestParams' => array_merge(array(
+                'type' => isset($aRequestParams['type']) ? $aRequestParams['type'] : BX_TIMELINE_TYPE_DEFAULT, 
+                'owner_id' => $iOwnerId
+            ), $aRequestParams)
+        ), $aGeneralParams), $bWrap, $bDynamic);
+    }
+
+    public function getPostBlock($iOwnerId, $aParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+        $oModule = $this->getModule();
+
+        $aResult = $oModule->getFormPost($aParams);
+
+        if($this->_oConfig->isEditorAutoattach() && !empty($aResult['form_object'])) {
+            $aUploadersInfo = $aResult['form_object']->getUploadersInfo($CNF['FIELD_PHOTO']);
+            if(!empty($aUploadersInfo) && is_array($aUploadersInfo))
+                $aParams = [
+                    'gparams' => ['sAutoUploader' => $aUploadersInfo['name'], 'sAutoUploaderId' => $aUploadersInfo['id']],
+                    'rparams' => $aParams
+                ];
+        }
+
+        if(bx_is_api()) {
+            $aType2Uri = [
+                '' => 'get_block_post_account',
+                'account' => 'get_block_post_account',
+                'feed' => 'get_block_post_account',
+                'owner' => 'get_block_post_profile',
+                'profile' => 'get_block_post_profile',
+                'public' => 'get_block_post_home'
+            ];
+ 
+            $sType = '';
+            if(isset($aParams['rparams']['type']))
+                $sType = $aParams['rparams']['type'];
+            else
+                $sType = trim(str_replace('bx_timeline_post_add', '', $aResult['form_object']->aParams['display']), '_');
+            
+
+            $oProfileOwner = BxDolProfile::getInstance($iOwnerId);
+            $aExt = ['request' => ['url' => $sUrl = '/api.php?r=' . $this->_oModule->getName() . '/' . $aType2Uri[$sType] . '&params[]=' . $oProfileOwner->getModule() . '&params[]=' . $oProfileOwner->getContentId(), 'immutable' => true]];
+            if(!empty($aResult['id'])) {
+                $aItemData = $oModule->getItemData($aResult['id']);
+                if(is_array($aItemData) && !empty($aItemData['event']))
+                    $aExt['response'] = $this->_getPostApi($aItemData['event'], $aParams);
+            }
+            $aExt['name'] = 'feed';
+            return [
+                bx_api_get_block('form', $aResult['form_object']->getCodeAPI(), ['ext' => $aExt])
+            ];
+        }
+
+        return $this->parseHtmlByName('block_post.html', array (
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'js_object' => $this->_oConfig->getJsObject('post'),
+            'js_content' => $this->getJsCodePost($iOwnerId, $aParams),
+            'form' => $aResult['form']
+        ));
+    }
+
+    public function getViewsBlock($aParams)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+        
+        $sType = isset($aParams['type']) ? $aParams['type'] : '';
+
+        $sMenu = $this->_oConfig->getObject('menu_view');
+        $oMenu = BxDolMenu::getObjectInstance($sMenu);
+
+    	$aMenuItems = $oMenu->getMenuItems();
+    	if(empty($aMenuItems) || !is_array($aMenuItems))
+            return '';
+
+    	if(empty($sType)) {
+            $aMenuItem = array_shift($aMenuItems);
+            $sType = $aMenuItem['name'];
+    	}
+    	$oMenu->setSelected($this->_oConfig->getName(), $sType);
+        $oMenu->addMarkers(array(
+            'js_object_view' => $sJsObject
+        ));
+
+        $sTitle = _t('_bx_timeline_page_block_title_views_' . $aParams['view']);
+
+        if (bx_is_api())
+            return [
+                'content' => [bx_api_get_block('browse', ['unit' => 'feed', 'data' => $this->getViewBlock($aParams)])],
+                'menu' => $oMenu->getCodeAPI()
+            ];
+
+        return [
+            'content' => $this->parseHtmlByName('block_views.html', [
+                'style_prefix' => $sStylePrefix,
+                'html_id' => $this->_oConfig->getHtmlIdView('views', $aParams, ['with_type' => false]),
+                'html_id_content' => $this->_oConfig->getHtmlIdView('views_content', $aParams, ['with_type' => false]),
+                'html_id_view_placeholder' => $this->_oConfig->getHtmlIdView('main', array_merge($aParams, ['type' => 'placeholder'])),
+                'title' => $sTitle,
+                'content' => $this->getViewBlock($aParams)
+            ]),
+            'menu' => $oMenu
+        ];
+    }
+
+    public function getViewsDbBlock($aParams)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+        $sJsContent = $this->getJsCodeView([
+            'sObjName' => $sJsObject,
+            'sName' => $aParams['name'],
+            'sView' => $aParams['view'],
+            'sType' => $aParams['type'],
+            'sVideosAutoplay' => $this->_oConfig->getVideosAutoplay(),
+            'oRequestParams' => $aParams
+        ], [
+            'wrap' => true,
+            'mask_markers' => ['object' => $sJsObject]
+        ]);
+
+        return array(
+            'content' => $this->parseHtmlByName('block_views_db.html', array(
+                'style_prefix' => $sStylePrefix,
+                'html_id' => $this->_oConfig->getHtmlIdView('views', $aParams, array('with_type' => false)),
+                'html_id_content' => $this->_oConfig->getHtmlIdView('views_content', $aParams, array('with_type' => false)),
+                'content' => $this->getViewBlock(array_merge($aParams, ['name' => ''])),
+                'js_content' => $sJsContent
+            )),
+            'buttons' => [
+                ['title' => _t('_bx_timeline_txt_filters'), 'href' => 'javascript:void(0)', 'onclick' => 'javascript:' . $sJsObject . '.changeFeedFilters(this)']
+            ]
+        );
+    }
+
+    public function getViewFilters($aParams)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+
+        $aHandlers = $this->_oDb->getHandlers(['type' => 'by_type', 'value' => 'insert']);       
+
+        $aModules = [];
+        foreach($aHandlers as $aHandler) {
+            $sModule = $aHandler['alert_unit'];
+            if(isset($aModules[$sModule]))
+                continue;
+
+            $aModule = $this->_oDb->getModuleByName($sModule);
+            if(empty($aModule) || !is_array($aModule))
+                continue;
+
+            $sTitleKey = '_' . $sModule;
+            $sTitleValue = _t($sTitleKey);
+            if(strcmp($sTitleKey, $sTitleValue) == 0)
+                $sTitleValue = $aModule['title'];
+
+            $aModules[$sModule] = [
+                'key' => $aHandler['alert_unit'],
+                'value' => $sTitleValue
+            ];
+        }
+
+        uasort($aModules, function($aV1, $aV2) {
+            return strcmp($aV1['value'], $aV2['value']);
+        });
+
+        $aForm = [
+            'inputs' => [
+                'by_module' => [
+                    'name' => 'by_module',
+                    'type' => 'radio_set',
+                    'caption' => _t('_bx_timeline_form_filters_input_by_modules'),
+                    'values' => [
+                        ['key' => 'all', 'value' => _t('_bx_timeline_form_filters_input_by_modules_all')],
+                        ['key' => 'selected', 'value' => _t('_bx_timeline_form_filters_input_by_modules_selected')]
+                    ],
+                    'value' => 'all',
+                    'attrs' => ['onchange' => $sJsObject . '.onFilterByModuleChange(this)'],
+                    'dv_thd' => 1
+                ],
+                'modules' => [
+                    'name' => 'modules',
+                    'type' => 'checkbox_set',
+                    'values' => array_values($aModules),
+                    'tr_attrs' => ['class' => 'modules', 'style' => 'display:none']
+                ],
+                'apply' => [
+                    'name' => 'apply',
+                    'type' => 'button',
+                    'value' => _t('_bx_timeline_form_filters_input_do_apply'),
+                    'attrs' => ['onclick' => $sJsObject . '.onFilterApply(this)']
+                ]
+            ]
+        ];
+        $oForm = new BxTemplFormView($aForm);
+        
+
+        $sViewFiltersPopupId = $this->_oConfig->getHtmlIdView('filters_popup', $aParams);
+        $sViewFiltersPopupContent = $this->parseHtmlByName('block_view_filters.html', array(
+            'style_prefix' => $sStylePrefix,
+            'js_object' => $sJsObject,
+            'content' => $oForm->genRows()
+    	));
+
+    	return BxTemplFunctions::getInstance()->transBox($sViewFiltersPopupId, $sViewFiltersPopupContent, true);
+    }
+
+    public function getViewBlock($aParams)
+    {
+        $oModule = $this->getModule();
+
+        if(bx_is_api()) {
+            if(defined('BX_API_PAGE'))
+                return [];
+
+            $oMenuActions = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_actions_all'));
+            if(!$oMenuActions)
+                $oMenuActions = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_actions'));
+
+            $oMenuManage = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_manage'));
+
+            $aPosts = $this->getPosts(array_merge($aParams, ['return_data_type' => 'array']));
+            foreach($aPosts as &$aPost)
+                $aPost = $this->_getPostApi($aPost, $aParams, [
+                    'menu_actions' => $oMenuActions,
+                    'menu_manage' => $oMenuManage
+                ]);
+
+            return $aPosts;
+        }
+
+        list($sContent, $sLoadMore, $sBack, $sEmpty, $iEvent, $bEventsToLoad) = $this->getPosts($aParams);
+        //--- Add live update
+        $oModule->actionResumeLiveUpdate($aParams['type'], $aParams['owner_id']);
+
+        $sModuleName = $oModule->getName();
+        $sModuleMethod = !empty($aParams['get_live_updates']) ? $aParams['get_live_updates'] : 'get_live_update';
+        $sService = BxDolService::getSerializedService($sModuleName, $sModuleMethod, [$aParams, $oModule->getUserId(), '{count}', '{init}']);
+
+        $aLiveUpdateParams = [$this->_oConfig->getLiveUpdateKey($aParams), 1, $sService, [true, false]];
+        if($sModuleMethod == 'get_live_update')
+            $aLiveUpdateParams[] = $iEvent;
+
+        $sLiveUpdateCode = null;
+        bx_alert($sModuleName, 'add_live_update', 0, 0, [
+            'browse_params' => $aParams,
+            'live_update_params' => &$aLiveUpdateParams,
+            'override_result' => &$sLiveUpdateCode,
+        ]);
+
+        if($sLiveUpdateCode === null && ($oLiveUpdates = BxDolLiveUpdates::getInstance()) !== false)
+            $sLiveUpdateCode = call_user_func_array([$oLiveUpdates, 'add'], $aLiveUpdateParams);
+        //--- Add live update
+
+        $sContentBefore = '';
+        $sContentAfter = '';
+
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+        $sJsContent = $this->getJsCodeView(array(
+            'sObjName' => $sJsObject,
+            'sName' => $aParams['name'],
+            'sView' => $aParams['view'],
+            'sType' => $aParams['type'],
+            'sVideosAutoplay' => $this->_oConfig->getVideosAutoplay(),
+            'bEventsToLoad' => $bEventsToLoad,
+            'bAutoMarkAsViewed' => $this->_oConfig->isSortByUnread(),
+            'oRequestParams' => $aParams
+        ), array(
+            'wrap' => true,
+            'mask_markers' => array('object' => $sJsObject)
+        )) . $this->getJsCode('repost');
+
+        bx_alert($sModuleName, 'get_view', 0, 0, array(
+            'params' => $aParams,
+            'event_first' => $iEvent,
+            'back' => &$sBack,
+            'empty' => &$sEmpty,
+            'content_before' => &$sContentBefore,
+            'content' => &$sContent,
+            'content_after' => &$sContentAfter,
+            'load_more' => &$sLoadMore,
+            'js_content' => &$sJsContent
+        ));
+
+        return $this->parseHtmlByName('block_view.html', array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'html_id' => $this->_oConfig->getHtmlIdView('main', $aParams),
+            'view' => $aParams['view'],
+            'back' => $sBack,
+            'empty' => $sEmpty,
+            'content_before' => $sContentBefore,
+            'content' => $sContent,
+            'content_after' => $sContentAfter,
+            'load_more' =>  $sLoadMore,
+            'show_more' => $this->_getShowMore($aParams),
+            'view_image_popup' => $this->_getImagePopup($aParams),
+            'live_update_code' => $sLiveUpdateCode,
+            'js_content' => $sJsContent
+        ));
+    }
+
+    public function getSearchBlock($sContent)
+    {
+        $oModule = $this->getModule();
+        $aParams = $oModule->getParams(BX_TIMELINE_VIEW_SEARCH);
+
+        return $this->parseHtmlByName('block_search.html', array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'html_id' => $this->_oConfig->getHtmlIdView('main', $aParams),
+            'view' => $aParams['view'],
+            'content' => $sContent,
+            'view_image_popup' => $this->_getImagePopup($aParams),
+            'js_content' => $this->getJsCodeView(array(
+            	'oRequestParams' => $aParams
+            ))
+        ));
+    }
+
+    public function getItemBlock($mixedId, $aBrowseParams = array())
+    {
+        $CNF = $this->_oConfig->CNF;
+        $oModule = $this->getModule();
+
+        if(is_numeric($mixedId))
+            $mixedId = $oModule->getItemData($mixedId);
+
+        if(empty($mixedId) || !is_array($mixedId))
+            return array('content' => MsgBox(_t('_Empty')), 'designbox_id' => 13);
+
+        if($mixedId['code'] != 0)
+            return array('content' => MsgBox($mixedId['content']), 'designbox_id' => 13);
+
+        $aEvent = $mixedId['event'];
+        $sContent = $mixedId['content'];
+
+        if(!$this->_oConfig->isSystem($aEvent['type'], $aEvent['action'])) {
+            $mixedViews = $oModule->getViewsData($aEvent['views']);
+            if($mixedViews !== false) {
+                list($sSystem, $iObjectId) = $mixedViews;
+                $oModule->getViewObject($sSystem, $iObjectId)->doView();
+            }
+        }
+
+        $sAuthorName = $oModule->getObjectUser($aEvent['object_owner_id'])->getDisplayName();
+
+        $sTitle = $sAuthorName . ' ' . _t($aEvent['sample_action'], _t($aEvent['sample']));
+        $sDescription = $aEvent['title'];
+        if(get_mb_substr($sDescription, 0, 1) == '_')
+            $sDescription = _t($sDescription);
+
+        $oTemplate = BxDolTemplate::getInstance();
+        $oTemplate->setPageHeader(strip_tags($sTitle));
+        $oTemplate->setPageDescription(strip_tags($sDescription));
+
+        $oMetatags = BxDolMetatags::getObjectInstance($this->_oConfig->getObject('metatags'));
+        if($oMetatags)
+            $oMetatags->addPageMetaInfo($aEvent[$CNF['FIELD_ID']]);
+
+        $sReferrer = '';
+        if(isset($_SERVER['HTTP_REFERER']) && mb_stripos($_SERVER['HTTP_REFERER'], BX_DOL_URL_ROOT) === 0)
+            $sReferrer = $_SERVER['HTTP_REFERER'];
+        else 
+            $sReferrer = bx_absolute_url(BxDolPermalinks::getInstance()->permalink($this->_oConfig->CNF['URL_HOME']));
+
+        $sJsObject = $this->_oConfig->getJsObjectView($aBrowseParams);
+        return array('content' => $this->parseHtmlByName('block_item.html', array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'html_id' => $this->_oConfig->getHtmlIdView('main', $aBrowseParams),
+            'content' => $sContent,
+            'show_more' => $this->_getShowMore($aBrowseParams),
+            'view_image_popup' => $this->_getImagePopup($aBrowseParams),
+            'js_content' => $this->getJsCodeView(array(
+                'sObjName' => $sJsObject,
+                'sVideosAutoplay' => $this->_oConfig->getVideosAutoplay(),
+                'sReferrer' => $sReferrer,
+                'oRequestParams' => $aBrowseParams
+            ), array(
+                'wrap' => true,
+                'mask_markers' => array('object' => $sJsObject)
+            )) . $this->getJsCode('repost')
+        )));
+    }
+
+    /**
+     * Get event's content.
+     * @param integer $iId - event ID.
+     * @param string $sMode - 'file'/'photo'/'video' are available for now. But only images will be shown.
+     */
+    public function getItemBlockContent($iId, $sMode) {
+        $oModule = $this->getModule();
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $aItemData = $oModule->getItemData($iId);
+        if(empty($aItemData) || $aItemData['code'] != 0)
+            return '';
+
+        $aEvent = $aItemData['event'];       
+
+        $sKeyMain = $sKeyAttach = '';
+        switch($sMode) {
+            case 'file':
+                $sKeyMain = 'files';
+                $sKeyAttach = 'files_attach';
+                break;
+
+            case 'photo':
+                $sKeyMain = 'images';
+                $sKeyAttach = 'images_attach';
+                break;
+
+            case 'video':
+                $sKeyMain = 'videos';
+                $sKeyAttach = 'videos_attach';
+                break;
+        }
+
+        $aImages = [];
+        if(!empty($aEvent['content'][$sKeyMain]) && is_array($aEvent['content'][$sKeyMain]))
+            $aImages = $aEvent['content'][$sKeyMain];
+        else if(!empty($aEvent['content'][$sKeyAttach]) && is_array($aEvent['content'][$sKeyAttach]))
+            $aImages = $aEvent['content'][$sKeyAttach];
+
+        if(isset($aImages['total']) && isset($aImages['items']))
+            $aImages = $aImages['items'];
+
+        $bImageSingle = count($aImages) == 1;
+        $sImageSelected = base64_decode(bx_process_input(bx_get('src')));
+
+        $aTmplVarsImages = [];
+        if(!$bImageSingle) {
+            $iIndex = 1;
+            foreach($aImages as $aImage)  {
+                if(!isset($aImage['src'], $aImage['src_orig']))
+                    continue;
+
+                $iCurrent = $iIndex;
+                if(strcmp($aImage['src'], $sImageSelected) == 0 || strcmp($aImage['src_orig'], $sImageSelected) == 0)
+                    $iCurrent = 0;
+
+                $aTmplVarsImages[$iCurrent] = [
+                    'style_prefix' => $sStylePrefix,
+                    'url' => $aImage['url'],
+                    'src' => $aImage[!empty($aImage['src_orig']) ? 'src_orig' : 'src']
+                ];
+
+                $iIndex++;
+            }
+
+            ksort($aTmplVarsImages);
+            $aTmplVarsImages = array_values($aTmplVarsImages);
+        }
+
+        $aTmplVars = [
+            'style_prefix' => $sStylePrefix,
+            'bx_if:show_image' => [
+                'condition' => $bImageSingle,
+                'content' => [
+                    'style_prefix' => $sStylePrefix,
+                    'src' => $sImageSelected,
+                ]
+            ],
+            'bx_if:show_images' => [
+                'condition' => !$bImageSingle,
+                'content' => [
+                    'style_prefix' => $sStylePrefix,
+                    'bx_repeat:images' => $aTmplVarsImages
+                ]
+            ]
+        ];
+
+        return $this->parseHtmlByName('block_item_content.html', $aTmplVars);
+    }
+
+    public function getItemBlockInfo($iId) {
+        $CNF = $this->_oConfig->CNF;
+
+        $aEvent = $this->_oDb->getEvents(array('browse' => 'id', 'value' => $iId));
+        if(empty($aEvent))
+            return '';
+
+        $aResult = $this->getDataCached($aEvent);
+        if($aResult === false)
+            return '';
+
+        if(bx_is_api()) {
+            $oMenuManage = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_manage'));
+            $oMenuManage->setEvent($aEvent);
+
+            return [bx_api_get_block('entity_author', [
+                'author_data' => BxDolProfile::getData($aResult[$CNF['FIELD_OBJECT_OWNER_ID']]),
+                'entry_date' => !empty($aEvent[$CNF['FIELD_ADDED']]) ? $aEvent[$CNF['FIELD_ADDED']] : '',
+                'menu_manage' => $oMenuManage->getCodeApi()
+            ])];
+        }
+
+        $oForm = BxDolForm::getObjectInstance($this->_oConfig->getObject('form_post'), $this->_oConfig->getObject('form_display_post_view'), $this);
+        $oForm->initChecker($aEvent);
+
+        return $this->parseHtmlByName('block_item_info.html', [
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'author' => $this->getModule()->getObjectUser($aResult['object_owner_id'])->getUnit(),
+            'fields' => $oForm->getCode()
+        ]);
+    }
+
+    public function getItemBlockComments($iId)
+    {
+        $aEvent = $this->_oDb->getEvents(['browse' => 'id', 'value' => $iId]);
+        if(empty($aEvent))
+            return '';
+
+        $aEventData = $this->getDataCached($aEvent);
+        if($aEventData === false)
+            return '';
+
+        if(!$this->getModule()->isAllowedComment(array_merge($aEvent, $aEventData)))
+            return '';
+
+        if(bx_is_api()) {
+            $mixedComments = $this->getModule()->getCommentsData($aEventData['comments']);
+            if($mixedComments === false)
+                return '';
+
+            list($sSystem, $iObjectId) = $mixedComments;
+            return [bx_srv('system', 'get_data_api', [['module' => $sSystem, 'object_id' => $iObjectId]], 'TemplCmtsServices')];
+        }
+
+        return $this->parseHtmlByName('block_item_comments.html', [
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'content' => $this->_getComments($aEventData['comments'])
+        ]);
+    }
+
+    public function getUnit(&$aEvent, $aBrowseParams = array())
+    {
+        $oModule = $this->getModule();
+
+        if(empty($aBrowseParams) || !is_array($aBrowseParams))
+            $aBrowseParams = $oModule->getParams(BX_TIMELINE_VIEW_SEARCH);
+
+        return $this->getPost($aEvent, $aBrowseParams);
+    }
+
+    public function getPost(&$aEvent, $aBrowseParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $iEventId = (int)$aEvent[$CNF['FIELD_ID']];
+        $iViewerId = isset($aBrowseParams['viewer_id']) ? (int)$aBrowseParams['viewer_id'] : bx_get_logged_profile_id();
+
+        $sMemoryCacheItemsKey = sprintf(self::$_sMemoryCacheItemsKeyMask, $aBrowseParams['view'], $iEventId);
+        if(array_key_exists($sMemoryCacheItemsKey, self::$_aMemoryCacheItems)) {
+            if(array_key_exists($sMemoryCacheItemsKey, self::$_aMemoryCacheItemsData))
+                $aEvent = self::$_aMemoryCacheItemsData[$sMemoryCacheItemsKey];
+
+            return self::$_aMemoryCacheItems[$sMemoryCacheItemsKey];
+        }
+
+        /**
+         * Add all items in memory cache even if they are empty.
+         */
+        self::$_aMemoryCacheItems[$sMemoryCacheItemsKey] = '';
+        self::$_aMemoryCacheItemsData[$sMemoryCacheItemsKey] = array();
+
+        $oPrivacy = BxDolPrivacy::getObjectInstance($this->_oConfig->getObject('privacy_view'));
+        if($oPrivacy) {
+            $oPrivacy->setTableFieldAuthor($this->_oConfig->isSystem($aEvent['type'], $aEvent['action']) ? 'owner_id' : 'object_id');
+            if(!$oPrivacy->check($iEventId, $iViewerId))
+                return '';
+        }
+
+        $aResult = $this->getDataCached($aEvent, $aBrowseParams);
+        if($aResult === false)
+            return '';
+
+        if(isset($aResult['owner_id']))
+            $aEvent['owner_id'] = $aResult['owner_id'];
+
+        $aEvent['object_owner_id'] = $aResult['object_owner_id'];
+        $aEvent['icon'] = !empty($aResult['icon']) ? $aResult['icon'] : '';
+        $aEvent['sample'] = !empty($aResult['sample']) ? $aResult['sample'] : '_bx_timeline_txt_sample';
+        $aEvent['sample_action'] = !empty($aResult['sample_action']) ? $aResult['sample_action'] : '_bx_timeline_txt_added_sample';
+        if(isset($aResult['sample_action_custom']))
+            $aEvent['sample_action_custom'] = $aResult['sample_action_custom'];
+        $aEvent['content'] = $aResult['content'];
+        $aEvent['content_type'] = !empty($aResult['content_type']) ? $aResult['content_type'] : BX_TIMELINE_PARSE_TYPE_DEFAULT;
+        $aEvent['views'] = $aResult['views'];
+        $aEvent['votes'] = $aResult['votes'];
+        $aEvent['reactions'] = $aResult['reactions'];
+        $aEvent['scores'] = $aResult['scores'];
+        $aEvent['reports'] = $aResult['reports'];
+        $aEvent['comments'] = $aResult['comments'];
+
+        $sKey = 'allowed_view';
+        $aEvent[$sKey] = $this->_preparePrivacy($sKey, $aEvent, $aResult);
+        if(isset($aEvent[$sKey]) && $aEvent[$sKey] !== CHECK_ACTION_RESULT_ALLOWED) 
+            return '';
+
+        self::$_aMemoryCacheItems[$sMemoryCacheItemsKey] = $this->_getPost($aEvent['content_type'], $aEvent, $aBrowseParams);
+        self::$_aMemoryCacheItemsData[$sMemoryCacheItemsKey] = $aEvent;
+
+        return self::$_aMemoryCacheItems[$sMemoryCacheItemsKey];
+    }
+
+    public function getPosts($aParams)
+    {
+        $bReturnArray = isset($aParams['return_data_type']) && $aParams['return_data_type'] == 'array';
+        $bViewTimeline = $aParams['view'] == BX_TIMELINE_VIEW_TIMELINE;
+
+        $iStart = $aParams['start'];
+        $iPerPage = $aParams['per_page'];
+
+        $aParamsDb = $aParams;
+
+        //--- Before: Check for Next
+        $aParamsDb['per_page'] += 1;
+        if($this->_oConfig->isCacheTable()) {
+            $aEvents = $this->_oDb->getEvents(array_merge($aParamsDb, ['from_cache' => true]));
+            if(count($aEvents) < $aParamsDb['per_page'])
+                $aEvents = $this->_oDb->getEvents(array_merge($aParamsDb, ['from_cache' => false]));
+        }
+        else
+            $aEvents = $this->_oDb->getEvents($aParamsDb);
+
+        //--- After: Check for Next
+        $bNext = false;
+        if(count($aEvents) > $iPerPage) {
+            $aEvent = array_pop($aEvents);
+            $bNext = true;
+        }
+
+        $sContent = '';
+        $sContent .= $this->getSizer($aParams);
+
+        $iFirst = 0;
+        $iEvents = count($aEvents);
+        if($iEvents > 0)
+            $iFirst = $this->_getFirst($aEvents, $aParams);
+
+        //--- Check for Visual Grouping
+        $aGroups = array();
+        foreach($aEvents as $iIndex => $aEvent) {
+            $aContent = unserialize($aEvent['content']);
+            if(!isset($aContent['timeline_group']))
+                continue;
+
+            $aGroup = $aContent['timeline_group'];
+            $sGroup = $aGroup['by'];
+            if(!isset($aGroups[$sGroup]))
+               $aGroups[$sGroup] = array('field' => $aGroup['field'], 'indexes' => array(), 'processed' => false);
+
+            $aGroups[$sGroup]['indexes'][] = $iIndex;
+        }
+
+        //--- Perform Visual Grouping
+        foreach($aGroups as $sGroup => $aGroup) {
+            if(empty($aGroup['field']) || empty($aGroup['indexes']))
+                continue;
+
+            switch($aGroup['field']) {
+                case 'owner_id':
+                    $aOwnerIds = array();
+                    foreach($aGroup['indexes'] as $iIndex)
+                        if(!in_array($aEvents[$iIndex]['owner_id'], $aOwnerIds))
+                            $aOwnerIds[] = $aEvents[$iIndex]['owner_id'];
+
+                    $iGroupIndex = (int)array_shift($aGroup['indexes']);
+                    if(is_null($iGroupIndex))
+                        break;
+
+                    foreach($aGroup['indexes'] as $iIndex)
+                        unset($aEvents[$iIndex]);
+
+                    $aEvents[$iGroupIndex]['owner_id_grouped'] = $aOwnerIds;
+                    break;
+            }
+        }
+
+        $iExtenalsEvery = $this->_oConfig->getExtenalsEvery($aParams['type']);
+
+        $iEventIndex = 0;
+        $mixedEvents = $bReturnArray ? array() : '';
+        foreach($aEvents as $aEvent) {
+            $iEvent = (int)$aEvent['id'];
+            $aEvent['index'] = $iEventIndex + 1;
+
+            $sEvent = $this->getPost($aEvent, $aParams);
+            if(empty($sEvent))
+                continue;
+
+            if($bReturnArray) {
+                $mixedEvents[] = $aEvent;
+                continue;
+            }
+
+            $mixedEvents .= $sEvent;
+
+            $iEventIndex++;
+            if($iExtenalsEvery > 0 && $iEventIndex % $iExtenalsEvery == 0) {
+                $sExternalPost = false;
+                bx_alert($this->_oConfig->getName(), 'get_external_post', 0, 0, array(
+                    'params' => $aParams,
+                    'override_result' => &$sExternalPost,
+                ));
+
+                if($sExternalPost !== false)
+                    $mixedEvents .= $sExternalPost;
+            }
+        }
+
+        if($bReturnArray)
+            return $mixedEvents;
+
+        $sContent .= $mixedEvents;
+
+        $bEvents = !empty($mixedEvents);
+
+        $sBack = $this->getBack($aParams);
+
+        if($this->_oConfig->isInfiniteScroll()) {
+            $aParams['per_page'] = $this->_oConfig->getPerPreload();
+            $iAutoPreloads = $this->_oConfig->getAutoPreloads();
+
+            $bAuto = ($aParams['start'] - $aParams['per_page_default'] - $aParams['per_page'] * ($iAutoPreloads - 2)) % ($aParams['per_page'] * $iAutoPreloads) != 0;
+            $sLoadMore = $this->{'getLoadMore' . ($bAuto ? 'Auto' : '')}($aParams, $bNext, $iEvents > 0 && $bEvents);
+        }
+        else
+            $sLoadMore = $this->getLoadMore($aParams, $bNext, $iEvents > 0 && $bEvents);
+
+        $sEmpty = $this->getEmpty($iEvents <= 0 || !$bEvents);
+
+        return array($sContent, $sLoadMore, $sBack, $sEmpty, $iFirst, $bNext);
+    }
+
+    public function getEmpty($bVisible)
+    {
+        return $this->parseHtmlByName('empty.html', array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'visible' => $bVisible ? 'block' : 'none',
+            'content' => MsgBox(_t('_bx_timeline_txt_msg_no_results'))
+        ));
+    }
+
+    public function getSizer($aParams)
+    {
+        if($aParams['view'] != BX_TIMELINE_VIEW_OUTLINE)
+            return '';
+
+        return $this->parseHtmlByName('sizer_' . $aParams['view'] . '.html', array(
+        	'style_prefix' => $this->_oConfig->getPrefix('style')
+        ));
+    }
+
+    public function getBack($aParams)
+    {
+        $iYearSel = (int)$aParams['timeline'];
+        if($iYearSel == 0)
+            return '';
+
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+
+        $iYearNow = date('Y', time());
+        return $this->parseHtmlByName('back.html', array(
+        	'style_prefix' => $sStylePrefix,
+            'content' => $this->parseLink('javascript:void(0)', _t('_bx_timeline_txt_jump_to_recent'), array(
+                'title' => _t('_bx_timeline_txt_jump_to_n_year', $iYearNow),
+        		'onclick' => 'javascript:' . $sJsObject . '.changeTimeline(this, 0)'
+            ))
+        ));
+    }
+
+    public function getLoadMore($aParams, $bEnabled, $bVisible = true)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+
+        $iStart = $aParams['start'];
+        $iPerPage = $aParams['per_page'];
+
+        $aTmplVars = array(
+            'style_prefix' => $sStylePrefix,
+            'visible' => ($aParams['view'] == BX_TIMELINE_VIEW_TIMELINE && $bVisible) || ($aParams['view'] == BX_TIMELINE_VIEW_OUTLINE && $bEnabled && $bVisible) ? 'block' : 'none',
+            'bx_if:is_disabled' => array(
+                'condition' => !$bEnabled,
+                'content' => array()
+            ),
+            'bx_if:show_on_click' => array(
+                'condition' => $bEnabled,
+                'content' => array(
+                    'on_click' => 'javascript:' . $sJsObject . '.changePage(this, ' . ($iStart + $iPerPage) . ', ' . $iPerPage . ')'
+                )
+            ),
+            'bx_if:show_jump_to' => array(
+                'condition' => $this->_oConfig->isJumpTo(),
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'content' => $this->getJumpTo($aParams)
+                )
+            )
+        );
+        return $this->parseHtmlByName('load_more.html', $aTmplVars);
+    }
+
+    public function getLoadMoreAuto($aParams, $bEnabled, $bVisible = true)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        return $this->parseHtmlByName('load_more_auto.html', array(
+            'style_prefix' => $sStylePrefix,
+            'visible' => ($aParams['view'] == BX_TIMELINE_VIEW_TIMELINE && $bVisible) || ($aParams['view'] == BX_TIMELINE_VIEW_OUTLINE && $bEnabled && $bVisible) ? 'block' : 'none',
+            'loading' => _t('_bx_timeline_txt_loading' . ($bEnabled ? '' : '_complete'))
+        ));
+    }
+
+    /**
+     * Note. For now both List and Caledar based Jump To elements are available.
+     * Calendar based element is used by default. List based one can be removed 
+     * later if it won't be used completely.
+     */
+    public function getJumpTo($aParams)
+    {
+        if(!$this->_oConfig->isJumpTo())
+            return '';
+
+        $bList = false;
+        $bDynamicMode = isset($aParams['dynamic_mode']) && (bool)$aParams['dynamic_mode'] === true;
+
+        if($bList && !$bDynamicMode)
+            return '';
+
+        return $this->{'_getJumpTo' . ($bList ? 'List' : 'Caledar')}($aParams);
+    }
+
+    public function getComments($sSystem, $iId, $aBrowseParams = array())
+    {
+        $oModule = $this->getModule();
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $oCmts = $oModule->getCmtsObject($sSystem, $iId);
+        if($oCmts === false)
+            return '';
+
+        if (bx_is_api()){
+            $aParams = ['mode' => 'feed', 'order_way' => 'desc', 'start_from' => 0];
+            return [bx_srv('system', 'get_comments_api', [$oCmts, $aParams], 'TemplCmtsServices')];
+        }
+        
+        $aCmtsBp = array();
+        if(!empty($aBrowseParams['cmts_preload_number']))
+            $aCmtsBp['init_view'] = $aBrowseParams['cmts_preload_number'];
+
+        $aCmtsDp = array(
+            'in_designbox' => false, 
+            'dynamic_mode' => isset($aBrowseParams['dynamic_mode']) && $aBrowseParams['dynamic_mode'] === true
+        );
+        if(!empty($aBrowseParams['cmts_min_post_form']))
+            $aCmtsDp['min_post_form'] = $aBrowseParams['cmts_min_post_form'];
+
+        $aComments = $oCmts->getCommentsBlock($aCmtsBp, $aCmtsDp);
+        if(empty($aComments) || !is_array($aComments))
+            return '';
+
+        return $this->parseHtmlByName('comments.html', array(
+            'style_prefix' => $sStylePrefix,
+            'id' => $iId,
+            'content' => $aComments['content']
+        ));
+    }
+
+    public function getRepostElement($iOwnerId, $sType, $sAction, $iObjectId, $aParams = [])
+    {
+        $aReposted = $this->_oDb->getReposted($sType, $sAction, $iObjectId);
+        if(empty($aReposted) || !is_array($aReposted))
+            return '';
+
+        $oModule = $this->getModule();
+        $bDisabled = $oModule->isAllowedRepost($aReposted) !== true || $this->_oDb->isReposted($aReposted['id'], $iOwnerId, $oModule->getUserId());
+        if($bDisabled && (int)$aReposted['reposts'] == 0)
+            return '';
+
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sStylePrefixRepost = $sStylePrefix . '-repost-';
+
+        $sDo = isset($aParams['do']) ? $aParams['do'] : 'repost';
+        $bDynamicMode = isset($aParams['dynamic_mode']) && $aParams['dynamic_mode'] === true;       
+
+        $bShowDoRepostAsButtonSmall = isset($aParams['show_do_repost_as_button_small']) && $aParams['show_do_repost_as_button_small'] == true;
+        $bShowDoRepostAsButton = !$bShowDoRepostAsButtonSmall && isset($aParams['show_do_repost_as_button']) && $aParams['show_do_repost_as_button'] == true;
+
+        $bShowDoRepostIcon = isset($aParams['show_do_repost_icon']) && $aParams['show_do_repost_icon'] == true && !empty($aParams['icon_do_repost']);
+        $bShowDoRepostText = isset($aParams['show_do_repost_text']) && $aParams['show_do_repost_text'] == true && !empty($aParams['text_do_repost']);
+        $bShowCounter = isset($aParams['show_counter']) && $aParams['show_counter'] === true;
+
+        //--- Do repost link ---//
+        $sClass = $sStylePrefixRepost . 'do-repost';
+        if($bShowDoRepostAsButton)
+            $sClass .= ' bx-btn';
+        else if($bShowDoRepostAsButtonSmall)
+            $sClass .= ' bx-btn bx-btn-small';
+
+        $sOnClick = '';
+        if(!$bDisabled) {
+            $sMethod = '_get' . bx_gen_method_name($sDo) . 'JsClick';
+            if(!method_exists($this, $sMethod))
+                $sMethod = '_getRepostJsClick';
+
+            $sCommonPrefix = $this->_oConfig->getPrefix('common_post');
+            if(str_replace($sCommonPrefix, '', $sType) == BX_TIMELINE_PARSE_TYPE_REPOST) {
+                $aRepostedData = unserialize($aReposted['content']);
+
+                $sOnClick = $this->$sMethod($iOwnerId, $aRepostedData['type'], $aRepostedData['action'], $aRepostedData['object_id']);
+            }
+            else
+                $sOnClick = $this->$sMethod($iOwnerId, $sType, $sAction, $iObjectId);
+        }
+        else
+            $sClass .= $bShowDoRepostAsButton || $bShowDoRepostAsButtonSmall ? ' bx-btn-disabled' : ' ' . $sStylePrefixRepost . 'disabled';
+
+        $aOnClickAttrs = array(
+            'title' => _t('_bx_timeline_txt_do_' . $sDo)
+        );
+        if(!empty($sClass))
+            $aOnClickAttrs['class'] = $sClass;
+        if(!empty($sOnClick))
+            $aOnClickAttrs['onclick'] = $sOnClick;
+
+        //--- Do repost label ---//
+        $sMethodDoRepostLabel = ''; 
+        $sTemplateDoRepostLabel = '';
+        if(!empty($aParams['template_do_repost_label'])) {
+            $sMethodDoRepostLabel = 'parseHtmlByContent';
+            $sTemplateDoRepostLabel = $aParams['template_do_repost_label'];
+        }
+        else {
+            $sMethodDoRepostLabel = 'parseHtmlByName';
+            $sTemplateDoRepostLabel = $aParams['template_do_repost_label_name'];
+        }
+
+        $sDoRepost = $this->$sMethodDoRepostLabel($sTemplateDoRepostLabel, array(
+            'style_prefix' => $sStylePrefix,
+            'bx_if:show_icon' => array(
+                'condition' => $bShowDoRepostIcon,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'icon' => $this->getImageAuto($aParams['icon_do_' . $sDo])
+                )
+            ),
+            'bx_if:show_text' => array(
+                'condition' => $bShowDoRepostText,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'text' => _t($aParams['text_do_' . $sDo])
+                )
+            )
+        ));
+
+        return $this->parseHtmlByName('repost_element_block.html', array(
+            'style_prefix' => $sStylePrefix,
+            'html_id' => $this->_oConfig->getHtmlIds('repost', 'main') . $aReposted['id'],
+            'class' => ($bShowDoRepostAsButton ? $sStylePrefixRepost . 'button' : '') . ($bShowDoRepostAsButtonSmall ? $sStylePrefixRepost . 'button-small' : ''),
+            'count' => $aReposted['reposts'],
+            'do_repost' => $this->parseLink('javascript:void(0)', $sDoRepost, $aOnClickAttrs),
+            'bx_if:show_counter' => array(
+                'condition' => $bShowCounter,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'bx_if:show_hidden' => array(
+                        'condition' => (int)$aReposted['reposts'] == 0,
+                        'content' => array()
+                    ),
+                    'counter' => $this->getRepostCounter($aReposted, array_merge($aParams, ['show_script' => false]))
+                )
+            ),
+            'script' => $this->getRepostJsScript($bDynamicMode)
+        ));
+    }
+
+    public function getRepostCounter($aEvent, $aParams = [])
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObject('repost');
+
+        $bDynamicMode = isset($aParams['dynamic_mode']) && $aParams['dynamic_mode'] === true;
+
+        $bShowDoRepostAsButtonSmall = isset($aParams['show_do_repost_as_button_small']) && $aParams['show_do_repost_as_button_small'] == true;
+        $bShowDoRepostAsButton = !$bShowDoRepostAsButtonSmall && isset($aParams['show_do_repost_as_button']) && $aParams['show_do_repost_as_button'] == true;
+        $bShowScript = !isset($aParams['show_script']) || (bool)$aParams['show_script'] === true;
+
+        $sClass = $sStylePrefix . '-repost-counter';
+        if($bShowDoRepostAsButtonSmall)
+            $sClass .= ' bx-btn-small-height';
+        if($bShowDoRepostAsButton)
+            $sClass .= ' bx-btn-height';
+
+        return $this->parseHtmlByName('repost_counter.html', [
+            'class' => $sClass,
+            'bx_repeat:attrs' => [
+                ['key' => 'id', 'value' => $this->_oConfig->getHtmlIds('repost', 'counter') . $aEvent['id']],
+                ['key' => 'title', 'value' => _t('_bx_timeline_txt_reposted_by')],
+                ['key' => 'href', 'value' => 'javascript:void(0)'],
+                ['key' => 'onclick', 'value' => 'javascript:' . $sJsObject . '.toggleByPopup(this, ' . $aEvent['id'] . ')']
+            ],
+            'content' => !empty($aEvent['reposts']) && (int)$aEvent['reposts'] > 0 ? $this->getRepostCounterLabel($aEvent['reposts'], $aParams) : '',
+            'script' => $bShowScript ? $this->getRepostJsScript($bDynamicMode) : ''
+        ]);
+    }
+
+    public function getRepostCounterLabel($iCount, $aParams = [])
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        return $this->parseHtmlByName('repost_counter_label.html', [
+            'style_prefix' => $sStylePrefix,
+            'bx_if:show_icon' => array(
+                'condition' => !isset($aParams['show_counter_label_icon']) || (bool)$aParams['show_counter_label_icon'] === true,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'icon' => $this->_getCounterIcon($aParams),
+                )
+            ),
+            'bx_if:show_text' => array(
+                'condition' => !isset($aParams['show_counter_label_text']) || (bool)$aParams['show_counter_label_text'] === true,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'text' => $this->_getCounterLabel($iCount, $aParams)
+                )
+            )
+        ]);
+    }
+
+    public function getRepostedBy($iId)
+    {
+        $aTmplUsers = array();
+        $oModule = $this->getModule();
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $aUserIds = $this->_oDb->getRepostedBy($iId);
+        foreach($aUserIds as $iUserId)
+            $aTmplUsers[] = array(
+                'style_prefix' => $sStylePrefix,
+                'user_unit' => $oModule->getObjectUser($iUserId)->getUnit()
+            );
+
+        bx_alert($this->_oConfig->getName(), 'get_reposted_by', 0, 0, array(
+            'content_id' => $iId,
+            'user_ids' => $aUserIds,
+            'users' => &$aTmplUsers
+        ));
+        
+        if(empty($aTmplUsers))
+            $aTmplUsers = MsgBox(_t('_Empty'));
+
+        return $this->parseHtmlByName('repost_by_list.html', array(
+            'style_prefix' => $sStylePrefix,
+            'bx_repeat:list' => $aTmplUsers
+        ));
+    }
+
+    public function getRepostWith($oForm)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObject('repost');
+
+        return $this->parseHtmlByName('repost_with_popup.html', array(
+            'style_prefix' => $sStylePrefix,
+            'js_object' => $sJsObject,
+            'form' => $oForm->getCode(),
+            'form_id' => $oForm->getId()
+        ));
+    }
+
+    public function getRepostWithFieldReposted($oForm, $aInput)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $aBrowseParams = $this->getModule()->getParams();
+
+        $aReposted = $oForm->getReposted();
+        $sReposted = $this->getPost($aReposted, $aBrowseParams);
+
+        $sContent = '';
+        if(!empty($sReposted))
+            $sContent = $this->_getContent($aReposted['content_type'], $aReposted, $aBrowseParams);
+        else
+            $sContent = MsgBox(_t('_Empty'));
+
+        return $this->parseHtmlByName('repost_with_reposted.html', array(
+            'style_prefix' => $sStylePrefix,
+            'content' => $sContent
+        ));
+    }
+
+    public function getRepostTo($oForm)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObject('repost');
+
+        return $this->parseHtmlByName('repost_to_popup.html', array(
+            'style_prefix' => $sStylePrefix,
+            'js_object' => $sJsObject,
+            'form' => $oForm->getCode(),
+            'form_id' => $oForm->getId()
+        ));
+    }
+
+    public function getRepostToFieldList($oForm, $aInput)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $oConnection = BxDolConnection::getObjectInstance('sys_profiles_subscriptions');
+        if(!$oConnection)
+            return '';
+
+        $iProfileId = bx_get_logged_profile_id();
+        $aConnectedIds = $oConnection->getConnectedContent($iProfileId);
+
+        $aSelected = array();
+        $aTmplVarsModules = array();
+        if(!empty($aConnectedIds) && is_array($aConnectedIds)) {
+            $aCheckbox = $aInput;
+            $aCheckbox['type'] = 'checkbox';
+            $aCheckbox['name'] .= '[]';
+
+            foreach($aConnectedIds as $iConnectedId) {
+                $oProfile = BxDolProfile::getInstanceMagic($iConnectedId);
+                if(!$oProfile)
+                    continue;
+
+                $sProfileModule = $oProfile->getModule();
+                if(!isset($aTmplVarsModules[$sProfileModule]))
+                    $aTmplVarsModules[$sProfileModule] = array(
+                        'style_prefix' => $sStylePrefix,
+                        'title' => _t('_' . $sProfileModule),
+                        'bx_repeat:contexts' => array()
+                    );
+                        
+                $aCheckbox['value'] = $iConnectedId;
+                $aCheckbox['checked'] = in_array($iConnectedId, $aSelected) ? 1 : 0;
+
+                $aTmplVarsModules[$sProfileModule]['bx_repeat:contexts'][] = array(
+                    'style_prefix' => $sStylePrefix,
+                    'checkbox' => $oForm->genInput($aCheckbox),
+                    'unit' => $oProfile->getUnit(0, array('template' => 'unit_wo_cover'))
+                );
+            }
+        }      
+
+        return $this->parseHtmlByName('repost_to_list.html', array(
+            'style_prefix' => $sStylePrefix,
+            'bx_repeat:modules' => !empty($aTmplVarsModules)? array_values($aTmplVarsModules) : MsgBox(_t('_Empty'))
+        ));
+    }
+
+    public function getRepostJsScript($bDynamicMode = false)
+    {
+        $sCode = $this->getJsCode('repost', array(), array('mask' => '{object} = new {class}({params});', 'wrap' => false));
+
+        if($bDynamicMode) {
+            $sJsObject = $this->_oConfig->getJsObject('repost');
+
+            $sCode = "var " . $sJsObject . " = null; 
+            if(typeof(window.BxTimelineRepost) === 'undefined')
+                $.getScript('" . bx_js_string($this->getJsUrl('main.js'), BX_ESCAPE_STR_APOS) . "', function(data, textStatus, jqxhr) {
+                    $.getScript('" . bx_js_string($this->getJsUrl('repost.js'), BX_ESCAPE_STR_APOS) . "', function(data, textStatus, jqxhr) {
+                        bx_get_style('" . bx_js_string($this->getCssUrl('repost.css'), BX_ESCAPE_STR_APOS) . "');
+                        " . $sCode . "
+                    });
+                }); ";
+        }
+        else {
+            $sCode = "var " . $sCode;
+
+            $this->addCss(array('repost.css'));
+            $this->addJs(array('main.js', 'repost.js'));
+        }
+
+        return $this->_wrapInTagJsCode($sCode);
+    }
+
+    public function getRepostJsClick($iOwnerId, $sType, $sAction, $iObjectId)
+    {
+        $aReposted = $this->_oDb->getReposted($sType, $sAction, $iObjectId);
+        if(empty($aReposted) || !is_array($aReposted))
+            return '';
+
+        $sResult = '';
+        $sCommonPrefix = $this->_oConfig->getPrefix('common_post');
+        if(str_replace($sCommonPrefix, '', $sType) == BX_TIMELINE_PARSE_TYPE_REPOST) {
+            $aRepostedData = unserialize($aReposted['content']);
+
+            $sResult = $this->_getRepostJsClick($iOwnerId, $aRepostedData['type'], $aRepostedData['action'], $aRepostedData['object_id']);
+        }
+        else
+            $sResult = $this->_getRepostJsClick($iOwnerId, $sType, $sAction, $iObjectId);
+
+        return $sResult;
+    }
+
+    public function getRepostWithJsClick($iReposterId, $sType, $sAction, $iObjectId)
+    {
+        $aReposted = $this->_oDb->getReposted($sType, $sAction, $iObjectId);
+        if(empty($aReposted) || !is_array($aReposted))
+            return '';
+
+        $sResult = '';
+        $sCommonPrefix = $this->_oConfig->getPrefix('common_post');
+        if(str_replace($sCommonPrefix, '', $sType) == BX_TIMELINE_PARSE_TYPE_REPOST) {
+            $aRepostedData = unserialize($aReposted['content']);
+
+            $sResult = $this->_getRepostWithJsClick($iReposterId, $aRepostedData['type'], $aRepostedData['action'], $aRepostedData['object_id']);
+        }
+        else
+            $sResult = $this->_getRepostWithJsClick($iReposterId, $sType, $sAction, $iObjectId);
+
+        return $sResult;
+    }
+
+    public function getRepostToJsClick($iReposterId, $sType, $sAction, $iObjectId)
+    {
+        $aReposted = $this->_oDb->getReposted($sType, $sAction, $iObjectId);
+        if(empty($aReposted) || !is_array($aReposted))
+            return '';
+
+        $sResult = '';
+        $sCommonPrefix = $this->_oConfig->getPrefix('common_post');
+        if(str_replace($sCommonPrefix, '', $sType) == BX_TIMELINE_PARSE_TYPE_REPOST) {
+            $aRepostedData = unserialize($aReposted['content']);
+
+            $sResult = $this->_getRepostToJsClick($iReposterId, $aRepostedData['type'], $aRepostedData['action'], $aRepostedData['object_id']);
+        }
+        else
+            $sResult = $this->_getRepostToJsClick($iReposterId, $sType, $sAction, $iObjectId);
+
+        return $sResult;
+    }
+
+    public function getAttachLinkForm($iEventId = 0)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObject('post');
+
+        $aForm = $this->getModule()->getFormAttachLink($iEventId);
+
+        return $this->parseHtmlByName('attach_link_form.html', array(
+            'style_prefix' => $sStylePrefix,
+            'js_object' => $sJsObject,
+            'form_id' => $aForm['form_id'],
+            'form' => $aForm['form'],
+        ));
+    }
+
+    public function getAttachLinkField($iUserId, $iEventId = 0)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        if(!$iEventId)
+            $aLinks = $this->_oDb->getUnusedLinks($iUserId);
+        else
+            $aLinks = $this->_oDb->getLinks($iEventId);
+
+        $sLinks = '';
+        foreach($aLinks as $aLink)
+            $sLinks .= $this->getAttachLinkItem($iUserId, $aLink);
+
+        return $this->parseHtmlByName('attach_link_form_field.html', array(
+            'html_id' => $this->_oConfig->getHtmlIds('post', 'attach_link_form_field') . $iEventId,
+            'style_prefix' => $sStylePrefix,
+            'links' => $sLinks
+        ));
+    }
+
+    public function getAttachLinkItem($iUserId, $mixedLink)
+    {
+        $aLink = is_array($mixedLink) ? $mixedLink : $this->_oDb->getLinksBy(array('type' => 'id', 'id' => (int)$mixedLink, 'profile_id' => $iUserId));
+        if(empty($aLink) || !is_array($aLink))
+            return '';
+
+        $sLinkIdPrefix = $this->_oConfig->getHtmlIds('post', 'attach_link_item');
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sClass = $sStylePrefix . '-al-item';
+        $sJsObject = $this->_oConfig->getJsObject('post');
+
+        $oEmbed = BxDolEmbed::getObjectInstance();
+        $bEmbed = $oEmbed !== false;
+
+        $sThumbnail = '';
+        $aLinkAttrs = array();
+        if(!$bEmbed) {
+            $aLinkAttrs = array(
+            	'title' => bx_html_attribute($aLink['title'])
+            );
+            if(!$this->_oConfig->isEqualUrls(BX_DOL_URL_ROOT, $aLink['url'])) {
+                $aLinkAttrs['target'] = '_blank';
+    
+                if($this->_oDb->getParam('sys_add_nofollow') == 'on')
+            	    $aLinkAttrs['rel'] = 'nofollow';
+            }
+
+            if((int)$aLink['media_id'] != 0)
+                $sThumbnail = BxDolTranscoderImage::getObjectInstance($this->_oConfig->getObject('transcoder_photos_preview'))->getFileUrl($aLink['media_id']);
+        }
+        else
+            $sClass .= ' embed';
+
+        return $this->parseHtmlByName('attach_link_item.html', array(
+            'html_id' => $sLinkIdPrefix . $aLink['id'],
+            'style_prefix' => $sStylePrefix,
+            'class' => $sClass,
+            'js_object' => $sJsObject,
+            'id' => $aLink['id'],
+            'bx_if:show_embed_outer' => array(
+                'condition' => $bEmbed,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'embed' => $bEmbed ? $oEmbed->getLinkHTML($aLink['url'], $aLink['title'], 300) : '',
+                )
+            ),
+            'bx_if:show_embed_inner' => array(
+                'condition' => !$bEmbed,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'bx_if:show_thumbnail' => array(
+                        'condition' => !empty($sThumbnail),
+                        'content' => array(
+                            'style_prefix' => $sStylePrefix,
+                            'thumbnail' => $sThumbnail
+                        )
+                    ),
+                    'url' => $aLink['url'],
+                    'link' => $this->parseLink($aLink['url'], $aLink['title'], $aLinkAttrs)
+                )
+            ),
+        ));
+    }
+
+    public function getData(&$aEvent, $aBrowseParams = array())
+    {
+        $aResult = $this->_oConfig->isSystem($aEvent['type'], $aEvent['action']) ? $this->_getSystemData($aEvent, $aBrowseParams) : $this->_getCommonData($aEvent, $aBrowseParams);
+        if(empty($aResult) || empty($aResult['object_owner_id']) || empty($aResult['content']))
+            return false;
+
+        $sSample = !empty($aResult['sample']) ? $aResult['sample'] : '_bx_timeline_txt_sample';
+
+        $aUpdate = array(
+            'object_owner_id' => $aResult['object_owner_id']
+        );
+
+        //--- Update Title if empty.
+        if(empty($aEvent['title'])) {
+            $sTitle = !empty($aResult['title']) ? $this->_oConfig->getTitle($aResult['title']) : _t($sSample);
+
+            $aUpdate['title'] = bx_process_input(strip_tags($sTitle));
+        }
+
+        //--- Update Description if empty.
+        if(empty($aEvent['description'])) {
+            $sUserName = $this->getModule()->getObjectUser($aResult['object_owner_id'])->getDisplayName();
+
+            $sDescription = !empty($aResult['description']) ? $aResult['description'] : _t('_bx_timeline_txt_user_added_sample', $sUserName, _t($sSample));
+            if($sDescription == '' && !empty($aResult['content']['text']))
+                $sDescription = $aResult['content']['text'];
+
+            $aUpdate['description'] = bx_process_input(strip_tags($sDescription));
+        }
+
+        if(!empty($aUpdate) && is_array($aUpdate)) 
+            $this->_oDb->updateEvent($aUpdate, array('id' => $aEvent['id']));
+
+        //--- Update Flags ---//
+        $aContent = &$aResult['content'];
+        $aFlagTypes = $this->_oDb->getEventFlagTypes();
+        foreach($aFlagTypes as $sFt)
+            if((!empty($aContent[$sFt]) && is_array($aContent[$sFt])) || (!empty($aContent[$sFt . '_attach']) && is_array($aContent[$sFt .'_attach'])))
+                $this->_oDb->updateEventFlagsByType($sFt, $aEvent['id']);
+
+        return $aResult;
+    }
+
+    public function getDataCached($aEvent, $aBrowseParams = array())
+    {
+        if(!$this->_oConfig->isCacheItem() || bx_is_api()) 
+            return $this->getData($aEvent, $aBrowseParams);
+
+        /**
+         * For now parameters from $aBrowseParams array aren't used during data retrieving.
+         * If they will then the cache should be created depending on their values.
+         */
+        $sCacheKey = $this->_oConfig->getCacheItemKey($aEvent['id']);
+        $iCacheLifetime = $this->_oConfig->getCacheItemLifetime();
+
+        $oCache = $this->getModule()->getCacheItemObject();
+        $aCached = $oCache->getData($sCacheKey, $iCacheLifetime);
+        if(!empty($aCached)) 
+            return unserialize($aCached);
+
+        $aBrowseParams['dynamic_mode'] = true;
+        $aResult = $this->getData($aEvent, $aBrowseParams);
+        if(empty($aResult) || (isset($aResult['_cache']) && (bool)$aResult['_cache'] === false))
+            return $aResult;
+
+        $oCache->setData($sCacheKey, serialize($aResult), $iCacheLifetime);           
+        return $aResult;
+    }
+
+    public function getVideo($aEvent, $aVideo)
+    {
+        $sVideoId = $this->_oConfig->getHtmlIds('view', 'video') . $aEvent['id'] . '-' . $aVideo['id'];
+        $oPlayer = BxDolPlayer::getObjectInstance();
+        if (!$oPlayer)
+            return '';
+
+        $sPlayer = $oPlayer->getCodeVideo (BX_PLAYER_EMBED, array(
+            'poster' => $aVideo['src_poster'],
+            'mp4' => array('sd' => $aVideo['src_mp4'], 'hd' => $aVideo['src_mp4_hd']),
+            'attrs' => array('id' => $sVideoId),
+        ));
+        return $this->parseHtmlByName('video_player.html', array(
+            'player' => $sPlayer,
+            'html_id' => $sVideoId
+        ));
+    }
+
+    /*
+     * Show only one Live Update notification for all new Events.
+     */
+    function getLiveUpdate($aBrowseParams, $iProfileId, $iCountOld = 0, $iCountNew = 0)
+    {
+        $oModule = $this->getModule();
+
+    	$iCount = (int)$iCountNew - (int)$iCountOld;
+    	if($iCount < 0)
+            return '';
+
+        $aParams = $oModule->getParamsExt($aBrowseParams);
+        $aParams['start'] = 0;
+        $aParams['per_page'] = 1;
+        $aParams['newest'] = true;
+        $aParams['filter'] = BX_TIMELINE_FILTER_OTHER_VIEWER;
+        $aEvents = $this->_oDb->getEvents($aParams);
+        if(empty($aEvents) || !is_array($aEvents))
+            return '';
+
+        $aEvent = array_shift($aEvents);
+        if(empty($aEvent) || !is_array($aEvent))
+            return '';
+
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        return $this->parseHtmlByName('live_update_button.html', array(
+            'style_prefix' => $sStylePrefix,
+            'html_id' => $this->_oConfig->getHtmlIds('view', 'live_update_popup') . $aBrowseParams['type'],
+            'onclick_show' => "javascript:" . $sJsObject . ".goToBtn(this, 'timeline-event-" . $aEvent['id'] . "', '" . $aEvent['id'] . "');",
+        ));
+    }
+
+    /*
+     * Show Live Update notification separately for each new Event. Popup Chain is used here.
+     * 
+     * Note. This way to display live update notifications isn't used for now. 
+     * See BxTimelineTemplate::getLiveUpdate method instead.
+     */
+    function getLiveUpdates($aBrowseParams, $iProfileId, $iCountOld = 0, $iCountNew = 0)
+    {
+        $bShowAll = true;
+        $bShowActions = false;
+        $oModule = $this->getModule();
+
+    	$iCount = (int)$iCountNew - (int)$iCountOld;
+    	if($iCount < 0)
+            return '';
+
+        $iCountMax = $this->_oConfig->getLiveUpdateLength();
+        if($iCount > $iCountMax)
+            $iCount = $iCountMax;
+
+        $aParams = $oModule->getParamsExt($aBrowseParams);
+        $aParams['start'] = 0;
+        $aParams['per_page'] = $iCount;
+        $aParams['filter'] = BX_TIMELINE_FILTER_OTHER_VIEWER;
+        $aEvents = $this->_oDb->getEvents($aParams);
+        if(empty($aEvents) || !is_array($aEvents))
+            return '';
+
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $iUserId = $oModule->getUserId();
+        $bModerator = $oModule->isModerator();
+
+        $aEvents = array_reverse($aEvents);
+        $iEvents = count($aEvents);
+
+        $aTmplVarsItems = array();
+        foreach($aEvents as $iIndex => $aEvent) {
+            $aData = $this->getDataCached($aEvent);
+            if($aData === false)
+                continue;
+
+            $iEventId = $aEvent['id'];
+            $iEventAuthorId = $this->_oConfig->isSystem($aEvent['type'], $aEvent['action']) ? (int)$aEvent['owner_id'] : (int)$aEvent['object_id'];
+            if($iEventAuthorId < 0) {
+                if(abs($iEventAuthorId) == $iUserId)
+                    continue;
+                else if($bModerator)
+                    $iEventAuthorId *= -1;
+            }
+
+            $oAuthor = $oModule->getObjectUser($iEventAuthorId);
+            $sAuthorName = $oAuthor->getDisplayName();
+
+            $aTmplVarsItems[] = array(
+                'bx_if:show_as_hidden' => array(
+                    'condition' => !$bShowAll && $iIndex < ($iEvents - 1),
+                    'content' => array(),
+                ),
+                'item' => $this->parseHtmlByName('live_update_notification.html', array(
+                    'style_prefix' => $sStylePrefix,
+                    'onclick_show' => "javascript:" . $sJsObject . ".goTo(this, 'timeline-event-" . $iEventId . "', '" . $iEventId . "');",
+                    'author_link' => $oAuthor->getUrl(), 
+                    'author_title' => bx_html_attribute($sAuthorName),
+                    'author_name' => $sAuthorName,
+                    'author_unit' => $oAuthor->getUnit(0, array('template' => 'unit_wo_info_links')), 
+                    'text' => _t($aData['sample_action'], _t($aData['sample'])),
+                )),
+                'bx_if:show_previous' => array(
+                    'condition' => $bShowActions && $iIndex > 0,
+                    'content' => array(
+                        'onclick_previous' => $sJsObject . '.previousLiveUpdate(this)'
+                    )
+                ),
+                'bx_if:show_close' => array(
+                    'condition' => $bShowActions,
+                    'content' => array(
+                        'onclick_close' => $sJsObject . '.hideLiveUpdate(this)'
+                    )
+                )
+            );
+        }
+
+        return $this->parseHtmlByName('popup_chain.html', array(
+            'html_id' => $this->_oConfig->getHtmlIds('view', 'live_update_popup') . $aBrowseParams['type'],
+            'bx_repeat:items' => $aTmplVarsItems
+        ));
+    }
+
+    protected function _getPost($sType, $aEvent, $aBrowseParams = array())
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $oModule = $this->getModule();
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aBrowseParams);
+
+        $bViewItem = isset($aBrowseParams['view']) && $aBrowseParams['view'] == BX_TIMELINE_VIEW_ITEM;
+        $bViewOutline = isset($aBrowseParams['view']) && $aBrowseParams['view'] == BX_TIMELINE_VIEW_OUTLINE;
+
+        $oAuthor = $oModule->getObjectUser($aEvent['object_owner_id']);
+        $sAuthorName = $oAuthor->getDisplayName(); 
+        $sAuthorUrl = $oAuthor->getUrl();
+        $sAuthorUnit = $oAuthor->getUnit(0, array('template' => 'unit_wo_info'));
+        $sAuthorBadges = $oAuthor->getBadges();
+        $sAuthorAction = '';
+
+        if(!empty($aEvent['sample_action_custom']) && is_array($aEvent['sample_action_custom'])) {
+            $aAuthorAction = $aEvent['sample_action_custom'];
+
+            foreach($aAuthorAction['markers'] as $iIndex => $sMarker)
+                if(get_mb_substr($sMarker, 0, 1) == '_')
+                    $aAuthorAction['markers'][$iIndex] = _t($sMarker);
+
+            $sAuthorAction = bx_replace_markers(_t($aAuthorAction['content']), $aAuthorAction['markers']);
+        }
+        else
+            $sAuthorAction = _t($aEvent['sample_action'], _t($aEvent['sample']));
+
+        if(($bViewItem || $this->_oConfig->isCountAllViews()) && !empty($aEvent['views']) && is_array($aEvent['views']) && isset($aEvent['views']['system']))
+            $oModule->getViewObject($aEvent['views']['system'], $aEvent['views']['object_id'])->doView();
+
+        $aTmplVarsNote = $this->_getTmplVarsNote($aEvent);
+        $aTmplVarsMenuItemCounters = $this->_getTmplVarsMenuItemCounters($aEvent, $aBrowseParams);
+        $aTmplVarsMenuItemActions = $this->_getTmplVarsMenuItemActions($aEvent, $aBrowseParams);
+        $aTmplVarsMenuItemMeta = $this->_getTmplVarsMenuItemMeta($aEvent, $aBrowseParams);
+
+        $aTmplVarsManage = $this->_getTmplVarsManage($aEvent, $aBrowseParams);
+
+        $aTmplVarsTimelineOwner = $this->_getTmplVarsTimelineOwner($aEvent);
+        $bTmplVarsTimelineOwner = !empty($aTmplVarsTimelineOwner);
+
+        $aTmplVarsOwnerActions = $this->_getTmplVarsOwnerActions($aEvent, $aBrowseParams);
+        $bTmplVarsOwnerActions = !empty($aTmplVarsOwnerActions); 
+
+        $bPinned = $aBrowseParams['type'] == BX_BASE_MOD_NTFS_TYPE_OWNER && (int)$aEvent['pinned'] > 0;
+        $bSticked = (int)$aEvent['sticked'] > 0;
+        $bPromoted = (int)$aEvent['promoted'] > 0;
+
+        $sClass = $sStylePrefix . '-view-sizer';
+        if($bViewOutline) {
+            $sClass = $sStylePrefix . '-grid-item-sizer';
+            if($bPinned || $bSticked || $bPromoted) {
+                $sClass .= ' ' . $sStylePrefix . '-gis';
+
+            if($bPinned)
+                $sClass .= '-pnd';
+            if($bSticked)
+                $sClass .= '-psd';
+            if($bPromoted)
+                $sClass .= '-pmd';
+            }
+        }
+
+        $sClass .= ' ' . $aEvent['type'] . (!empty($aEvent['action']) ? ' ' . $aEvent['action'] : '');
+        $aAuthorAcl = BxDolAcl::getInstance()->getMemberMembershipInfo($aEvent['object_owner_id']);
+        if(!empty($aAuthorAcl) && isset($this->_aAclId2Name[$aAuthorAcl['id']]))
+            $sClass .= ' ' . $sStylePrefix . '-aml-' . $this->_aAclId2Name[$aAuthorAcl['id']];
+
+        if(!empty($aBrowseParams['blink']) && in_array($aEvent['id'], $aBrowseParams['blink']))
+            $sClass .= ' ' . $sStylePrefix . '-blink';
+        if($bPinned)
+            $sClass .= ' ' . $sStylePrefix . '-pinned';
+        if($bSticked)
+            $sClass .= ' ' . $sStylePrefix . '-sticked';
+        if($bPromoted)
+            $sClass .= ' ' . $sStylePrefix . '-promoted';
+
+        $sClassOwner = $bTmplVarsOwnerActions ? $sStylePrefix . '-io-with-actions' : '';
+
+        $oMetatags = BxDolMetatags::getObjectInstance($this->_oConfig->getObject('metatags'));
+        $sLocation = $oMetatags->locationsString($aEvent['id']);
+ 
+        $sFields = '';
+        if(!$bViewItem) {
+            $oForm = BxDolForm::getObjectInstance($this->_oConfig->getObject('form_post'), $this->_oConfig->getObject('form_display_post_view'), $this, $aEvent['id']);
+            $oForm->initChecker($aEvent);
+
+            if(!empty($oForm->aInputs) && is_array($oForm->aInputs))
+                foreach($oForm->aInputs as $aInput)
+                    if($aInput['type'] != 'hidden' && !empty($aInput['value'])) {
+                        $sFields = $oForm->getCode();
+                        break;
+                    }
+        }
+
+        $sIcon = !empty($aEvent['icon']) ? $aEvent['icon'] : $CNF['ICON'];
+
+        $aTmplVars = array (
+            'style_prefix' => $sStylePrefix,
+            'js_object' => $sJsObject,
+            'html_id' => $this->_oConfig->getHtmlIdView('item', $aBrowseParams, array('whole' => false)) . $aEvent['id'],
+            'class' => $sClass,
+            'class_owner' => $sClassOwner,
+            'class_content' => $bViewItem ? 'bx-def-color-bg-block' : 'bx-def-color-bg-box',
+            'id' => $aEvent['id'],
+            'bx_if:show_note' => array(
+                'condition' => !empty($aTmplVarsNote),
+                'content' => $aTmplVarsNote
+            ),
+            'bx_if:show_owner_actions' => array(
+                'condition' => $bTmplVarsOwnerActions,
+                'content' => $aTmplVarsOwnerActions
+            ),
+            'item_icon' => $sIcon,
+            'item_owner_url' => $sAuthorUrl,
+            'item_owner_title' => bx_html_attribute($sAuthorName),
+            'item_owner_name' => $sAuthorName,
+            'item_owner_badges' => $sAuthorBadges,
+            'item_owner_unit' => $sAuthorUnit,
+            'item_view_url' => $this->_oConfig->getItemViewUrl($aEvent),
+            'item_date' => bx_time_js($aEvent['date']),
+            'bx_if:show_pinned' => array(
+            	'condition' => $bPinned,
+            	'content' => array(
+                    'style_prefix' => $sStylePrefix,
+            	)
+            ),
+            'bx_if:show_sticked' => array(
+            	'condition' => $bSticked,
+            	'content' => array(
+                    'style_prefix' => $sStylePrefix,
+            	)
+            ),
+            'bx_if:show_hot' => array(
+            	'condition' => $this->_oConfig->isHotEvent($aEvent['id']),
+            	'content' => array(
+                    'style_prefix' => $sStylePrefix,
+            	)
+            ),
+            'bx_if:show_manage' => array(
+                'condition' => !empty($aTmplVarsManage),
+                'content' => $aTmplVarsManage
+            ),
+            'bx_if:show_item_action' => array(
+                'condition' => $this->_oConfig->isSystem($aEvent['type'], $aEvent['action']) || $bTmplVarsTimelineOwner,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'item_icon' => $sIcon,
+                    'item_owner_action' => $sAuthorAction,
+                    'bx_if:show_timeline_owner' => array(
+                        'condition' => $bTmplVarsTimelineOwner,
+                        'content' => $aTmplVarsTimelineOwner
+                    ),
+                )
+            ),
+            'content_type' => $sType,
+            'content' => is_string($aEvent['content']) ? $aEvent['content'] : $this->_getContent($sType, $aEvent, $aBrowseParams),
+            'bx_if:show_location' => array(
+            	'condition' => !empty($sLocation),
+            	'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'location' => $sLocation
+            	)
+            ),
+            'bx_if:show_fields' => array(
+                'condition' => !empty($sFields),
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'fields' => $sFields
+            	)
+            ),
+            'bx_if:show_menu_item_counters' => array(
+                'condition' => !empty($aTmplVarsMenuItemCounters),
+                'content' => $aTmplVarsMenuItemCounters
+            ),
+            'bx_if:show_menu_item_actions' => array(
+                'condition' => !empty($aTmplVarsMenuItemActions),
+                'content' => $aTmplVarsMenuItemActions
+            ),
+            'bx_if:show_menu_item_meta' => array(
+                'condition' => !empty($aTmplVarsMenuItemMeta),
+                'content' => $aTmplVarsMenuItemMeta
+            ),
+            'comments' => '',
+            'line_number' => isset($aEvent['index']) ? $aEvent['index'] : 0
+        );
+
+        $iPreloadComments = $this->_oConfig->getPreloadComments();
+        if($iPreloadComments > 0 && $oModule->isAllowedComment($aEvent) && in_array($aBrowseParams['view'], array(BX_TIMELINE_VIEW_TIMELINE, BX_TIMELINE_VIEW_OUTLINE)))
+            $aTmplVars['comments'] = $this->_getComments($aEvent['comments'], array_merge($aBrowseParams, array(
+                'cmts_preload_number' => $iPreloadComments,
+                'cmts_min_post_form' => false
+            )));       
+
+        $sVariable = '_sTmplContentItem' . bx_gen_method_name($aBrowseParams['view']);
+        if(empty(self::$$sVariable))
+            self::$$sVariable = $this->getHtml('item_' . $aBrowseParams['view'] . '.html');
+
+        $sTmplCode = self::$$sVariable;
+        bx_alert($this->_oConfig->getName(), 'get_post', 0, 0, array(
+            'type' => $sType,
+            'event' => $aEvent,
+            'browse_params' => $aBrowseParams,
+            'tmpl_code' => &$sTmplCode,
+            'tmpl_vars' => &$aTmplVars
+        ));
+
+        return $this->parseHtmlByContent($sTmplCode, $aTmplVars);
+    }
+
+    public function _getPostApi(&$aEvent, $aParams = [], $aEventAdd = [])
+    {
+        $oModule = $this->getModule();
+
+        $aEvent['author_data'] = BxDolProfile::getData($aEvent['object_owner_id']);
+        $aEvent['author_actions'] = [];
+
+        $iInitiatorProfile = (int)bx_get_logged_profile_id();
+        $iContentProfile = (int)$aEvent['object_owner_id'];
+
+        $sConnection = 'sys_profiles_subscriptions';      
+        if(($oConnection = BxDolConnection::getObjectInstance($sConnection)) !== false && $iInitiatorProfile != $iContentProfile && !$oConnection->isConnected($iInitiatorProfile, $iContentProfile)) {
+            $aEvent['author_actions'][] = [
+                'type' => 'connections',
+                'o' => $sConnection,
+                'a' => 'add',
+                'iid' => $iInitiatorProfile,
+                'cid' => $iContentProfile,
+                'title' => _t('_sys_menu_item_title_sm_subscribe'),
+            ];
+        }
+
+        $aEvent['url'] = bx_ltrim_str($aEvent['content']['url'], BX_DOL_URL_ROOT);
+
+        if(empty($aEventAdd['menu_actions'])) {
+            $oMenuActions = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_actions_all'));
+            if(!$oMenuActions)
+                $oMenuActions = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_actions'));
+        }
+        else
+            $oMenuActions = $aEventAdd['menu_actions'];
+
+        if($oMenuActions !== false) {
+            $oMenuActions->setEvent($aEvent, $aParams);
+
+            $aEvent['menu_actions'] = $oMenuActions->getCodeAPI();
+        }
+
+        if(empty($aEventAdd['menu_manage']))
+            $oMenuManage = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_manage'));
+        else
+            $oMenuManage = $aEventAdd['menu_manage'];
+
+        if($oMenuManage !== false) {
+            $oMenuManage->setEvent($aEvent);
+
+            $aEvent['menu_manage'] = $oMenuManage->getCodeAPI();
+        }
+
+        $aCmts = [];
+        $oCmts = $oModule->getCmtsObject($aEvent['comments']['system'], $aEvent['comments']['object_id']);
+        if($oCmts !== false) {
+            $aCmtsParams = ['mode' => 'feed', 'order_way' => 'desc', 'start_from' => 0, 'per_view' => 1];
+            $aCmts = bx_srv('system', 'get_comments_api', [$oCmts, $aCmtsParams], 'TemplCmtsServices');
+
+            $aEvent['cmts'] = $aCmts;
+            $aEvent['cmts']['count'] = $aEvent['comments']['count'];
+        }
+
+        return $aEvent;
+    }
+
+    protected function _getContent($sType, $aEvent, $aBrowseParams = array())
+    {
+        $sMethod = '_getTmplVarsContent' . ucfirst($sType);
+        if(!method_exists($this, $sMethod))
+            return '';
+
+        $aTmplVars = $this->$sMethod($aEvent, $aBrowseParams);
+
+        $sVariable = '_sTmplContentType' . bx_gen_method_name($sType);
+        if(empty(self::$$sVariable))
+            self::$$sVariable = $this->getHtml('type_' . $sType . '.html');
+
+        $sTmplCode = self::$$sVariable;
+        bx_alert($this->_oConfig->getName(), 'get_post_content', 0, 0, array(
+            'type' => $sType,
+            'event' => $aEvent,
+            'browse_params' => $aBrowseParams,
+            'tmpl_code' => &$sTmplCode,
+            'tmpl_vars' => &$aTmplVars
+        ));
+
+        return $this->parseHtmlByContent($sTmplCode, $aTmplVars);
+    }
+
+    protected function _getComments($aComments, $aBrowseParams = array())
+    {
+        
+        $mixedComments = $this->getModule()->getCommentsData($aComments);
+        if($mixedComments === false)
+            return '';
+
+        list($sSystem, $iObjectId, $iCount) = $mixedComments;
+        return $this->getComments($sSystem, $iObjectId, $aBrowseParams);
+    }
+
+    protected function _getShowMore($aParams)
+    {
+        return $this->parseHtmlByName('show_more.html', array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'js_object' => $this->_oConfig->getJsObjectView($aParams),
+        ));
+    }
+
+    protected function _getImagePopup($aParams)
+    {
+        $sViewImagePopupId = $this->_oConfig->getHtmlIdView('photo_popup', $aParams);
+        $sViewImagePopupContent = $this->parseHtmlByName('popup_image.html', array(
+            'image_url' => ''
+    	));
+
+    	return BxTemplFunctions::getInstance()->transBox($sViewImagePopupId, $sViewImagePopupContent, true);
+    }
+
+    protected function _getRepostJsClick($iOwnerId, $sType, $sAction, $iObjectId)
+    {
+        $sJsObject = $this->_oConfig->getJsObject('repost');
+        $sFormat = "%s.repostItem(this, %d, '%s', '%s', %d);";
+
+        $iOwnerId = !empty($iOwnerId) ? (int)$iOwnerId : $this->getModule()->getUserId(); //--- in whose timeline the content will be reposted
+        return sprintf($sFormat, $sJsObject, $iOwnerId, $sType, $sAction, (int)$iObjectId);
+    }
+
+    protected function _getRepostWithJsClick($iReposterId, $sType, $sAction, $iObjectId)
+    {
+        $sJsObject = $this->_oConfig->getJsObject('repost');
+        $sFormat = "%s.repostItemWith(this, %d, '%s', '%s', %d);";
+
+        $iReposterId = !empty($iReposterId) ? (int)$iReposterId : $this->getModule()->getUserId();
+        return sprintf($sFormat, $sJsObject, $iReposterId, $sType, $sAction, (int)$iObjectId);
+    }
+
+    protected function _getRepostToJsClick($iReposterId, $sType, $sAction, $iObjectId)
+    {
+        $sJsObject = $this->_oConfig->getJsObject('repost');
+        $sFormat = "%s.repostItemTo(this, %d, '%s', '%s', %d);";
+
+        $iReposterId = !empty($iReposterId) ? (int)$iReposterId : $this->getModule()->getUserId();
+        return sprintf($sFormat, $sJsObject, $iReposterId, $sType, $sAction, (int)$iObjectId);
+    }
+
+    protected function _getJumpToList($aParams)
+    {
+        $iYearSel = (int)$aParams['timeline'];
+        $iYearMin = $this->_oDb->getMaxDuration($aParams);      
+        if(empty($iYearMin))
+            return '';
+
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+
+        $aYears = array();
+        $iYearMax = date('Y', time()) - 1;
+        for($i = $iYearMax; $i >= $iYearMin; $i--) {
+            $bCurrent = $i == $iYearSel;
+            $aYears[] = array(
+                'style_prefix' => $sStylePrefix,
+                'bx_if:show_link' => array(
+                    'condition' => !$bCurrent,
+                    'content' => array(
+                        'title' => _t('_bx_timeline_txt_jump_to_n_year', $i),
+                        'onclick' => 'javascript:' . $sJsObject . '.changeTimeline(this, \'' . $i . '-12-31\')',
+                        'content' => $i
+                    )
+                ),
+                'bx_if:show_text' => array(
+                    'condition' => $bCurrent,
+                    'content' => array(
+                        'content' => $i
+                    )
+                ),
+            );
+        }
+
+        return $this->parseHtmlByName('jump_to.html', array(
+            'bx_if:show_list' => array(
+                'condition' => true,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'bx_repeat:links' => $aYears,
+                )
+            ),
+            'bx_if:show_calendar' => array(
+                'condition' => false,
+                'content' => array()
+            )
+        ));
+    }
+
+    protected function _getJumpToCaledar($aParams)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aParams);
+                
+        return $this->parseHtmlByName('jump_to.html', array(
+            'bx_if:show_list' => array(
+                'condition' => false,
+                'content' => array()
+            ),
+            'bx_if:show_calendar' => array(
+                'condition' => true,
+                'content' => array(
+                    'style_prefix' => $sStylePrefix,
+                    'onclick' => 'javascript:' . $sJsObject . '.showCalendar(this)',
+                )
+            )
+        ));
+    }
+
+    protected function _getTmplVarsMenuItemActions(&$aEvent, $aBrowseParams = array())
+    {
+        $oMenu = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_actions_all'));
+        if(!$oMenu) {
+            $oMenu = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_actions'));
+            if(!$oMenu)
+                return array();
+        }
+
+        $oMenu->setEvent($aEvent, $aBrowseParams);
+        $oMenu->setDynamicMode(isset($aBrowseParams['dynamic_mode']) && $aBrowseParams['dynamic_mode'] === true);
+
+        $sMenu = $oMenu->getCode();
+        if(empty($sMenu))
+            return array();
+
+        return array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'js_object' => $this->_oConfig->getJsObjectView($aBrowseParams),
+            'menu_item_actions' => $sMenu
+        );
+    }
+
+    protected function _getTmplVarsManage(&$aEvent, $aBrowseParams = array())
+    {
+        $oMenu = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_actions_all'));
+        if($oMenu)
+            return array();
+
+        $oMenu = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_manage'));
+        if(!$oMenu)
+            return array();
+
+        $oMenu->setEvent($aEvent, $aBrowseParams);
+        $oMenu->setDynamicMode(isset($aBrowseParams['dynamic_mode']) && $aBrowseParams['dynamic_mode'] === true);
+        if(!$oMenu->isVisible())
+            return array();
+
+        $sOnclick = "bx_menu_popup('bx_timeline_menu_item_manage', this, {'id':'bx_timeline_menu_item_manage_{content_id}'}, {content_id:{content_id}, name:'{name}', view:'{view}', type:'{type}'});";
+        $sOnclick = bx_replace_markers($sOnclick, array(
+            'content_id' => (int)$aEvent['id'],            
+            'view' => bx_process_output($aBrowseParams['view']),
+            'type' => bx_process_output($aBrowseParams['type']),
+            'name' => bx_process_output($aBrowseParams['name']),
+        ));
+
+        return array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'onclick' => $sOnclick
+        );
+    }
+
+    protected function _getTmplVarsMenuItemCounters(&$aEvent, $aBrowseParams = array())
+    {
+        $oMenu = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_counters'));
+        if(!$oMenu)
+            return array();
+
+        $oMenu->setEvent($aEvent, $aBrowseParams);
+        $oMenu->setDynamicMode(isset($aBrowseParams['dynamic_mode']) && $aBrowseParams['dynamic_mode'] === true);
+        $sMenu = $oMenu->getCode();
+        if(empty($sMenu))
+            return array();
+
+        return array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'js_object' => $this->_oConfig->getJsObjectView($aBrowseParams),
+            'menu_item_counters' => $sMenu
+        );
+    }
+
+    protected function _getTmplVarsMenuItemMeta(&$aEvent, $aBrowseParams = array())
+    {
+        $oMenu = BxDolMenu::getObjectInstance($this->_oConfig->getObject('menu_item_meta'));
+        if(!$oMenu)
+            return array();
+
+        $oMenu->setEvent($aEvent);
+
+        $sMenu = $oMenu->getCode();
+        if(empty($sMenu))
+            return array();
+
+        return array(
+            'style_prefix' => $this->_oConfig->getPrefix('style'),
+            'meta' => $sMenu
+        );
+    }
+
+    protected function _getTmplVarsTimelineOwner(&$aEvent)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $iUser = bx_get_logged_profile_id();
+        $oModule = $this->getModule();
+
+        $sConnection = $this->_oConfig->getObject('conn_subscriptions');
+        $oConnection = BxDolConnection::getObjectInstance($sConnection);
+        $sConnectionTitle = _t('_sys_menu_item_title_sm_subscribe');
+
+        $sKeyOwnerId = isset($aEvent['owner_id_grouped']) ? 'owner_id_grouped' : 'owner_id';
+        $aOwnerIds = is_array($aEvent[$sKeyOwnerId]) ? $aEvent[$sKeyOwnerId] : array($aEvent[$sKeyOwnerId]);
+
+        $aTmplVarsOwners = array();
+        foreach($aOwnerIds as $iOwnerId) {
+            $iOwnerId = (int)$iOwnerId;
+            $iObjectOwner = (int)$aEvent['object_owner_id'];
+            if($iObjectOwner < 0 && abs($iObjectOwner) == $iUser)
+                $iObjectOwner *= -1;
+
+            if($iOwnerId == 0 || $iOwnerId == $iObjectOwner)
+                continue;
+
+            $oOwner = $oModule->getObjectUser($iOwnerId);
+            $sToType = $oOwner->getModule();
+            $sToName = $oOwner->getDisplayName();
+            $sToUrl = $oOwner->getUrl();
+
+            $aTmplVarsActions = array();
+            if(!empty($iUser) && $iUser != $iOwnerId && $oConnection->checkAllowedConnect($iUser, $iOwnerId) === CHECK_ACTION_RESULT_ALLOWED) {
+                $aTmplVarsActions[] = array(
+                    'href' => "javascript:void(0)",
+                    'onclick' => "bx_conn_action(this, '" . $sConnection . "', 'add', '" . $iOwnerId . "')",
+                    'title' => bx_html_attribute($sConnectionTitle),
+                    'content' => $sConnectionTitle,
+                    'icon' => 'check'
+                );
+            }
+
+            $aTmplVarsOwners[] =  array(
+                'style_prefix' => $sStylePrefix,
+                'owner_type' => _t('_' . $sToType),
+                'owner_url' => $sToUrl,
+                'owner_username' => $sToName,
+                'bx_if:show_timeline_owner_actions' => array(
+                    'condition' => !empty($aTmplVarsActions),
+                    'content' => array(
+                        'style_prefix' => $sStylePrefix,
+                        'bx_repeat:timeline_owner_actions' => $aTmplVarsActions
+                    )
+                )
+            );
+        }
+
+        if(empty($aTmplVarsOwners))
+            return array();
+
+        return array(
+            'style_prefix' => $sStylePrefix,
+            'bx_repeat:owners' => $aTmplVarsOwners
+        );
+    }
+
+    protected function _getTmplVarsContentPost(&$aEvent, $aBrowseParams = array())
+    {
+    	$aContent = &$aEvent['content'];
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $bViewItem = isset($aBrowseParams['view']) && $aBrowseParams['view'] == BX_TIMELINE_VIEW_ITEM;
+        $bViewSearch = isset($aBrowseParams['view']) && $aBrowseParams['view'] == BX_TIMELINE_VIEW_SEARCH;
+
+        //--- Process Top Raw ---//
+        $sTopRaw = isset($aContent['top_raw']) ? $aContent['top_raw'] : '';
+
+        //--- Process Raw ---//
+        $sRaw = isset($aContent['raw']) ? $aContent['raw'] : '';
+
+        //--- Process Text ---//
+        $sUrl = isset($aContent['url']) ? bx_html_attribute($aContent['url']) : '';
+        $sTitle = '';
+        if(isset($aContent['title']))
+            $sTitle = bx_process_output($aContent['title']);
+
+        if(!empty($sUrl) && !empty($sTitle))
+            $sTitle = $this->parseLink($sUrl, $sTitle, array(
+            	'class' => $sStylePrefix . '-title bx-lnk-src',
+                'title' => $sTitle
+            ));
+
+        $sMethodPrepare = '_prepareTextForOutput';
+        if($this->_oConfig->isBriefCards() && !$bViewItem)
+            $sMethodPrepare .= 'BriefCard';
+
+        $sText = isset($aContent['text']) ? $aContent['text'] : '';
+        $sText = $this->$sMethodPrepare($sText, $aEvent['id']);
+
+        //--- Process Links ---//
+        $bAddNofollow = $this->_oDb->getParam('sys_add_nofollow') == 'on';
+
+        $aTmplVarsLinks = [];
+        if(!empty($aContent['links']))
+            foreach($aContent['links'] as $aLink) {
+                $sLink = '';
+
+                $oEmbed = BxDolEmbed::getObjectInstance();
+                if ($oEmbed) {
+                    $sLink = $this->parseHtmlByName('link_embed_provider.html', array(
+                        'style_prefix' => $sStylePrefix,
+                        'embed' => $oEmbed->getLinkHTML($aLink['url'], $aLink['title']),
+                    ));
+                }
+                else {
+                    $aLinkAttrs = array(
+                    	'title' => $aLink['title']
+                    );
+                    if(!$this->_oConfig->isEqualUrls(BX_DOL_URL_ROOT, $aLink['url'])) {
+                        $aLinkAttrs['target'] = '_blank';
+    
+                        if($bAddNofollow)
+                    	    $aLinkAttrs['rel'] = 'nofollow';
+                    }
+
+                    $sLinkAttrs = '';
+                    foreach($aLinkAttrs as $sKey => $sValue)
+                        $sLinkAttrs .= ' ' . $sKey . '="' . bx_html_attribute($sValue) . '"';
+
+                    $sLink = $this->parseHtmlByName('link_embed_common.html', array(
+                        'bx_if:show_thumbnail' => [
+                            'condition' => !empty($aLink['thumbnail']),
+                            'content' => [
+                                'style_prefix' => $sStylePrefix,
+                                'thumbnail' => $aLink['thumbnail'],
+                                'link' => !empty($aLink['url']) ? $aLink['url'] : 'javascript:void(0)',
+                                'attrs' => $sLinkAttrs
+                            ]
+                        ],
+                        'link' => !empty($aLink['url']) ? $aLink['url'] : 'javascript:void(0)',
+                        'attrs' => $sLinkAttrs,
+                        'content' => $aLink['title'],
+                        'bx_if:show_text' => [
+                            'condition' => !empty($aLink['text']),
+                            'content' => [
+                                'style_prefix' => $sStylePrefix,
+                                'text' => $aLink['text']
+                            ]
+                        ]
+                    ));
+                }
+
+                $aTmplVarsLinks[] = [
+                    'style_prefix' => $sStylePrefix,
+                    'link' => $sLink
+                ];
+            }
+
+        /*
+         * View Item page and Snippet in Search Results should use Gallery layout.
+         */
+        $sAttachmentsLayout = $this->_oConfig->getAttachmentsLayout();
+        if($bViewItem || $bViewSearch)
+            $sAttachmentsLayout = BX_TIMELINE_ML_GALLERY;
+
+        $iAttachmentsTotal = 0;
+        $aTmplVarsImages = $aTmplVarsVideos = $aTmplVarsFiles = $aTmplVarsAttachments = $aTmplVarsAttachmentsFiles = array();
+
+        //--- Process Photos ---//
+        $bImages = !empty($aContent['images']) && is_array($aContent['images']);
+        if($bImages) {
+            $aImages = $this->_getTmplVarsImages($aContent['images'], true, $aEvent, $aBrowseParams);
+            if(!empty($aImages))
+                $aTmplVarsImages = array(
+                    'style_prefix' => $sStylePrefix,
+                    'display' => $aImages['display'],
+                    'bx_repeat:items' => $aImages['items']
+                );
+        }
+        $bImagesAttach = !empty($aContent['images_attach']) && is_array($aContent['images_attach']);
+        if($bImagesAttach) {
+            $aImagesAttach = $this->_getTmplVarsImages($aContent['images_attach'], ['layout' => $sAttachmentsLayout, 'first' => empty($aTmplVarsAttachments)], $aEvent, $aBrowseParams);
+            if(!empty($aImagesAttach)) {
+                $iAttachmentsTotal += $aImagesAttach['total'];
+                $aTmplVarsAttachments = array_merge($aTmplVarsAttachments, $aImagesAttach['items']);
+            }
+        }
+
+        //--- Add Meta Image when Item is viewed on a separate page ---//
+        if($bViewItem) {
+            $sMetaImageSrc = '';
+            if($bImages && !empty($aContent['images'][0]['src']))
+                $sMetaImageSrc = $aContent['images'][0]['src'];
+            else if($bImagesAttach && !empty($aContent['images_attach'][0]['src']))
+                $sMetaImageSrc = $aContent['images_attach'][0]['src'];
+
+            if(!empty($sMetaImageSrc))
+                BxDolTemplate::getInstance()->addPageMetaImage($sMetaImageSrc);
+        }
+
+    	//--- Process Videos ---//
+        $bVideos = !empty($aContent['videos']) && is_array($aContent['videos']);
+        if($bVideos) {
+            $aVideos = $this->_getTmplVarsVideos($aContent['videos'], true, $aEvent, $aBrowseParams);
+            if(!empty($aVideos))
+                $aTmplVarsVideos = [
+                    'style_prefix' => $sStylePrefix,
+                    'display' => $aVideos['display'],
+                    'bx_repeat:items' => $aVideos['items']
+                ];
+        }
+
+        $bVideosAttach = !empty($aContent['videos_attach']) && is_array($aContent['videos_attach']);
+        if($bVideosAttach) {
+            $aVideosAttach = $this->_getTmplVarsVideos($aContent['videos_attach'], ['layout' => $sAttachmentsLayout, 'first' => empty($aTmplVarsAttachments)], $aEvent, $aBrowseParams);
+            if(!empty($aVideosAttach)) {
+                $iAttachmentsTotal += $aVideosAttach['total'];
+                $aTmplVarsAttachments = array_merge($aTmplVarsAttachments, $aVideosAttach['items']);
+            }
+        }
+ 
+        //--- Process Files ---//
+        $bFiles = !empty($aContent['files']) && is_array($aContent['files']);
+        if($bFiles) {
+            $aFiles = $this->_getTmplVarsFiles($aContent['files'], $aEvent, $aBrowseParams);
+            if(!empty($aFiles))
+                $aTmplVarsFiles = [
+                    'style_prefix' => $sStylePrefix,
+                    'display' => $aFiles['display'],
+                    'bx_repeat:items' => $aFiles['items']
+                ];
+        }
+
+        $bFilesAttach = !empty($aContent['files_attach']) && is_array($aContent['files_attach']);
+        if($bFilesAttach) {
+            $aFilesAttach = $this->_getTmplVarsFiles($aContent['files_attach'], $aEvent, $aBrowseParams);
+            if(!empty($aFilesAttach)) {
+                $aTmplVarsAttachmentsFiles = [
+                    'style_prefix' => $sStylePrefix,
+                    'display' => BX_TIMELINE_ML_GALLERY,
+                    'count' => count($aFilesAttach['items']),
+                    'bx_repeat:items' => $aFilesAttach['items']
+                ];
+            }
+        }
+
+        /*
+         *  Process collected attachments in case of Showcase layout.
+         */
+        $iAttachmentsShow = 4;
+        $iAttachmentsCount = count($aTmplVarsAttachments);
+        if($sAttachmentsLayout == BX_TIMELINE_ML_SHOWCASE && $iAttachmentsCount > 0) {
+            $aTmplVarsAttachments[0]['class'] .= ' ' . $sStylePrefix . '-ia-first';
+
+            if($iAttachmentsCount > $iAttachmentsShow)
+                $aTmplVarsAttachments = array_slice($aTmplVarsAttachments, 0, $iAttachmentsShow);
+
+            if($iAttachmentsTotal > $iAttachmentsShow)
+                $aTmplVarsAttachments[$iAttachmentsShow - 1]['item'] .= $this->parseHtmlByName('attach_more.html', [
+                    'style_prefix' => $sStylePrefix,
+                    'link' => $this->_oConfig->getItemViewUrl($aEvent),
+                    'more' => $iAttachmentsTotal - $iAttachmentsShow
+                ]);
+        }
+
+        return [
+            'style_prefix' => $sStylePrefix,
+            'bx_if:show_title' => [
+                'condition' => !empty($sTitle),
+                'content' => [
+                    'style_prefix' => $sStylePrefix,
+                    'title' => $sTitle,
+                ]
+            ],
+            'bx_if:show_content' => [
+                'condition' => !empty($sText),
+                'content' => [
+                    'style_prefix' => $sStylePrefix,
+                    'item_content' => $sText
+                ]
+            ],
+            'bx_if:show_top_raw' => [
+                'condition' => !empty($sTopRaw),
+                'content' => [
+                    'style_prefix' => $sStylePrefix,
+                    'item_content_top_raw' => $sTopRaw
+                ]
+            ],
+            'bx_if:show_raw' => [
+                'condition' => !empty($sRaw),
+                'content' => [
+                    'style_prefix' => $sStylePrefix,
+                    'item_content_raw' => $sRaw
+                ]
+            ],
+            'bx_if:show_links' => [
+                'condition' => !empty($aTmplVarsLinks),
+                'content' => [
+                    'style_prefix' => $sStylePrefix,
+                    'bx_repeat:links' => $aTmplVarsLinks
+                ]
+            ],
+            'bx_if:show_images' => [
+                'condition' => !empty($aTmplVarsImages),
+                'content' => $aTmplVarsImages
+            ],
+            'bx_if:show_videos' => [
+                'condition' => !empty($aTmplVarsVideos),
+                'content' => $aTmplVarsVideos
+            ],
+            'bx_if:show_files' => [
+                'condition' => !empty($aTmplVarsFiles),
+                'content' => $aTmplVarsFiles
+            ],
+            'bx_if:show_attachments' => [
+                'condition' => !empty($aTmplVarsAttachments),
+                'content' => [
+                    'style_prefix' => $sStylePrefix,
+                    'display' => $sAttachmentsLayout,
+                    'count' => count($aTmplVarsAttachments),
+                    'bx_repeat:items' => $aTmplVarsAttachments
+                ]
+            ],
+            'bx_if:show_attachments_files' => [
+                'condition' => !empty($aTmplVarsAttachmentsFiles),
+                'content' => $aTmplVarsAttachmentsFiles
+            ]
+        ];
+    }
+
+    protected function _getTmplVarsContentRepost(&$aEvent, $aBrowseParams = array())
+    {
+    	$aContent = &$aEvent['content'];
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $sOwnerLink = $this->parseLink($aContent['owner_url'], $aContent['owner_name']);
+
+        $sSample = _t($aContent['sample']);
+        $sSampleLink = empty($aContent['url']) ? $sSample : $this->parseLink($aContent['url'], $sSample);
+
+        $aTmplVarsData = [];
+        if(!empty($aContent['rdata']) && is_array($aContent['rdata'])) {
+            $aData = $aContent['rdata'];
+
+            $aTmplVarsData['style_prefix'] = $sStylePrefix;
+
+            if(isset($aData['text']))
+                $aTmplVarsData['text'] = $this->_prepareTextForOutput($aData['text'], $aEvent['id']);
+        }
+
+        $sContent = $this->_getContent($aContent['parse_type'], $aEvent, $aBrowseParams);
+
+        return [
+            'style_prefix' => $sStylePrefix,
+            'item_owner_action' => _t('_bx_timeline_txt_reposted', $sOwnerLink, $sSampleLink),
+            'bx_if:show_data' => [
+                'condition' => !empty($aTmplVarsData),
+                'content' => $aTmplVarsData
+            ],
+            'bx_if:show_content' => [
+                'condition' => !empty($sContent),
+                'content' => [
+                    'style_prefix' => $sStylePrefix,
+                    'content' => $sContent,
+                ]
+            ]
+        ];
+    }
+
+    protected function _getTmplVarsNote(&$aEvent)
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $iUser = bx_get_logged_profile_id();
+        $iOwner = $this->_oConfig->isSystem($aEvent['type'], $aEvent['action']) ? $aEvent['owner_id'] : $aEvent['object_id'];
+
+        $aTmplVars = array();
+        if(!empty($iOwner) && !is_array($iOwner) && !empty($aEvent['promoted'])) {
+            $sConnection = $this->_oConfig->getObject('conn_subscriptions');
+            $oConnection = BxDolConnection::getObjectInstance($sConnection);
+            if(!$oConnection->isConnected($iUser, $iOwner))
+                $aTmplVars[] = [
+                    'style_prefix' => $sStylePrefix,
+                    'class' => '',
+                    'bx_if:show_note_color' => [
+                        'condition' => false,
+                        'content' => []
+                    ],
+                    'item_note' => _t('_bx_timeline_txt_promoted')
+                ];
+        }
+
+        //--- Awaiting status related notes.
+        if($aEvent['status'] == BX_TIMELINE_STATUS_AWAITING) {
+            $sNote = '';
+            if((int)$aEvent['published'] > (int)$aEvent['date'])
+                $sNote = _t('_bx_timeline_txt_note_scheduled_awaiting', bx_time_js($aEvent['published'], BX_FORMAT_DATE, true));
+            else
+                $sNote = _t('_bx_timeline_txt_note_processing_awaiting');
+
+            $aTmplVars[] = [
+                'style_prefix' => $sStylePrefix,
+                'bx_if:show_note_color' => [
+                    'condition' => true,
+                    'content' => [
+                        'item_note_color' => 'red3'
+                    ]
+                ],
+                'item_note' => $sNote
+            ];
+        }
+
+        //--- Failed status related notes.
+        if($aEvent['status'] == BX_TIMELINE_STATUS_FAILED)
+            $aTmplVars[] = [
+                'style_prefix' => $sStylePrefix,
+                'bx_if:show_note_color' => [
+                    'condition' => true,
+                    'content' => [
+                        'item_note_color' => 'red2'
+                    ]
+                ],
+                'item_note' => _t('_bx_timeline_txt_note_processing_failed')
+            ];
+
+        //--- Pending status related notes.
+        if($aEvent['status_admin'] == BX_TIMELINE_STATUS_PENDING)
+            $aTmplVars[] = [
+                'style_prefix' => $sStylePrefix,
+                'bx_if:show_note_color' => [
+                    'condition' => true,
+                    'content' => [
+                        'item_note_color' => 'red3'
+                    ]
+                ],
+                'item_note' => _t('_bx_timeline_txt_note_approve_pending')
+            ];
+
+        return empty($aTmplVars) ? [] : [
+            'style_prefix' => $sStylePrefix,
+            'bx_repeat:notes' => $aTmplVars
+        ];
+    }
+
+    protected function _getTmplVarsOwnerActions(&$aEvent, $aBrowseParams = array())
+    {
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+
+        $iUser = bx_get_logged_profile_id();
+        $iOwner = $this->_oConfig->isSystem($aEvent['type'], $aEvent['action']) ? $aEvent['object_owner_id'] : $aEvent['object_id'];
+
+        $aTmplVarsActions = array();
+        if(!empty($iUser) && !empty($iOwner) && $iUser != $iOwner) {
+            $oOwner = BxDolProfile::getInstance($iOwner);
+            if($oOwner !== false && bx_srv($oOwner->getModule(), 'check_allowed_with_content', array('subscribe_add', $oOwner->getContentId())) === CHECK_ACTION_RESULT_ALLOWED) {
+                $sJsObject = $this->_oConfig->getJsObjectView($aBrowseParams);
+                $sConnection = $this->_oConfig->getObject('conn_subscriptions');
+
+                $sContent = _t('_sys_menu_item_title_sm_subscribe');
+                $aTmplVarsActions[] = array(
+                    'style_prefix' => $sStylePrefix,
+                    'href' => "javascript:void(0)",
+                    'onclick' => "bx_conn_action(this, '" . $sConnection . "', 'add', '" . $iOwner . "', false, function(oData, eElement) {" . $sJsObject . ".onConnect(eElement, oData);})",
+                    'title' => bx_html_attribute($sContent),
+                    'content' => $sContent,
+                    'icon' => 'check'
+                );
+            }
+        }
+
+        return array(
+            'style_prefix' => $sStylePrefix,
+            'bx_repeat:owner_actions' => $aTmplVarsActions
+        );
+    }
+
+    protected function _getTmplVarsImages($aImages, $mixedLayout, &$aEvent, &$aBrowseParams)
+    {
+        if(empty($aImages) || !is_array($aImages))
+            return [];
+
+        $iTotal = 0; //--- Total count of images related to the event.
+        if(isset($aImages['total']) && isset($aImages['items'])) {
+            $iTotal = (int)$aImages['total'];
+            $aImages = $aImages['items'];
+        }
+        else
+            $iTotal = count($aImages);
+
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aBrowseParams);
+
+        $bViewItem = isset($aBrowseParams['view']) && $aBrowseParams['view'] == BX_TIMELINE_VIEW_ITEM;
+
+        $sDisplay = '';
+        $aTmplVarsImages = [];
+
+        $sImageSrcKey = $sImageSrcKeyBig = '';
+        $sImageSrcKeyDefault = 'src';
+        if(count($aImages) == 1) {
+            $sDisplay = BX_TIMELINE_ML_SINGLE;
+            $sImageSrcKey = $bViewItem ? 'src_orig' : 'src_medium';
+        }
+        else if(is_array($mixedLayout) && !empty($mixedLayout['layout'])) {
+            $sDisplay = $mixedLayout['layout'];
+            $sImageSrcKey = 'src';
+            $sImageSrcKeyBig = 'src_medium';
+        }
+        else {
+            $sDisplay = BX_TIMELINE_ML_GALLERY;
+            $sImageSrcKey = 'src';
+            $sImageSrcKeyBig = 'src_medium';
+        }
+
+        $bAttachFirst = false;
+        if($sDisplay == BX_TIMELINE_ML_SHOWCASE)
+            $bAttachFirst = isset($mixedLayout['first']) && $mixedLayout['first'] === true;
+
+        $aImageFirst = reset($aImages);
+        $iImageFirst = isset($aImageFirst['id']) ? (int)$aImageFirst['id'] : 0;
+
+        $aImageSizes = [
+            'small' => '300w', 
+            'medium' => '500w', 
+            'orig' => '1200w'
+        ];
+        foreach($aImages as $aImage) {
+            $sImageSrcKeyCur = $sImageSrcKey;
+            if(($bAttachFirst && isset($aImage['id']) && (int)$aImage['id'] == $iImageFirst) || $iTotal == 2)
+                $sImageSrcKeyCur = $sImageSrcKeyBig;
+
+            if (is_array($aImage))
+                $sImageSrc = !empty($aImage[$sImageSrcKeyCur]) ? $aImage[$sImageSrcKeyCur] : $aImage[$sImageSrcKeyDefault];
+            if(empty($sImageSrc))
+                continue;
+
+            $sImageAttrSrcset = '';
+            foreach($aImageSizes as $sSize => $sWidth)
+                if(isset($aImage['src_' . $sSize]))
+                    $sImageAttrSrcset .= $aImage['src_' . $sSize] . ' ' . $sWidth . ', ';
+
+            $sImage = $this->parseImage($sImageSrc, array(
+                'class' => $sStylePrefix . '-item-image',
+                'srcset' => trim($sImageAttrSrcset, ", "),
+                'sizes' => '100%'
+            ));
+
+            $aAttrs = array();
+            if(isset($aImage['onclick']))
+                $aAttrs['onclick'] = $aImage['onclick'];
+            else if(!empty($aImage['src_orig']))
+                $aAttrs['onclick'] = 'return ' . $sJsObject . '.showItem(this, \'' . $aEvent['id'] . '\', \'photo\', ' . json_encode(array('src' => base64_encode($aImage['src_orig']))) . ')'; 
+
+            $sImage = $this->parseLinkByName('image_link.html', isset($aImage['url']) ? $aImage['url'] : 'javascript:void(0)', $sImage, $aAttrs);
+
+            $aTmplVarsImages[] = array(
+                'style_prefix' => $sStylePrefix,
+                'class' => '',
+                'item' => $sImage
+            );
+        }
+        
+        return array(
+            'display' => $sDisplay,
+            'total' => $iTotal,
+            'items' => $aTmplVarsImages
+        );
+    }
+
+    protected function _getTmplVarsVideos($aVideos, $mixedLayout, &$aEvent, &$aBrowseParams)
+    {
+        if(empty($aVideos) || !is_array($aVideos))
+            return array();
+
+        $iTotal = 0; //--- Total count of videos related to the event.
+        if(isset($aVideos['total']) && isset($aVideos['items'])) {
+            $iTotal = (int)$aVideos['total'];
+            $aVideos = $aVideos['items'];
+        }
+        else
+            $iTotal = count($aVideos);
+
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aBrowseParams);
+        $aTmplVarsVideos = array();
+
+        /*
+         * For now Main Section may have only one video which can use 'autoplay' feature.
+         */
+        $bMain = $mixedLayout === true;
+        if($bMain) {
+            $sDisplay = BX_TIMELINE_ML_SINGLE;
+            if(count($aVideos) > 1)
+                $aVideos = array_slice($aVideos, 0, 1);
+        }
+        else
+            $sDisplay = is_array($mixedLayout) && !empty($mixedLayout['layout']) ? $mixedLayout['layout'] : BX_TIMELINE_AML_DEFAULT;
+
+        /*
+         * Main Section: Autoplay feature is only available here.
+         */
+        $sVap = $sVapId = $sVapSrc = $sVapTmpl = '';
+        if($bMain) {
+            $sVap = $this->_oConfig->getVideosAutoplay();
+            if($sVap != BX_TIMELINE_VAP_OFF) {
+                $sVapId = $this->_oConfig->getHtmlIds('view', 'video_iframe') . $aEvent['id'] . '-';
+                $sVapSrc = BX_DOL_URL_ROOT . $this->_oConfig->getBaseUri() . 'video/' . $aEvent['id'] . '/';
+                $sVapTmpl = $this->getHtml('video_iframe.html'); 
+            }
+        }
+
+        /*
+         * Attachments Section.
+         */
+        $sAttachTmpl = '';
+        $bAttachFirst = false;
+        if($sDisplay == BX_TIMELINE_ML_SHOWCASE) {
+            $sAttachTmpl = $this->getHtml('attach_video_preview.html');
+            $bAttachFirst = isset($mixedLayout['first']) && $mixedLayout['first'] === true;
+        }
+
+        $iVideoFirst = reset($aVideos)['id'];
+        foreach($aVideos as $aVideo) {
+            $iVideo = (int)$aVideo['id'];
+
+            if($bMain && $sVap != BX_TIMELINE_VAP_OFF)
+                $aTmplVarsVideos[] = array(
+                    'style_prefix' => $sStylePrefix,
+                    'class' => '',
+                    'item' => $this->parseHtmlByContent($sVapTmpl, array(
+                        'style_prefix' => $sStylePrefix,
+                        'html_id' => $sVapId . $iVideo,
+                        'src' => $sVapSrc . $iVideo . '/'
+                    )) 
+                );
+            else {
+                if($bMain || $sDisplay == BX_TIMELINE_ML_GALLERY || ($bAttachFirst && $iVideo == $iVideoFirst)) {
+                    $sItem = '';
+                    if(isset($aVideo['src'], $aVideo['src_orig'])) {
+                        $sItem = $this->parseImage($aVideo['src'], array(
+                            'class' => $sStylePrefix . '-item-image'
+                        ));
+
+                        $aAttrs = array();
+                        if(!empty($aVideo['src_orig']))
+                            $aAttrs['onclick'] = 'return ' . $sJsObject . '.showItem(this, \'' . $aEvent['id'] . '\', \'video\', ' . json_encode(array('src' => base64_encode($aVideo['src_orig']))) . ')'; 
+
+                        $sItem = $this->parseLinkByName('image_link.html', isset($aVideo['url']) ? $aVideo['url'] : 'javascript:void(0)', $sItem, $aAttrs);
+                    }
+                    else 
+                        $sItem = BxTemplFunctions::getInstance()->videoPlayer($aVideo['src_poster'], $aVideo['src_mp4'], $aVideo['src_mp4_hd'], array(
+                            'preload' => $this->_oConfig->getVideosPreload(),
+                        ), '', $aBrowseParams['dynamic_mode']);
+
+                    $aTmplVarsVideos[] = array(
+                        'style_prefix' => $sStylePrefix,
+                        'class' => '',
+                        'item' => $sItem
+                    );
+                }
+                else {
+                    $bUrl = !empty($aVideo['url']);
+                    $sUrl = $bUrl ? $aVideo['url'] : '';
+
+                    $sSrc = $aVideo[isset($aVideo['src'], $aVideo['src_orig']) ? 'src' : 'src_poster'];
+
+                    $bDuration = !empty($aVideo['duration']);
+                    $sDuration = _t_format_duration($bDuration ? $aVideo['duration'] : 0);
+
+                    $aTmplVarsVideos[] = array(
+                        'style_prefix' => $sStylePrefix,
+                        'class' => '',
+                        'item' => $this->parseHtmlByContent($sAttachTmpl, array(
+                            'style_prefix' => $sStylePrefix,
+                            'bx_if:show_link' => array(
+                                'condition' => $bUrl,
+                                'content' => array(
+                                    'style_prefix' => $sStylePrefix,
+                                    'url' => $sUrl,
+                                    'src' => $sSrc,
+                                )
+                            ),
+                            'bx_if:show_non_link' => array(
+                                'condition' => !$bUrl,
+                                'content' => array(
+                                    'style_prefix' => $sStylePrefix,
+                                    'src' => $sSrc,
+                                )
+                            ),
+                            'bx_if:show_duration' => array(
+                                'condition' => $bDuration,
+                                'content' => array(
+                                    'style_prefix' => $sStylePrefix,
+                                    'duration' => $sDuration,
+                                )
+                            )
+                        ))
+                    );
+                }
+            }
+        }
+ 
+        return array( 
+            'display' => $sDisplay,
+            'total' => $iTotal,
+            'items' => $aTmplVarsVideos
+        );
+    }
+
+    protected function _getTmplVarsFiles($aFiles, &$aEvent, &$aBrowseParams)
+    {
+        if(empty($aFiles) || !is_array($aFiles))
+            return array();
+
+        $iTotal = 0; //--- Total count of files related to the event.
+        if(isset($aFiles['total']) && isset($aFiles['items'])) {
+            $iTotal = (int)$aFiles['total'];
+            $aFiles = $aFiles['items'];
+        }
+        else
+            $iTotal = count($aFiles);
+
+        $sStylePrefix = $this->_oConfig->getPrefix('style');
+        $sJsObject = $this->_oConfig->getJsObjectView($aBrowseParams);
+
+        $bViewItem = isset($aBrowseParams['view']) && $aBrowseParams['view'] == BX_TIMELINE_VIEW_ITEM;
+
+        $sDisplay = '';
+        $aTmplVarsFiles = array();
+
+        $sFileSrcKey = '';
+        $sFileSrcKeyDefault = 'src';
+        if(count($aFiles) == 1) {
+            $sDisplay = 'single';
+            $sFileSrcKey = $bViewItem ? 'src_orig' : 'src_medium';
+        }
+        else {
+            $sDisplay = 'gallery';
+            $sFileSrcKey = 'src';
+        }
+
+        foreach($aFiles as $aFile) {
+            $sFileSrc = !empty($aFile[$sFileSrcKey]) ? $aFile[$sFileSrcKey] : $aFile[$sFileSrcKeyDefault];
+            if(empty($sFileSrc))
+                continue;
+
+            $aAttrs = ['target' => '_blank'];
+            if(isset($aFile['onclick']))
+                $aAttrs['onclick'] = $aFile['onclick'];
+            else if(!$bViewItem && !empty($aFile['src_orig']))
+                $aAttrs['onclick'] = 'return ' . $sJsObject . '.showItem(this, \'' . $aEvent['id'] . '\', \'file\', ' . json_encode(array('src' => base64_encode($aFile['src_orig']))) . ')'; 
+            if(isset($aFile['title']))
+                $aAttrs['title'] = $aFile['title'];
+
+            $sAttrs = '';
+            foreach($aAttrs as $sKey => $sValue)
+                $sAttrs .= ' ' . $sKey . '="' . bx_html_attribute($sValue) . '"';
+
+            $aTmplVarsFiles[] = [
+                'style_prefix' => $sStylePrefix,
+                'class' => '',
+                'item' => $this->parseHtmlByName('file_link.html', [
+                    'href' => isset($aFile['url']) ? $aFile['url'] : 'javascript:void(0)',
+                    'attrs' => $sAttrs,
+                    'icon_src' => $sFileSrc,
+                    'icon_class' => $sStylePrefix . '-item-file'
+                ])
+            ];
+        }
+
+        return array(
+            'display' => $sDisplay,
+            'total' => $iTotal,
+            'items' => $aTmplVarsFiles
+        );
+    }
+
+    protected function _getSystemData(&$aEvent, $aBrowseParams = array())
+    {
+        $mixedResult = $this->_oConfig->getSystemData($aEvent, $aBrowseParams);
+        if($mixedResult === false) {
+            $sMethod = 'display' . bx_gen_method_name($aEvent['type'] . '_' . $aEvent['action']);
+            if(method_exists($this, $sMethod))
+                $mixedResult = $this->$sMethod($aEvent);
+        }
+
+        if($mixedResult === false)
+            return '';
+
+        $this->_preparetDataActions($aEvent, $mixedResult);
+        return $mixedResult;
+    }
+
+    protected function _getCommonData(&$aEvent, $aBrowseParams = array())
+    {
+        $CNF = $this->_oConfig->CNF;
+
+        $oModule = $this->getModule();
+        $sJsObject = $this->_oConfig->getJsObjectView($aBrowseParams);
+        $sPrefix = $this->_oConfig->getPrefix('common_post');
+        $sType = str_replace($sPrefix, '', $aEvent['type']);
+
+        $oObjectOwner = BxDolProfile::getInstanceMagic($aEvent['object_id']);
+
+        $iOwnerId = $aEvent['owner_id'];
+        if(is_array($aEvent['owner_id']))
+            $iOwnerId = is_numeric($aEvent['object_privacy_view']) && (int)$aEvent['object_privacy_view'] < 0 ? abs((int)$aEvent['object_privacy_view']) : (int)array_shift($aEvent['owner_id']);
+
+        $aResult = array(
+            'owner_id' => $iOwnerId,
+            'object_owner_id' => $aEvent['object_id'],
+            'icon' => $CNF['ICON'],
+            'sample' => '_bx_timeline_txt_sample_with_article',
+            'sample_wo_article' => '_bx_timeline_txt_sample',
+            'sample_action' => '_bx_timeline_txt_added_sample',
+            'content_type' => $sType,
+            'content' => array(
+                'sample' => '_bx_timeline_txt_sample_with_article',
+                'sample_wo_article' => '_bx_timeline_txt_sample',
+                'sample_action' => '_bx_timeline_txt_added_sample',
+                'url' => $this->_oConfig->getItemViewUrl($aEvent)
+            ), //a string to display or array to parse default template before displaying.
+            'views' => '',
+            'votes' => '',
+            'reactions' => '',
+            'scores' => '',
+            'reports' => '',
+            'comments' => '',
+            'title' => $aEvent['title'], //may be empty.
+            'description' => bx_replace_markers($aEvent['description'], array(
+                'profile_name' => $oObjectOwner->getDisplayName()
+            )) //may be empty.
+        );
+
+        switch($sType) {
+            case BX_TIMELINE_PARSE_TYPE_POST:
+                if(!empty($aEvent['content'])) {
+                    $aContent = @unserialize($aEvent['content']);
+                    if(!empty($aContent) && is_array($aContent))
+                        $aResult['content'] = array_merge($aResult['content'], $aContent);
+                }
+
+                $aResult['content']['links'] = $oModule->getEventLinks($aEvent['id']);
+                $aResult['content']['images_attach'] = $oModule->getEventImages($aEvent['id']);
+                $aResult['content']['videos_attach'] = $oModule->getEventVideos($aEvent['id']);
+                $aResult['content']['files_attach'] = $oModule->getEventFiles($aEvent['id']);
+                break;
+
+            case BX_TIMELINE_PARSE_TYPE_REPOST:
+                if(empty($aEvent['content']))
+                    return [];
+
+                $aContent = unserialize($aEvent['content']);
+                if(empty($aContent) || !is_array($aContent))
+                    return [];
+
+                if(!$this->_oConfig->isSystem($aContent['type'] , $aContent['action'])) {
+                    $aEventReposted = $this->_oDb->getEvents(array('browse' => 'id', 'value' => $aContent['object_id']));
+                    $aReposted = $this->_getCommonData($aEventReposted, $aBrowseParams);
+                } 
+                else {
+                    $aEventReposted = $this->_oDb->getEvents(array_merge(array('browse' => 'descriptor'), $aContent));
+                    $aReposted = $this->_getSystemData($aEventReposted, $aBrowseParams);
+                }
+
+                if(empty($aReposted) || !is_array($aReposted))
+                    return array();
+
+                $aEventReposted['content'] = $aReposted['content'];
+
+                $aResult['content'] = array_merge($aContent, $aReposted['content']);
+                $aResult['content']['parse_type'] = !empty($aReposted['content_type']) ? $aReposted['content_type'] : BX_TIMELINE_PARSE_TYPE_DEFAULT;
+
+                $sKey = 'allowed_view';
+                $aResult['content'][$sKey] = $this->_preparePrivacy($sKey, $aEventReposted, $aReposted);
+
+                $oObjectOwner = $oModule->getObjectUser($aReposted['object_owner_id']);
+                $aResult['content']['owner_id'] = $aReposted['object_owner_id'];
+                $aResult['content']['owner_name'] = $oObjectOwner->getDisplayName();
+                $aResult['content']['owner_url'] = $oObjectOwner->getUrl();
+
+                if(!empty($aReposted['sample']))
+                    $aResult['content']['sample'] = $aReposted['sample'];
+                if(!empty($aReposted['sample_wo_article']))
+                    $aResult['content']['sample'] = $aReposted['sample_wo_article'];
+
+                $sUserName = $oModule->getObjectUser($aEvent['object_id'])->getDisplayName();
+                $aResult['title'] = _t('_bx_timeline_txt_user_repost', $sUserName, _t($aResult['content']['sample']));
+                $aResult['description'] = _t('_bx_timeline_txt_user_reposted_user_sample', $sUserName, $aResult['content']['owner_name'], _t($aResult['content']['sample']));
+                $aResult['allowed_view'] = array(
+                    'module' => $this->_oConfig->getName(),
+                    'method' => 'get_timeline_repost_allowed_view',
+                );
+
+                if(!$this->_oConfig->isRepostOwnActions()) {
+                    $aResult['views'] = $aReposted['views'];
+                    $aResult['votes'] = $aReposted['votes'];
+                    $aResult['reactions'] = $aReposted['reactions'];
+                    $aResult['scores'] = $aReposted['scores'];
+                    $aResult['reports'] = $aReposted['reports'];
+                    $aResult['comments'] = $aReposted['comments'];
+                }
+                break;
+        }
+
+        $this->_preparetDataActions($aEvent, $aResult);
+        return $aResult;
+    }
+
+    protected function _getFirst($aEvents, $aParams = array())
+    {
+        $CNF = $this->_oConfig->CNF;
+
+        foreach($aEvents as $aEvent)
+            if((int)$aEvent[$CNF['FIELD_STICKED']] == 0)
+                return (int)$aEvent[$CNF['FIELD_ID']];
+
+        $aParams['start'] += $aParams['per_page'];
+        $aEvents = $this->_oDb->getEvents($aParams);
+        if(!empty($aEvents) && is_array($aEvents))
+            return $this->_getFirst($aEvents, $aParams);
+
+        return 0;
+    }
+
+    protected function _preparetDataActions(&$aEvent, &$aResult)
+    {
+        if(empty($aEvent) || !is_array($aEvent) || empty($aEvent['id']))
+            return;
+
+        $oModule = $this->getModule();
+
+        $sSystem = $this->_oConfig->getObject('view');
+        if(empty($aResult['views'])) {
+            $aResult['views'] = array();
+            if($oModule->getViewObject($sSystem, $aEvent['id']) !== false)
+                $aResult['views'] = array(
+                    'system' => $sSystem,
+                    'object_id' => $aEvent['id'],
+                    'count' => $aEvent['views']
+                );
+        }
+
+        $sSystem = $this->_oConfig->getObject('vote');
+        if(empty($aResult['votes'])) {
+            $aResult['votes'] = array();
+            if($oModule->getVoteObject($sSystem, $aEvent['id']) !== false)
+                $aResult['votes'] = array(
+                    'system' => $sSystem,
+                    'object_id' => $aEvent['id'],
+                    'count' => $aEvent['votes']
+                );
+        }
+        
+        $sSystem = $this->_oConfig->getObject('reaction');
+        if(empty($aResult['reactions'])) {
+            $aResult['reactions'] = array();
+            if($oModule->getReactionObject($sSystem, $aEvent['id']) !== false)
+                $aResult['reactions'] = array(
+                    'system' => $sSystem,
+                    'object_id' => $aEvent['id'],
+                    'count' => $aEvent['rvotes']
+                );
+        }
+
+        $sSystem = $this->_oConfig->getObject('score');
+        if(empty($aResult['scores'])) {
+            $aResult['scores'] = array();
+            if($oModule->getScoreObject($sSystem, $aEvent['id']) !== false)
+                $aResult['scores'] = array(
+                    'system' => $sSystem,
+                    'object_id' => $aEvent['id'],
+                    'score' => $aEvent['score']
+                );
+        }
+
+        $sSystem = $this->_oConfig->getObject('report');
+        if(empty($aResult['reports'])) {
+            $aResult['reports'] = array();
+            if($oModule->getReportObject($sSystem, $aEvent['id']) !== false)
+                $aResult['reports'] = array(
+                    'system' => $sSystem,
+                    'object_id' => $aEvent['id'],
+                    'count' => $aEvent['reports']
+                );
+        }
+
+        $sSystem = $this->_oConfig->getObject('comment');
+        if(empty($aResult['comments'])) {
+            $aResult['comments'] = array();
+            if($oModule->getCmtsObject($sSystem, $aEvent['id']) !== false)
+                $aResult['comments'] = array(
+                    'system' => $sSystem,
+                    'object_id' => $aEvent['id'],
+                    'count' => $aEvent['comments']
+                );
+        }
+    }
+
+    protected function _prepareTextForOutputBriefCard($s, $iEventId = 0)
+    {
+        $s = strip_tags($s, $this->_oConfig->getBriefCardsTags(true));
+        
+        return $this->_prepareTextForOutput($s, $iEventId = 0);
+    }
+
+    protected function _prepareTextForOutput($s, $iEventId = 0)
+    {
+    	$s = bx_process_output($s, BX_DATA_HTML);
+
+        $oMetatags = BxDolMetatags::getObjectInstance($this->_oConfig->getObject('metatags'));
+        $s = $oMetatags->metaParse($iEventId, $s);
+
+        return $s;
+    }
+    
+    protected function _preparePrivacy($sKey, $aEvent, $aEventData)
+    {
+        $iResult = CHECK_ACTION_RESULT_ALLOWED;
+        if(isset($aEventData[$sKey], $aEventData[$sKey]['module'], $aEventData[$sKey]['method']))
+            $iResult = BxDolService::call($aEventData[$sKey]['module'], $aEventData[$sKey]['method'], array($aEvent));
+        else if(($aHandler = $this->_oConfig->getHandler($aEvent)) !== false && BxDolRequest::serviceExists($aHandler['module_name'], 'get_timeline_post_allowed_view'))
+            $iResult = BxDolService::call($aHandler['module_name'], 'get_timeline_post_allowed_view', array($aEvent));
+
+        return $iResult;
+    }
+
+    protected function _getCounterIcon($aParams = array())
+    {
+        return $this->parseHtmlByName('repost_counter_icon.html', []);
+    }
+
+    protected function _getCounterLabel($iCount, $aParams = array())
+    {
+        return _t(isset($aParams['caption']) ? $aParams['caption'] : '_bx_timeline_txt_repost_counter', $iCount);
+    }
+}
+
+/** @} */

+ 143 - 0
modules/boonex/timeline/updates/13.0.14_13.0.15/source/install/config.php

@@ -0,0 +1,143 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Timeline Timeline
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_timeline',
+    'title' => 'Timeline',
+    'note' => 'Timeline module.',
+    'version' => '13.0.15',
+    'vendor' => 'BoonEx',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.1.0-RC1'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/timeline/',
+    'home_uri' => 'timeline',
+
+    'db_prefix' => 'bx_timeline_',
+    'class_prefix' => 'BxTimeline',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Timeline',
+
+    /**
+     * List of page triggers.
+     */
+    'page_triggers' => array (
+        'trigger_page_profile_view_entry',
+        'trigger_page_group_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_timeline_photos',
+    	'bx_timeline_videos',
+        'bx_timeline_files'
+    ),
+
+    /**
+     * Transcoders.
+     */
+    'transcoders' => array(
+    	'bx_timeline_photos_preview',
+    	'bx_timeline_photos_view',
+        'bx_timeline_photos_medium',
+        'bx_timeline_photos_big',
+
+        'bx_timeline_videos_photo_preview',
+        'bx_timeline_videos_photo_view',
+        'bx_timeline_videos_photo_big',
+        'bx_timeline_videos_poster_preview',
+    	'bx_timeline_videos_poster_view',
+        'bx_timeline_videos_poster_big',
+    	'bx_timeline_videos_mp4',
+    	'bx_timeline_videos_mp4_hd'
+    ),
+
+    /**
+     * 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,
+    	'update_relations' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'enable_success' => array(
+    	'process_page_triggers' => 1,
+        'process_menu_triggers' => 1,
+    	'register_transcoders' => 1,
+    	'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+    	'update_relations_for_all' => 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
+     */
+    'relation_handlers' => array(
+    	'on_install' => '',
+    	'on_uninstall' => 'delete_module_events',
+	    'on_enable' => 'add_handlers',
+	    'on_disable' => 'delete_handlers',
+    ),
+    'relations' => array(
+    	'bx_notifications'
+    )
+);
+
+/** @} */