Explorar o código

Merge branch 'master' of https://github.com/boonex/dolphin

AntonLV %!s(int64=12) %!d(string=hai) anos
pai
achega
fcdad44987
Modificáronse 88 ficheiros con 17357 adicións e 95 borrados
  1. 11 3
      inc/classes/BxDolTemplate.php
  2. 7 0
      inc/js/functions.js
  3. 43 12
      inc/js/jquery.dolPopup.js
  4. 8 6
      install/sql/system.sql
  5. 1 0
      modules/boonex/english/data/langs/system.xml
  6. 5 9
      modules/boonex/ocean/data/template/system/_sub_header.html
  7. 7 11
      template/_sub_header.html
  8. 42 1
      template/css/common.css
  9. 12 7
      template/css/media-phone.css
  10. 12 12
      template/css/menu.css
  11. 5 0
      template/menu_main_submenu_wrapper.html
  12. 0 5
      template/menu_site.html
  13. 1 1
      template/menu_toolbar.html
  14. 2 2
      template/popup_box.html
  15. 1 1
      template/popup_loading.html
  16. 4 1
      template/popup_trans.html
  17. 0 18
      template/scripts/BxBaseMenuSite.php
  18. 7 6
      template/scripts/BxBaseMenuSubmenu.php
  19. 4 0
      template/search.html
  20. 8 0
      upgrade/files/8.0.0.A6-8.0.0.A7/check.php
  21. 978 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolCmts.php
  22. 177 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolInstallerUtils.php
  23. 363 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolMetatags.php
  24. 2212 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolTemplate.php
  25. 396 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolVote.php
  26. 150 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/js/classes/BxDolVote.js
  27. 555 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/js/functions.js
  28. 437 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/js/jquery.dolPopup.js
  29. 1201 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/utils.inc.php
  30. 12 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/version.inc.php
  31. 170 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/notifications/classes/BxBaseModNotificationsConfig.php
  32. 5 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/profile/template/unit.html
  33. 5 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/author.html
  34. 72 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/css/main.css
  35. 7 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/entry-text.html
  36. 16 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/unit.html
  37. 23 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/unit_gallery.html
  38. 7 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/plugins_public/marka/marka.min.css
  39. 8 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/plugins_public/marka/marka.min.js
  40. 0 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/plugins_public/marka/marka.min.js.map
  41. 791 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioInstaller.php
  42. 545 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioInstallerUtils.php
  43. 100 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioOAuth.php
  44. 138 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioOAuthLib.php
  45. 151 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioOAuthPlugin.php
  46. 338 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioStore.php
  47. 39 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioStoreQuery.php
  48. 648 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioToolsAudit.php
  49. 229 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioUpdater.php
  50. 412 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/js/store.js
  51. 58 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/_header.html
  52. 190 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/css/general.css
  53. 249 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/css/launcher.css
  54. 0 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/css/page.css
  55. 45 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/scripts/BxBaseStudioConfig.php
  56. 804 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/scripts/BxBaseStudioNavigationItems.php
  57. 712 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/scripts/BxBaseStudioStore.php
  58. 29 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/str_product_v1.html
  59. 39 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/str_product_v2.html
  60. 77 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/_header.html
  61. 71 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/_sub_header.html
  62. 399 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/common.css
  63. 521 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/default.less
  64. 125 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/general.css
  65. 90 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/media-phone.css
  66. 418 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/menu.css
  67. 82 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/uploaders.css
  68. 9 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/menu_account_popup.html
  69. 5 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/menu_custom.html
  70. 8 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/menu_toolbar.html
  71. 12 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/popup_box.html
  72. 7 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/popup_image.html
  73. 3 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/popup_slide.html
  74. 6 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/popup_trans.html
  75. 3 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/rss_holder.html
  76. 722 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseCmts.php
  77. 58 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseCmtsForm.php
  78. 1240 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseFormView.php
  79. 376 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseFunctions.php
  80. 167 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseMenu.php
  81. 85 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBasePageAccount.php
  82. 309 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseVote.php
  83. 43 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/uploader_form_html5.html
  84. 1 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/vote_do_vote_likes.html
  85. 7 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/vote_element_block.html
  86. 4 0
      upgrade/files/8.0.0.A6-8.0.0.A7/files/template/vote_element_inline.html
  87. 2 0
      upgrade/files/8.0.0.A6-8.0.0.A7/script.php
  88. 16 0
      upgrade/files/8.0.0.A6-8.0.0.A7/sql.sql

+ 11 - 3
inc/classes/BxDolTemplate.php

@@ -1018,10 +1018,18 @@ class BxDolTemplate extends BxDol implements iBxDolSingleton
                     $sRet = bx_process_output($this->aPage['header_text']);
                 break;
             case 'popup_loading':
-                $sContent = $this->parsePageByName('popup_loading.html', array());
-
                 bx_import('BxTemplFunctions');
-                $sRet = BxTemplFunctions::getInstance()->transBox('bx-popup-loading', $sContent, true);
+                $s = $this->parsePageByName('popup_loading.html', array());
+                $sRet = BxTemplFunctions::getInstance()->transBox('bx-popup-loading', $s, true);
+
+                bx_import('BxTemplSearch');
+                $oSearch = new BxTemplSearch();
+                $oSearch->setLiveSearch(true);
+                $s = $this->parsePageByName('search.html', array(
+                    'search_form' => $oSearch->getForm(BX_DB_CONTENT_ONLY),
+                    'results' => $oSearch->getResultsContainer(),
+                ));
+                $sRet .= BxTemplFunctions::getInstance()->transBox('bx-popup-search', $s, true);
                 break;
             case 'lang':
                 $sRet = bx_lang_name();

+ 7 - 0
inc/js/functions.js

@@ -201,6 +201,11 @@ function bx_loading (elem, b) {
 
     var block = $(elem);
 
+    if (block.hasClass('bx-btn')) {
+        bx_loading_btn (block, b);
+        return;
+    }
+
     if (1 == b || true == b) {
 
         bx_loading_content(block, b);
@@ -271,6 +276,8 @@ function bx_menu_popup (o, e, options, vars) {
     var options = options || {};
     var vars = vars || {};
     var o = $.extend({}, $.fn.dolPopupDefaultOptions, options, {id: o, url: bx_append_url_params('menu.php', $.extend({o:o}, vars))});
+    if ('undefined' == typeof(e))
+        e = window;
     $(e).dolPopupAjax(o);
 }
 

+ 43 - 12
inc/js/jquery.dolPopup.js

@@ -18,7 +18,7 @@
         onShow: function () {},
         onBeforeHide: function () {},
         onHide: function () {},
-        speed: 150
+        speed: 200
     }; 
 
     $.fn.dolPopupDefaultPointerOptions = {
@@ -72,6 +72,9 @@
                 return true;
             } 
 
+            if (false != o.pointer) // all popups without pointer are responsive
+                $el.removeClass('bx-popup-responsive').addClass('bx-popup-with-pointer');
+
             // default style for correct positioning
             $el.css({
                 display: 'block',
@@ -81,7 +84,13 @@
                 left: 0
             });
 
+
             setTimeout(function() { // timeout is needed for some browsers to render element
+
+                // remove any transitions before setting popup position
+                $el.removeClass('bx-popup-transitions bx-popup-inactive'); 
+
+                // set popup position
                 $el._dolPopupSetPosition(o);
 
                 if (!$el.hasClass('bx-popup-applied')) { // do this only once
@@ -120,15 +129,25 @@
 
                 o.onBeforeShow();
 
-                if (o.speed > 0) {
-                    $el.css({display: 'none', visibility: 'visible'}).fadeIn(o.speed, o.onShow);
-                    if (o.fog)
+                // transition effect
+                $el.addClass('bx-popup-inactive');
+                setTimeout(function () {
+                    $el.addClass('bx-popup-transitions bx-popup-active');
+                }, 10);
+    
+                // show popup
+                $el.css({display: 'block', visibility: 'visible'});
+                if (o.fog) {
+                    if (o.speed > 0)
                         $('#bx-popup-fog').fadeIn(o.speed);
-                } else {
-                    $el.css({display: 'block', visibility: 'visible'});
-                    if (o.fog)
-                        $('#bx-popup-fog').show(o.onShow);
+                    else
+                        $('#bx-popup-fog').show();
                 }
+                
+                setTimeout(function () {
+                    o.onShow();                    
+                    $el.find('input').focus(); // put cursor to the first input element
+                }, o.speed);
 
             }, 10);
         });
@@ -146,7 +165,7 @@
                 return false;
 
             if (!$el.is(':visible') || 'hidden' == $el.css('visibility') || 'none' == $el.css('display'))
-                return false;
+                return false;            
 
             var o = $.extend({}, $el.data('bx-popup-options'), options);
 
@@ -159,13 +178,23 @@
 
             o.onBeforeHide();
 
-            if (o.speed > 0) {                
+            $el.removeClass('bx-popup-active').addClass('bx-popup-inactive');
+
+            if (o.speed > 0) {
+
                 if (o.fog)
                     $('#bx-popup-fog').fadeOut(o.speed);
-                $el.fadeOut(o.speed, o.onHide);
-            } else {                
+
+                setTimeout(function () {
+                    $el.hide();
+                    o.onHide();
+                }, o.speed);
+
+            } else {
+
                 if (o.fog)
                     $('#bx-popup-fog').hide();
+
                 $el.hide(o.onHide);
             }
         });
@@ -246,6 +275,8 @@
 
                 $('<div id="' + sPopupId + '" style="display:none;">' + $('#bx-popup-loading').html() + '</div>').appendTo('body').find(options.container).hide();
 
+                $('#' + sPopupId).addClass($('#bx-popup-loading').attr('class'));
+
                 var oLoading = $('#' + sPopupId + ' .bx-popup-loading-wrapped');
                 bx_loading_content(oLoading, true, true);
 

+ 8 - 6
install/sql/system.sql

@@ -2594,7 +2594,7 @@ CREATE TABLE IF NOT EXISTS `sys_objects_menu` (
 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 
 INSERT INTO `sys_objects_menu` (`object`, `title`, `set_name`, `module`, `template_id`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES
-('sys_site', '_sys_menu_title_main', 'sys_site', 'system', 7, 0, 1, 'BxTemplMenuSite', ''),
+('sys_site', '_sys_menu_title_main', 'sys_site', 'system', 14, 0, 1, 'BxTemplMenuSite', ''),
 ('sys_site_submenu', '_sys_menu_title_submenu', 'sys_site', 'system', 1, 0, 1, 'BxTemplMenuSubmenu', ''),
 ('sys_site_submenu_main', '_sys_menu_title_submenu_main', 'sys_site', 'system', 8, 0, 1, '', ''),
 ('sys_footer', '_sys_menu_title_footer', 'sys_footer', 'system', 2, 0, 1, 'BxTemplMenuFooter', ''),
@@ -2678,13 +2678,15 @@ INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `tit
 ('sys_footer', 'system', 'switch_template', '_sys_menu_item_title_system_switch_template', '_sys_menu_item_title_switch_template', 'javascript:void(0);', 'bx_menu_popup(''sys_switch_template'', window);', '', '', '', 2147483647, 1, 1, 3);
 
 -- site toolbar menu
-INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
-('sys_toolbar_site', 'system', 'main-menu', '_sys_menu_item_title_system_main_menu', '', 'javascript:void(0);', 'bx_menu_popup(''sys_site'', this, {pointer:{align:''left''}});', '', 'reorder', '', 2147483647, 1, 1, 1);
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_toolbar_site', 'system', 'main-menu', '_sys_menu_item_title_system_main_menu', '', 'javascript:void(0);', 'bx_menu_popup(\'sys_site\');', '', 'reorder', '', 2147483647, 1, 1, 1),
+('sys_toolbar_site', 'system', 'search', '_sys_menu_item_title_system_search', '', 'javascript:void(0);', '$(\'#bx-popup-search\').dolPopup();', '', 'search', '', 2147483647, 1, 1, 2);
 
 -- member toolbar menu
-INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `addon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES
-('sys_toolbar_member', 'system', 'account', '_sys_menu_item_title_system_account_menu', '_sys_menu_item_title_account_menu', 'javascript:void(0);', 'bx_menu_popup(''sys_account_popup'', this, {pointer:{align:''right''}});', '', 'user', 'a:3:{s:6:"module";s:6:"system";s:6:"method";s:21:"profile_notifications";s:5:"class";s:20:"TemplServiceProfiles";}', 'sys_account_popup', 510, 1, 0, 1),
-('sys_toolbar_member', 'system', 'login', '_sys_menu_item_title_system_login', '', 'page.php?i=login', '', '', 'user', '', '', 1, 1, 0, 2);
+INSERT INTO `sys_menu_items`(`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES 
+('sys_toolbar_member', 'system', 'add-content', '_sys_menu_item_title_system_add_content', '', 'javascript:void(0);', 'bx_menu_popup(\'sys_add_content\');', '', 'plus', '', 510, 1, 1, 0),
+('sys_toolbar_member', 'system', 'account', '_sys_menu_item_title_system_account_menu', '_sys_menu_item_title_account_menu', 'javascript:void(0);', 'bx_menu_popup(\'sys_account_popup\');', '', 'user', 'sys_account_popup', 510, 1, 0, 1),
+('sys_toolbar_member', 'system', 'login', '_sys_menu_item_title_system_login', '', 'page.php?i=login', '', '', 'user', '', 1, 1, 0, 2);
 
 -- simple menu in account popup
 INSERT INTO `sys_menu_items` (`set_name`, `module`, `name`, `title_system`, `title`, `link`, `onclick`, `target`, `icon`, `submenu_object`, `visible_for_levels`, `active`, `copyable`, `order`) VALUES

+ 1 - 0
modules/boonex/english/data/langs/system.xml

@@ -1781,6 +1781,7 @@ If it is not enabled then please consider implement this optimization, since it
 	<string name="_sys_menu_item_title_system_main_menu"><![CDATA[Main Menu]]></string>
 	<string name="_sys_menu_item_title_system_more"><![CDATA[More]]></string>
 	<string name="_sys_menu_item_title_system_profile"><![CDATA[Profile]]></string>
+	<string name="_sys_menu_item_title_system_search"><![CDATA[Search]]></string>
 	<string name="_sys_menu_item_title_system_set_acl_level"><![CDATA[Set membership]]></string>
 	<string name="_sys_menu_set_title_account"><![CDATA[Account]]></string>
 	<string name="_sys_menu_set_title_account_dashboard"><![CDATA[Account Dashboard]]></string>

+ 5 - 9
modules/boonex/ocean/data/template/system/_sub_header.html

@@ -39,17 +39,13 @@
 	    <div id="bx-menu-main-bar" class="bx-def-color-bg-sec">
 	
 	        <div class="bx-def-centered bx-def-padding-sec-leftright bx-def-page-width">
-	
+
 	            <bx_injection:injection_top_menu_before />
-	
-	            <div id="bx-menu-main-container">
-	                <div id="bx-menu-main-container-scroller"> 
-	                    <bx_menu:sys_site_submenu />
-	                </div>
-	            </div>
-	
+
+	            <bx_menu:sys_site_submenu />
+
 	            <bx_injection:injection_top_menu_after />
-	
+
 	        </div>
 	
 	    </div>

+ 7 - 11
template/_sub_header.html

@@ -1,6 +1,7 @@
-
 <bx_include_auto:_header.html />
+
 <div class="bx-main">
+
 	<bx_injection:top />
 
 	<div id="bx-toolbar" class="bx-def-z-index-nav">
@@ -13,7 +14,6 @@
 	                <bx_menu:sys_toolbar_site />
 	            </div>
 	
-	
 	            <div id="bx-logo-container">
 	
 	                <bx_injection:injection_logo_before />
@@ -39,17 +39,13 @@
 	    <div id="bx-menu-main-bar" class="bx-def-color-bg-sec">
 	
 	        <div class="bx-def-centered bx-def-padding-sec-left bx-def-padding-sec-right bx-def-page-width">
-	
+
 	            <bx_injection:injection_top_menu_before />
-	
-	            <div id="bx-menu-main-container">
-	                <div id="bx-menu-main-container-scroller"> 
-	                    <bx_menu:sys_site_submenu />
-	                </div>
-	            </div>
-	
+
+        	    <bx_menu:sys_site_submenu />
+
 	            <bx_injection:injection_top_menu_after />
-	
+
 	        </div>
 	
 	    </div>

+ 42 - 1
template/css/common.css

@@ -63,7 +63,13 @@ blockquote {
 
 .sys-icon-a {
     width:1.5rem;
-    height:1.5rem;
+    height:1.5rem;    
+}
+
+.bx-search-site {
+    width:20rem;
+    margin-left:auto;
+    margin-right:auto;
 }
 
 /* --------------- design boxes */
@@ -97,8 +103,36 @@ blockquote {
     text-decoration:none;
 }
 
+.bx-db-menu .sys-icon-a {
+    vertical-align:middle;
+}
+
 /* --------------- popup boxes */
 
+.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 {
+    -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-width {
 	width: 500px;
 }
@@ -186,6 +220,13 @@ blockquote {
     text-align:right;
 }
 
+.bx-popup-image-wrapper .bx-popup-close-wrapper,
+.bx-popup-image-wrapper .bx-popup-element-close,
+.bx-popup-with-pointer .bx-popup-close-wrapper,
+.bx-popup-with-pointer .bx-popup-element-close {
+    display:none;
+}
+
 /* --------------- message box */
 
 .bx-msg-box-container {

+ 12 - 7
template/css/media-phone.css

@@ -42,8 +42,10 @@ html {
 
 /* common.css */
 
-.bx-popup-box-wrapper,
-.bx-popup-box {
+.bx-popup-responsive.bx-popup-trans-wrapper:not(.bx-popup-image-wrapper),
+.bx-popup-responsive:not(.bx-popup-image-wrapper) .bx-popup-trans,
+.bx-popup-responsive.bx-popup-box-wrapper,
+.bx-popup-responsive .bx-popup-box {
     position:fixed !important;
     width:100%;
 	width:100vw;
@@ -52,19 +54,22 @@ html {
     overflow:scroll;
 }
 
-.bx-popup-box > div {
+.bx-popup-responsive .bx-popup-trans > div,
+.bx-popup-responsive .bx-popup-box > div {
 	border-radius:0;
 	-moz-border-radius:0;
 	-webkit-border-radius:0;
 }
 
-.bx-popup-image-wrapper .bx-popup-border,
-.bx-popup-box-wrapper .bx-popup-border {
+.bx-popup-responsive.bx-popup-image-wrapper .bx-popup-border,
+.bx-popup-responsive.bx-popup-trans-wrapper .bx-popup-border,
+.bx-popup-responsive.bx-popup-box-wrapper .bx-popup-border {
     border:none;
 }
 
-.bx-popup-image-wrapper .bx-popup,
-.bx-popup-box-wrapper .bx-popup {
+.bx-popup-responsive.bx-popup-image-wrapper .bx-popup,
+.bx-popup-responsive.bx-popup-trans-wrapper .bx-popup,
+.bx-popup-responsive.bx-popup-box-wrapper .bx-popup {
 	-moz-box-shadow:none;
 	-webkit-box-shadow:none;
     box-shadow:none;

+ 12 - 12
template/css/menu.css

@@ -288,10 +288,10 @@ ul.bx-menu-floating-blocks {
     height:4rem;
 }
 .bx-menu-floating-blocks-big li {
-    width:256px;
-    width:16rem;
-    height:256px;
-    height:16rem;
+    width:192px;
+    width:12rem;
+    height:192px;
+    height:12rem;
 }
 
 .bx-menu-floating-blocks-big li:hover,
@@ -330,10 +330,10 @@ ul.bx-menu-floating-blocks {
 .bx-menu-floating-blocks-big li img,
 .bx-menu-floating-blocks-big li .sys-icon {
     display:block;
-    width:192px;
-    width:12rem;
-    height:192px;
-    height:12rem;
+    width:128px;
+    width:8rem;
+    height:128px;
+    height:8rem;
     margin:16px 32px 8px 32px;
     margin:1rem 2rem 0.5rem 2rem;
 }
@@ -354,10 +354,10 @@ ul.bx-menu-floating-blocks {
     font-size:2rem;
 }
 .bx-menu-floating-blocks-big li .sys-icon {
-    line-height:192px;
-    line-height:12rem;
-    font-size:192px;
-    font-size:12rem;
+    line-height:128px;
+    line-height:8rem;
+    font-size:128px;
+    font-size:8rem;
 }
 
 .bx-menu-floating-blocks-big li .bx-menu-floating-blocks-title,

+ 5 - 0
template/menu_main_submenu_wrapper.html

@@ -0,0 +1,5 @@
+<div id="bx-menu-main-container">
+    <div id="bx-menu-main-container-scroller">
+        __menu__
+    </div>
+</div>

+ 0 - 5
template/menu_site.html

@@ -1,5 +0,0 @@
-<div id="bx-menu-site">
-    <div id="bx-menu-site-search">__search__</div>
-    <div class="bx-def-color-bg-sec bx-def-margin-top bx-def-margin-bottom" style="height:0.5em; margin-left:-3em; margin-right:-3em;"></div>
-    <div id="bx-menu-site-menu">__menu__</div>
-</div>

+ 1 - 1
template/menu_toolbar.html

@@ -1,7 +1,7 @@
 <ul class="bx-menu-toolbar bx-menu-object-__object__">
     <bx_repeat:menu_items>
         <li class="bx-menu-toolbar-item bx-def-color-bg-hl-hover __class_add__">
-            <a href="__link__" target="__target__" onclick="__onclick__" class="bx-def-margin-sec-left bx-def-font-contrasted"><bx_if:title><span class="bx-menu-toolbar-item-title bx-def-margin-sec-leftright">__title__</span></bx_if:title><bx_if:icon><i class="sys-icon __icon__ bx-def-margin-sec-leftright"></i></bx_if:icon><bx_if:image><img class="bx-def-thumb bx-def-thumb-size bx-def-margin-sec-leftright" src="__icon_url__" /></bx_if:image></a>
+            <a href="__link__" target="__target__" onclick="__onclick__" class="bx-def-font-contrasted"><bx_if:title><span class="bx-menu-toolbar-item-title bx-def-margin-sec-leftright">__title__</span></bx_if:title><bx_if:icon><i class="sys-icon __icon__ bx-def-margin-sec-leftright"></i></bx_if:icon><bx_if:image><img class="bx-def-thumb bx-def-thumb-size bx-def-margin-sec-leftright" src="__icon_url__" /></bx_if:image></a>
             <bx_if:addon><span class="bx-menu-toolbar-item-addon bx-def-unit-alert-middle bg-col-blue2-dark">__addon__</span></bx_if:addon>
         </li>
     </bx_repeat:menu_items>

+ 2 - 2
template/popup_box.html

@@ -1,10 +1,10 @@
-<div id="__id__" class="bx-popup-wrapper bx-popup-box-wrapper" style="__wrapper_style__" role="dialog" aria-describedby="__id___title">
+<div id="__id__" class="bx-popup-wrapper bx-popup-responsive bx-popup-box-wrapper" style="__wrapper_style__" role="dialog" aria-describedby="__id___title">
     <div class="bx-popup-box-pointer"><i class="bx-popup-border"></i></div>
     <div class="bx-popup bx-popup-box bx-popup-width bx-popup-color-bg bx-popup-border bx-def-round-corners">
     <div class="bx-def-round-corners" style="overflow:hidden;">
         <div class="bx-popup-header bx-popup-color-bg bx-def-padding bx-def-font-contrasted">
             <h2 id="__id___title">__title__</h2>
-            <a href="javascript:void(0);" class="bx-popup-element-close bx-def-font-h2 bx-def-font-contrasted bx-def-margin bx-def-media-tablet-hide bx-def-media-desktop-hide"><i class="sys-icon remove"></i></a>
+            <a href="javascript:void(0);" class="bx-popup-element-close bx-def-font-h1 bx-def-font-contrasted bx-def-margin bx-def-media-tablet-hide bx-def-media-desktop-hide"><i class="sys-icon remove"></i></a>
         </div>
         <div class="bx-popup-content bx-def-padding">__content__</div>
     </div>

+ 1 - 1
template/popup_loading.html

@@ -1,4 +1,4 @@
 <div class="bx-popup-content-indent bx-def-padding">
 	<div class="bx-popup-content-wrapped"></div>
 	<div class="bx-popup-loading-wrapped"></div>
-</div>
+</div>

+ 4 - 1
template/popup_trans.html

@@ -1,6 +1,9 @@
-<div  id="__id__" class="bx-popup-wrapper bx-popup-trans-wrapper" style="__wrapper_style__" role="alert">
+<div  id="__id__" class="bx-popup-wrapper bx-popup-responsive bx-popup-trans-wrapper" style="__wrapper_style__" role="alert">
     <div class="bx-popup-box-pointer"><i class="bx-popup-border"></i></div>
     <div class="bx-popup bx-popup-trans bx-popup-border bx-popup-color-bg bx-def-round-corners">
+        <div class="bx-popup-close-wrapper bx-def-media-tablet-hide bx-def-media-desktop-hide bx-def-padding">
+            <a href="javascript:void(0);" class="bx-popup-element-close bx-def-font-h1 bx-def-margin"><i class="sys-icon remove"></i></a>
+        </div>
         <div class="bx-popup-content" style="">__content__</div>
     </div>
 </div>

+ 0 - 18
template/scripts/BxBaseMenuSite.php

@@ -14,28 +14,10 @@ bx_import('BxTemplMenu');
  */
 class BxBaseMenuSite extends BxTemplMenu
 {
-
-    protected $_sObjectSubmenu = false;
-    protected $_mixedMainMenuItemSelected = false;
-
     public function __construct ($aObject, $oTemplate)
     {
         parent::__construct ($aObject, $oTemplate);
     }
-
-    public function getCode ()
-    {
-        bx_import('BxTemplSearch');
-        $oSearch = new BxTemplSearch();
-        $oSearch->setLiveSearch(true);
-
-        $aVars = array (
-            'menu' => parent::getCode (),
-            'search' => $oSearch->getForm(BX_DB_CONTENT_ONLY) . $oSearch->getResultsContainer(),
-        );
-        return $this->_oTemplate->parseHtmlByName('menu_site.html', $aVars);
-    }
-
 }
 
 /** @} */

+ 7 - 6
template/scripts/BxBaseMenuSubmenu.php

@@ -64,12 +64,13 @@ class BxBaseMenuSubmenu extends BxTemplMenu
         if ($this->_sObjectSubmenu)
             $oSubmenu = BxDolMenu::getObjectInstance($this->_sObjectSubmenu);
 
-        if (!$oSubmenu) {
-            $oSubmenu = BxDolMenu::getObjectInstance('sys_site_submenu_main');
-            return $oSubmenu->getCode();
-        } else {
-            return $this->_oTemplate->parseHtmlByName($this->_aObject['template'], $aVars) . $oSubmenu->getCode();
-        }
+        if (!$oSubmenu)
+            return '';
+
+        $sMenu = $this->_oTemplate->parseHtmlByName($this->_aObject['template'], $aVars) . $oSubmenu->getCode();
+
+        $aVars = array('menu' => $sMenu);
+        return $this->_oTemplate->parseHtmlByName('menu_main_submenu_wrapper.html', $aVars);
     }
 
     protected function _getSelectedMenuItem ()

+ 4 - 0
template/search.html

@@ -0,0 +1,4 @@
+<div class="bx-search-site bx-def-padding">
+    __search_form__
+    __results__
+</div>

+ 8 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/check.php

@@ -0,0 +1,8 @@
+<?php
+
+$mixCheckResult = 'Update can not be applied';
+
+if ('8.0.0-A6' == $this->oDb->getOne("SELECT `version` FROM `sys_modules` WHERE `name` = 'system'"))
+    $mixCheckResult = true;
+
+return $mixCheckResult;

+ 978 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolCmts.php

@@ -0,0 +1,978 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolPermalinks');
+bx_import('BxDolCmtsQuery');
+
+define('BX_CMT_OLD_VOTES', 365*86400); ///< comment votes older than this number of seconds will be deleted automatically
+
+define('BX_CMT_ACTION_POST', 'post');
+define('BX_CMT_ACTION_EDIT', 'edit');
+
+define('BX_CMT_DISPLAY_FLAT', 'flat');
+define('BX_CMT_DISPLAY_THREADED', 'threaded');
+
+define('BX_CMT_BROWSE_HEAD', 'head');
+define('BX_CMT_BROWSE_TAIL', 'tail');
+define('BX_CMT_BROWSE_POPULAR', 'popular');
+define('BX_CMT_BROWSE_CONNECTION', 'connection');
+
+define('BX_CMT_ORDER_BY_DATE', 'date');
+define('BX_CMT_ORDER_BY_POPULAR', 'popular');
+
+define('BX_CMT_FILTER_ALL', 'all');
+define('BX_CMT_FILTER_FRIENDS', 'friends');
+define('BX_CMT_FILTER_SUBSCRIPTIONS', 'subscriptions');
+
+define('BX_CMT_ORDER_WAY_ASC', 'asc');
+define('BX_CMT_ORDER_WAY_DESC', 'desc');
+
+define('BX_CMT_PFP_TOP', 'top');
+define('BX_CMT_PFP_BOTTOM', 'bottom');
+
+define('BX_CMT_RATE_VALUE_PLUS', 1);
+define('BX_CMT_RATE_VALUE_MINUS', -1);
+
+/**
+ * @page objects
+ * @section comments Comments
+ * @ref BxDolCmts
+ */
+
+/**
+ * Comments for any content
+ *
+ * Related classes:
+ * - BxDolCmtsQuery - comments database queries
+ * - BxBaseCmts - comments base representation
+ * - BxTemplCmts - custom template representation
+ *
+ * AJAX comments for any content.
+ * Self moderated - users rate all comments, and if comment is
+ * below viewing treshold it is hidden by default.
+ *
+ * To add comments section to your module you need to add a record to 'sys_objects_cmts' table:
+ *
+ * - ID - autoincremented id for internal usage
+ * - ObjectName - your unique module name, with vendor prefix, lowercase and spaces are underscored
+ * - TableCmts - table name where comments are stored
+ * - Nl2br - convert new lines to <br /> on saving
+ * - PerView - number of comments on a page
+ * - IsRatable - 0 or 1 allow to rate comments or not
+ * - ViewingThreshold - comment viewing treshost, if comment is below this number it is hidden by default
+ * - IsOn - is this comment object enabled
+ * - RootStylePrefix - toot comments style prefix, if you need root comments look different\
+ * - ObjectVote - Vote object name to process comments' votes. May be empty if Comment Vote is not needed.
+ * - TriggerTable - table to be updated upon each comment
+ * - TriggerFieldId - TriggerTable table field with unique record id of
+ * - TriggerFieldComments - TriggerTable table field with comments count, it will be updated automatically upon eaech comment
+ * - ClassName - your custom class name if you need to override default class, this class must have the same constructor arguments
+ * - ClassFile - file where your ClassName is stored.
+ *
+ * You can refer to BoonEx modules for sample record in this table.
+ *
+ *
+ *
+ * @section example Example of usage:
+ * After filling in the table you can show comments section in any place, using the following code:
+ *
+ * @code
+ * bx_import ('BxTemplCmts');
+ * $o = new BxTemplCmts('value of ObjectName field', $iYourEntryId);
+ * if ($o->isEnabled())
+ *     echo $o->getCommentsFirst ();
+ * @endcode
+ *
+ * Please note that you never need to use BxDolCmts class directly, use BxTemplCmts instead.
+ * Also if you override comments class with your own then make it child of BxTemplCmts class.
+ *
+ *
+ *
+ * @section acl Memberships/ACL:
+ * - comments post
+ * - comments edit own
+ * - comments remove own
+ * - comments edit all
+ *
+ *
+ *
+ * @section alerts Alerts:
+ * Alerts type/unit - every module has own type/unit, it equals to ObjectName.
+ *
+ * The following alerts are rised
+ *
+ * - commentPost - comment was posted
+ *      - $iObjectId - entry id
+ *      - $iSenderId - author of comment
+ *      - $aExtra['comment_id'] - just added comment id
+ *
+ * - commentRemoved - comments was removed
+ *      - $iObjectId - entry id
+ *      - $iSenderId - comment deleter id
+ *      - $aExtra['comment_id'] - removed comment id
+ *
+ * - commentUpdated - comments was updated
+ *      - $iObjectId - entry id
+ *      - $iSenderId - comment deleter id
+ *      - $aExtra['comment_id'] - updated comment id
+ *
+ * - commentRated - comments was rated
+ *      - $iObjectId - entry id
+ *      - $iSenderId - comment rater id
+ *      - $aExtra['comment_id'] - rated comment id
+ *      - $aExtra['rate'] - comment rate 1 or -1
+ *
+ */
+class BxDolCmts extends BxDol implements iBxDolReplaceable
+{
+    protected $_oQuery = null;
+
+    protected $_sFormObject;
+    protected $_sFormDisplayPost;
+    protected $_sFormDisplayEdit;
+
+    protected $_sTableImages;
+    protected $_sTableImages2Entries;
+
+    protected $_sConnObjFriends;
+    protected $_sConnObjSubscriptions;
+
+    protected $_sMenuObjManage;
+    protected $_sMenuObjActions;
+
+    protected $_sViewUrl = '';
+    protected $_sBaseUrl = '';
+    protected $_sListAnchor = '';
+
+    protected $_sSystem = 'profile'; ///< current comment system name
+    protected $_aSystem = array (); ///< current comments system array
+    protected $_iId = 0; ///< obect id to be commented
+
+    protected $_aMarkers = array ();
+
+    protected $_sDisplayType = '';
+    protected $_sDpSessionKey = '';
+    protected $_iDpMaxLevel = 0;
+
+    protected $_sBrowseType = '';
+    protected $_sBrowseFilter = '';
+    protected $_sBpSessionKeyType = '';
+    protected $_sBpSessionKeyFilter = '';
+    protected $_aOrder = array();
+
+    protected $_sSnippetLenthLiveSearch = 50;
+
+    protected $_iRememberTime = 2592000;
+
+    /**
+     * Constructor
+     * $sSystem - comments system name
+     * $iId - obect id to be commented
+     */
+    function __construct($sSystem, $iId, $iInit = 1)
+    {
+        parent::__construct();
+
+        $this->_aSystems = $this->getSystems();
+        if(!isset($this->_aSystems[$sSystem]))
+            return;
+
+        $this->_sSystem = $sSystem;
+        $this->_aSystem = $this->_aSystems[$sSystem];
+
+        $this->_aSystem['table_images'] = 'sys_cmts_images';
+        $this->_aSystem['table_images2entries'] = 'sys_cmts_images2entries';
+
+        $this->_iDpMaxLevel = (int)$this->_aSystem['number_of_levels'];
+        $this->_sDisplayType = $this->_iDpMaxLevel == 0 ? BX_CMT_DISPLAY_FLAT : BX_CMT_DISPLAY_THREADED;
+        $this->_sDpSessionKey = 'bx_' . $this->_sSystem . '_dp_';
+
+        $this->_sBrowseType = $this->_aSystem['browse_type'];
+        $this->_sBrowseFilter = BX_CMT_FILTER_ALL;
+        $this->_sBpSessionKeyType = 'bx_' . $this->_sSystem . '_bpt_';
+        $this->_sBpSessionKeyFilter = 'bx_' . $this->_sSystem . '_bpf_';
+        $this->_aOrder = array(
+            'by' => BX_CMT_ORDER_BY_DATE,
+            'way' => BX_CMT_ORDER_WAY_ASC
+        );
+
+        list($mixedUserDp, $mixedUserBpType, $mixedUserBpFilter) = $this->_getUserChoice();
+        if(!empty($mixedUserDp))
+            $this->_sDisplayType = $mixedUserDp;
+        if(!empty($mixedUserBpType))
+            $this->_sBrowseType = $mixedUserBpType;
+        if(!empty($mixedUserBpFilter))
+            $this->_sBrowseFilter = $mixedUserBpFilter;
+
+        $this->_sViewUrl = BX_DOL_URL_ROOT . 'cmts.php';
+        $this->_sBaseUrl = BxDolPermalinks::getInstance()->permalink($this->_aSystem['base_url']);
+        if(get_mb_substr($this->_sBaseUrl, 0, 4) != 'http')
+            $this->_sBaseUrl = BX_DOL_URL_ROOT . $this->_sBaseUrl;
+        $this->_sListAnchor = "cmts-anchor-%s-%d";
+
+        $this->_oQuery = new BxDolCmtsQuery($this);
+
+        $this->_sFormObject = 'sys_comment';
+        $this->_sFormDisplayPost = 'sys_comment_post';
+        $this->_sFormDisplayEdit = 'sys_comment_edit';
+
+        $this->_sConnObjFriends = 'sys_profiles_friends';
+        $this->_sConnObjSubscriptions = 'sys_profiles_subscriptions';
+
+        $this->_sMenuObjManage = 'sys_cmts_item_manage';
+        $this->_sMenuObjActions = 'sys_cmts_item_actions';
+
+        if ($iInit)
+            $this->init($iId);
+    }
+
+    /**
+     * get comments object instanse
+     * @param $sSys comments object name
+     * @param $iId associated content id, where comments are postred in
+     * @param $iInit perform initialization
+     * @return null on error, or ready to use class instance
+     */
+    public static function getObjectInstance($sSys, $iId, $iInit = true)
+    {
+        if(isset($GLOBALS['bxDolClasses']['BxDolCmts!' . $sSys . $iId]))
+            return $GLOBALS['bxDolClasses']['BxDolCmts!' . $sSys . $iId];
+
+        $aSystems = self::getSystems();
+        if (!isset($aSystems[$sSys]))
+            return null;
+
+        bx_import('BxTemplCmts');
+        $sClassName = 'BxTemplCmts';
+        if(!empty($aSystems[$sSys]['class_name'])) {
+            $sClassName = $aSystems[$sSys]['class_name'];
+            if(!empty($aSystems[$sSys]['class_file']))
+                require_once(BX_DIRECTORY_PATH_ROOT . $aSystems[$sSys]['class_file']);
+            else
+                bx_import($sClassName);
+        }
+
+        $o = new $sClassName($sSys, $iId, $iInit);
+        return ($GLOBALS['bxDolClasses']['BxDolCmts!' . $sSys . $iId] = $o);
+    }
+
+    public static function &getSystems ()
+    {
+        if (!isset($GLOBALS['bx_dol_cmts_systems'])) {
+            $GLOBALS['bx_dol_cmts_systems'] = BxDolDb::getInstance()->fromCache('sys_objects_cmts', 'getAllWithKey', '
+                SELECT
+                    `ID` as `system_id`,
+                    `Name` AS `name`,
+                    `Table` AS `table`,
+                    `CharsPostMin` AS `chars_post_min`,
+                    `CharsPostMax` AS `chars_post_max`,
+                    `CharsDisplayMax` AS `chars_display_max`,
+                    `Nl2br` AS `nl2br`,
+                    `PerView` AS `per_view`,
+                    `PerViewReplies` AS `per_view_replies`,
+                    `BrowseType` AS `browse_type`,
+                    `IsBrowseSwitch` AS `is_browse_switch`,
+                    `PostFormPosition` AS `post_form_position`,
+                    `NumberOfLevels` AS `number_of_levels`,
+                    `IsDisplaySwitch` AS `is_display_switch`,
+                    `IsRatable` AS `is_ratable`,
+                    `ViewingThreshold` AS `viewing_threshold`,
+                    `IsOn` AS `is_on`,
+                    `RootStylePrefix` AS `root_style_prefix`,
+                    `BaseUrl` AS `base_url`,
+                    `ObjectVote` AS `object_vote`,
+                    `TriggerTable` AS `trigger_table`,
+                    `TriggerFieldId` AS `trigger_field_id`,
+                    `TriggerFieldTitle` AS `trigger_field_title`,
+                    `TriggerFieldComments` AS `trigger_field_comments`,
+                    `ClassName` AS `class_name`,
+                    `ClassFile` AS `class_file`
+                FROM `sys_objects_cmts`', 'name');
+        }
+        return $GLOBALS['bx_dol_cmts_systems'];
+    }
+
+    public function init ($iId)
+    {
+        if (empty($this->iId) && $iId)
+            $this->setId($iId);
+
+        $this->addMarkers(array(
+            'object_id' => $this->getId(),
+            'user_id' => $this->_getAuthorId()
+        ));
+    }
+
+    public function getId ()
+    {
+        return $this->_iId;
+    }
+
+    public function isEnabled ()
+    {
+        return isset($this->_aSystem['is_on']) && $this->_aSystem['is_on'];
+    }
+
+    public function getSystemId()
+    {
+        return $this->_aSystem['system_id'];
+    }
+
+    public function getSystemName()
+    {
+        return $this->_sSystem;
+    }
+
+    public function getStorageObjectName()
+    {
+    	return $this->_getFormObject()->getStorageObjectName();
+    }
+
+	public function getTranscoderPreviewName()
+    {
+    	return $this->_getFormObject()->getTranscoderPreviewName();
+    }
+
+    public function getSystemInfo()
+    {
+        return $this->_aSystem;
+    }
+
+    public function getMaxLevel()
+    {
+        return $this->_iDpMaxLevel;
+    }
+
+    public function getOrder ()
+    {
+        return $this->_sOrder;
+    }
+
+    public function getPerView ($iCmtParentId = 0)
+    {
+        return $iCmtParentId == 0 ? $this->_aSystem['per_view'] : $this->_aSystem['per_view_replies'];
+    }
+
+    public function getBaseUrl()
+    {
+        return $this->_replaceMarkers($this->_sBaseUrl);
+    }
+
+    public function getListUrl()
+    {
+        return $this->getBaseUrl() . '#' . $this->getListAnchor();
+    }
+
+    public function getListAnchor()
+    {
+        return sprintf($this->_sListAnchor, str_replace('_', '-', $this->getSystemName()), $this->getId());
+    }
+
+	public function getViewUrl($iCmtId)
+    {
+    	if(empty($this->_aSystem['trigger_field_title']))
+    		return '';
+
+    	return bx_append_url_params($this->_sViewUrl, array(
+			'sys' => $this->_sSystem,
+			'id' => $this->_iId,
+			'cmt_id' => $iCmtId
+		));
+    }
+
+    public function getConnectionObject($sType)
+    {
+        $sResult = '';
+
+        switch($sType) {
+            case BX_CMT_FILTER_FRIENDS:
+                $sResult = $this->_sConnObjFriends;
+                break;
+            case BX_CMT_FILTER_SUBSCRIPTIONS:
+                $sResult = $this->_sConnObjSubscriptions;
+                break;
+        }
+
+        return $sResult;
+    }
+
+    public function getVoteObject($iId)
+    {
+        if(empty($this->_aSystem['object_vote']))
+            return false;
+
+        bx_import('BxDolVote');
+        $oVote = BxDolVote::getObjectInstance($this->_aSystem['object_vote'], $iId);
+        if(!$oVote->isEnabled())
+            return false;
+
+        return $oVote;
+    }
+
+    public function isNl2br ()
+    {
+        return $this->_aSystem['nl2br'];
+    }
+
+    public function isRatable ()
+    {
+        return $this->_aSystem['is_ratable'];
+    }
+
+    public function isAttachImageEnabled()
+    {
+        return true;
+    }
+
+    /**
+     * set id to operate with votes
+     */
+    public function setId ($iId)
+    {
+        if ($iId == $this->getId()) return;
+        $this->_iId = $iId;
+    }
+
+    /**
+     * Add replace markers.
+     * @param $a array of markers as key => value
+     * @return true on success or false on error
+     */
+    public function addMarkers ($a)
+    {
+        if (empty($a) || !is_array($a))
+            return false;
+        $this->_aMarkers = array_merge ($this->_aMarkers, $a);
+        return true;
+    }
+
+    /**
+     * Database functions
+     */
+    public function getQueryObject ()
+    {
+        return $this->_oQuery;
+    }
+
+    public function getCommentsTableName ()
+    {
+        return $this->_oQuery->getTableName ();
+    }
+
+    public function getObjectTitle ($iObjectId = 0)
+    {
+        return $this->_oQuery->getObjectTitle ($iObjectId ? $iObjectId : $this->getId());
+    }
+
+    public function getCommentsCount ($iObjectId = 0, $iCmtVParentId = 0, $sFilter = '')
+    {
+        return $this->_oQuery->getCommentsCount ($iObjectId ? $iObjectId : $this->getId(), $iCmtVParentId, $this->_getAuthorId(), $sFilter);
+    }
+
+    public function getCommentsArray ($iVParentId, $sFilter, $aOrder, $iStart = 0, $iCount = -1)
+    {
+        return $this->_oQuery->getComments ($this->getId(), $iVParentId, $this->_getAuthorId(), $sFilter, $aOrder, $iStart, $iCount);
+    }
+
+    public function getCommentRow ($iCmtId)
+    {
+        return $this->_oQuery->getComment ($this->getId(), $iCmtId);
+    }
+
+    public function onObjectDelete ($iObjectId = 0)
+    {
+        // delete comments
+        $aFiles = array();
+        $this->_oQuery->deleteObjectComments ($iObjectId ? $iObjectId : $this->getId(), $aFiles);
+
+        // delete files
+        if ($aFiles) {
+            bx_import('BxDolStorage');
+            $oStorage = BxDolStorage::getObjectInstance($this->getStorageObjectName());
+            if ($oStorage)
+                $oStorage->queueFilesForDeletion($aFiles);
+        }
+    }
+
+    public static function onAuthorDelete ($iAuthorId)
+    {
+        bx_import('BxDolStorage');
+        $aSystems = self::getSystems();
+        foreach($aSystems as $sSystem => $aSystem) {
+            $o = self::getObjectInstance($sSystem, 0);
+            $oQuery = $o->getQueryObject();
+
+            // delete comments
+            $aFiles = array ();
+            $oQuery->deleteAuthorComments($iAuthorId, $aFiles);
+
+            // delete files
+            $oStorage = BxDolStorage::getObjectInstance($o->getStorageObjectName());
+            if ($oStorage)
+                $oStorage->queueFilesForDeletion($aFiles);
+        }
+        return true;
+    }
+
+    public static function onModuleUninstall ($sModuleName, &$iFiles = null)
+    {
+        bx_import('BxDolStorage');
+        $aSystems = self::getSystems();
+        foreach($aSystems as $sSystem => $aSystem) {
+            if (0 != strncasecmp($sModuleName, $sSystem, strlen($sModuleName)))
+                continue;
+
+            $o = self::getObjectInstance($sSystem, 0);
+            $oQuery = $o->getQueryObject();
+
+            // delete comments
+            $aFiles = array ();
+            $oQuery->deleteAll($aSystem['system_id'], $aFiles);
+
+            // delete files
+            $oStorage = BxDolStorage::getObjectInstance($o->getStorageObjectName());
+            if ($oStorage && $aFiles)
+                $oStorage->queueFilesForDeletion($aFiles);
+
+            if (null !== $iFiles)
+                $iFiles += count($aFiles);
+        }
+
+        return true;
+    }
+
+    /**
+     * Permissions functions
+     */
+    public function checkAction ($sAction, $isPerformAction = false)
+    {
+        $iId = $this->_getAuthorId();
+        $a = checkActionModule($iId, $sAction, 'system', $isPerformAction);
+        return $a[CHECK_ACTION_RESULT] === CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function checkActionErrorMsg ($sAction)
+    {
+        $iId = $this->_getAuthorId();
+        $a = checkActionModule($iId, $sAction, 'system');
+        return $a[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED ? $a[CHECK_ACTION_MESSAGE] : '';
+    }
+
+    public function isVoteAllowed ($aCmt, $isPerformAction = false)
+    {
+        if(!$this->isRatable())
+            return false;
+
+        $oVote = $this->getVoteObject($aCmt['cmt_id']);
+        if($oVote === false)
+            return false;
+
+        $iUserId = (int)$this->_getAuthorId();
+        if($iUserId == 0)
+            return false;
+
+        if(isAdmin())
+            return true;
+
+        return $oVote->isAllowedVote($isPerformAction);
+    }
+
+    public function isPostReplyAllowed ($isPerformAction = false)
+    {
+        return $this->checkAction ('comments post', $isPerformAction);
+    }
+
+    public function msgErrPostReplyAllowed ()
+    {
+        return $this->checkActionErrorMsg('comments post');
+    }
+
+    public function isEditAllowed ($aCmt, $isPerformAction = false)
+    {
+        if(isAdmin())
+            return true;
+
+        if($aCmt['cmt_author_id'] == $this->_getAuthorId() && $this->checkAction ('comments edit own', $isPerformAction))
+            return true;
+
+        return $this->checkAction('comments edit all', $isPerformAction);
+    }
+
+    public function msgErrEditAllowed ()
+    {
+        return $this->checkActionErrorMsg ('comments edit own');
+    }
+
+    public function isRemoveAllowed ($aCmt, $isPerformAction = false)
+    {
+        if(isAdmin())
+            return true;
+
+        if($aCmt['cmt_author_id'] == $this->_getAuthorId() && $this->checkAction ('comments remove own', $isPerformAction))
+            return true;
+
+        return $this->checkAction ('comments remove all', $isPerformAction);
+    }
+
+    public function msgErrRemoveAllowed ()
+    {
+        return $this->checkActionErrorMsg('comments remove own');
+    }
+
+    /**
+     * Actions functions
+     */
+    public function actionGetFormPost ()
+    {
+        if (!$this->isEnabled())
+            return '';
+
+        $iCmtParentId= isset($_REQUEST['CmtParent']) ? bx_process_input($_REQUEST['CmtParent'], BX_DATA_INT) : 0;
+        $sCmtBrowse = isset($_REQUEST['CmtBrowse']) ? bx_process_input($_REQUEST['CmtBrowse'], BX_DATA_TEXT) : '';
+        $sCmtDisplay = isset($_REQUEST['CmtDisplay']) ? bx_process_input($_REQUEST['CmtDisplay'], BX_DATA_TEXT) : '';
+
+        return $this->getFormBoxPost(array('parent_id' => $iCmtParentId, 'type' => $sCmtBrowse), array('type' => $sCmtDisplay));
+    }
+
+    public function actionGetFormEdit ()
+    {
+        if (!$this->isEnabled()){
+            $this->_echoResultJson(array());
+            return;
+        }
+
+        $iCmtId = bx_process_input(bx_get('Cmt'), BX_DATA_INT);
+        $this->_echoResultJson($this->getFormEdit($iCmtId));
+    }
+
+    public function actionGetCmt ()
+    {
+        if (!$this->isEnabled())
+            return '';
+
+        $iCmtId = bx_process_input($_REQUEST['Cmt'], BX_DATA_INT);
+        $sCmtBrowse = isset($_REQUEST['CmtBrowse']) ? bx_process_input($_REQUEST['CmtBrowse'], BX_DATA_TEXT) : '';
+        $sCmtDisplay = isset($_REQUEST['CmtDisplay']) ? bx_process_input($_REQUEST['CmtDisplay'], BX_DATA_TEXT) : '';
+
+        $aCmt = $this->getCommentRow($iCmtId);
+        $this->_echoResultJson(array(
+            'parent_id' => $aCmt['cmt_parent_id'],
+            'vparent_id' => $aCmt['cmt_vparent_id'],
+            'content' => $this->getComment($aCmt, array('type' => $sCmtBrowse), array('type' => $sCmtDisplay))
+        ));
+    }
+
+    public function actionGetCmts ()
+    {
+        if (!$this->isEnabled())
+            return '';
+
+        $iCmtVParentId = bx_process_input($_REQUEST['CmtParent'], BX_DATA_INT);
+        $iCmtStart = isset($_REQUEST['CmtStart']) ? bx_process_input($_REQUEST['CmtStart'], BX_DATA_INT) : -1;
+        $iCmtPerView = isset($_REQUEST['CmtPerView']) ? bx_process_input($_REQUEST['CmtPerView'], BX_DATA_INT) : -1;
+        $sCmtBrowse = isset($_REQUEST['CmtBrowse']) ? bx_process_input($_REQUEST['CmtBrowse'], BX_DATA_TEXT) : '';
+        $sCmtDisplay = isset($_REQUEST['CmtDisplay']) ? bx_process_input($_REQUEST['CmtDisplay'], BX_DATA_TEXT) : '';
+        $sCmtFilter = isset($_REQUEST['CmtFilter']) ? bx_process_input($_REQUEST['CmtFilter'], BX_DATA_TEXT) : '';
+
+        return $this->getComments(array('vparent_id' => $iCmtVParentId, 'start' => $iCmtStart, 'per_view' => $iCmtPerView, 'type' => $sCmtBrowse, 'filter' => $sCmtFilter), array('type' => $sCmtDisplay, 'show_empty' => true));
+    }
+
+    public function actionSubmitPostForm()
+    {
+        if(!$this->isEnabled() || !$this->isPostReplyAllowed()) {
+            $this->_echoResultJson(array());
+            return;
+        }
+
+        $iCmtParentId = 0;
+        if(bx_get('cmt_parent_id') !== false)
+            $iCmtParentId = bx_process_input(bx_get('cmt_parent_id'), BX_DATA_INT);
+
+        $this->_echoResultJson($this->getFormPost($iCmtParentId));
+    }
+
+    public function actionSubmitEditForm()
+    {
+        if (!$this->isEnabled()) {
+            $this->_echoResultJson(array());
+            return;
+        };
+
+        $iCmtId = 0;
+        if(bx_get('cmt_id') !== false)
+            $iCmtId = bx_process_input(bx_get('cmt_id'), BX_DATA_INT);
+
+        $this->_echoResultJson($this->getFormEdit($iCmtId));
+    }
+
+    public function actionRemove()
+    {
+        if (!$this->isEnabled()) {
+            $this->_echoResultJson(array());
+            return;
+        };
+
+        $iCmtId = 0;
+        if(bx_get('Cmt') !== false)
+            $iCmtId = bx_process_input(bx_get('Cmt'), BX_DATA_INT);
+
+        $aCmt = $this->_oQuery->getCommentSimple ($this->getId(), $iCmtId);
+        if(!$aCmt) {
+            $this->_echoResultJson(array('msg' => _t('_No such comment')));
+            return;
+        }
+
+        if ($aCmt['cmt_replies'] > 0) {
+            $this->_echoResultJson(array('msg' => _t('_Can not delete comments with replies')));
+            return;
+        }
+
+        $iCmtAuthorId = $this->_getAuthorId();
+        if(!$this->isRemoveAllowed($aCmt)) {
+            $this->_echoResultJson(array('msg' => $aCmt['cmt_author_id'] == $iCmtAuthorId ? strip_tags($this->msgErrRemoveAllowed()) : _t('_Access denied')));
+            return;
+        }
+
+        if($this->_oQuery->removeComment ($this->getId(), $aCmt['cmt_id'], $aCmt['cmt_parent_id'])) {
+            $this->_triggerComment();
+
+            bx_import('BxDolStorage');
+            $oStorage = BxDolStorage::getObjectInstance($this->getStorageObjectName());
+
+            $aImages = $this->_oQuery->getImages($this->_aSystem['system_id'], $aCmt['cmt_id']);
+            foreach($aImages as $aImage)
+                $oStorage->deleteFile($aImage['image_id']);
+
+            $this->_oQuery->deleteImages($this->_aSystem['system_id'], $aCmt['cmt_id']);
+
+            $this->isRemoveAllowed(true);
+
+            bx_import('BxDolAlerts');
+            $oZ = new BxDolAlerts($this->_sSystem, 'commentRemoved', $this->getId(), $iCmtAuthorId, array('comment_id' => $aCmt['cmt_id'], 'comment_author_id' => $aCmt['cmt_author_id']));
+            $oZ->alert();
+
+            $this->_echoResultJson(array('id' => $iCmtId));
+            return;
+        }
+
+        $this->_echoResultJson(array('msg' => _t('_cmt_err_cannot_perform_action')));
+    }
+
+    /**
+     * Internal functions
+     */
+    protected function _getAuthorId ()
+    {
+        return isMember() ? bx_get_logged_profile_id() : 0;
+    }
+
+    protected function _getAuthorPassword ()
+    {
+        return isMember() ? $_COOKIE['memberPassword'] : "";
+    }
+
+    protected function _getAuthorIp ()
+    {
+        return getVisitorIP();
+    }
+
+
+    protected function _getAuthorInfo($iAuthorId = 0)
+    {
+        $oProfile = $this->_getAuthorObject($iAuthorId);
+
+        return array(
+            $oProfile->getDisplayName(),
+            $oProfile->getUrl(),
+            $oProfile->getThumb(),
+            $oProfile->getUnit()
+        );
+    }
+
+    protected function _getAuthorObject($iAuthorId = 0)
+    {
+        bx_import('BxDolProfile');
+        $oProfile = BxDolProfile::getInstance($iAuthorId);
+        if (!$oProfile) {
+            bx_import('BxDolProfileUndefined');
+            $oProfile = BxDolProfileUndefined::getInstance();
+        }
+
+        return $oProfile;
+    }
+
+	protected function _getFormObject($sAction = BX_CMT_ACTION_POST)
+    {
+        $sDisplayName = '_sFormDisplay' . ucfirst($sAction);
+
+        bx_import('BxDolForm');
+        return BxDolForm::getObjectInstance($this->_sFormObject, $this->$sDisplayName);
+    }
+
+    protected function _prepareTextForEdit ($s)
+    {
+        if ($this->isNl2br())
+            return htmlspecialchars_decode(str_replace('<br />', "", $s));
+
+        return $s;
+    }
+
+    protected function _prepareTextForSave ($s)
+    {
+        $iDataAction = $this->isNl2br() ? BX_DATA_TEXT_MULTILINE : BX_DATA_HTML;
+        return bx_process_input($s, $iDataAction);
+    }
+
+    protected function _prepareTextForOutput ($s)
+    {
+        $sHttp = '';
+        $sPattern = "/((https?|ftp|news):\/\/)?([a-z]([a-z0-9\-]*\.)+(aero|arpa|biz|com|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|[a-z]{2})|(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(\/[a-z0-9_\-\.~]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&amp;]*)?)?(#[a-z][a-z0-9_]*)?/";
+        $aMatches = array();
+        if(preg_match($sPattern, $s, $aMatches) && empty($aMatches[1]))
+            $sHttp = 'http://';
+
+        $s = bx_process_output($s, BX_DATA_HTML);
+        $s = preg_replace($sPattern, '<a href="' . $sHttp . '$0" target="_blank">$0</a>', $s);
+
+        return $s;
+    }
+
+    protected function _prepareParams(&$aBp, &$aDp)
+    {
+        $aBp['type'] = isset($aBp['type']) && !empty($aBp['type']) ? $aBp['type'] : $this->_sBrowseType;
+        $aBp['filter'] = isset($aBp['filter']) && !empty($aBp['filter']) ? $aBp['filter'] : $this->_sBrowseFilter;
+        $aBp['parent_id'] = isset($aBp['parent_id']) ? $aBp['parent_id'] : 0;
+        $aBp['start'] = isset($aBp['start']) ? $aBp['start'] : -1;
+        $aBp['per_view'] = isset($aBp['per_view']) ? $aBp['per_view'] : -1;
+        $aBp['order']['by'] = isset($aBp['order_by']) ? $aBp['order_by'] : $this->_aOrder['by'];
+        $aBp['order']['way'] = isset($aBp['order_way']) ? $aBp['order_way'] : $this->_aOrder['way'];
+
+        $aDp['type'] = isset($aDp['type']) && !empty($aDp['type']) ? $aDp['type'] : $this->_sDisplayType;
+
+        switch($aDp['type']) {
+            case BX_CMT_DISPLAY_FLAT:
+                $aBp['vparent_id'] = -1;
+                $aBp['per_view'] = $aBp['per_view'] != -1 ? $aBp['per_view'] : $this->getPerView(0);
+                break;
+
+            case BX_CMT_DISPLAY_THREADED:
+                $aBp['per_view'] = $aBp['per_view'] != -1 ? $aBp['per_view'] : $this->getPerView($aBp['vparent_id']);
+                break;
+        }
+
+        switch ($aBp['type']) {
+            case BX_CMT_BROWSE_POPULAR:
+                $aBp['order'] = array(
+                    'by' => BX_CMT_ORDER_BY_POPULAR,
+                    'way' => BX_CMT_ORDER_WAY_DESC
+                );
+                break;
+        }
+
+        $aBp['count'] = $this->getCommentsCount($this->_iId, $aBp['vparent_id'], $aBp['filter']);
+        if($aBp['start'] != -1)
+            return;
+
+        $aBp['start'] = 0;
+        if($aBp['type'] == BX_CMT_BROWSE_TAIL) {
+            $aBp['start'] = $aBp['count'] - $aBp['per_view'];
+            if($aBp['start'] < 0) {
+                $aBp['per_view'] += $aBp['start'];
+                $aBp['start'] = 0;
+            }
+        }
+
+        $this->_setUserChoice($aDp['type'], $aBp['type'], $aBp['filter']);
+    }
+
+    protected function _triggerComment()
+    {
+        if(!$this->_aSystem['trigger_table'])
+            return false;
+
+        $iId = $this->getId();
+        if(!$iId)
+            return false;
+
+        $iCount = $this->getCommentsCount($iId);
+        return $this->_oQuery->updateTriggerTable($iId, $iCount);
+    }
+
+    /**
+     * Replace provided markers in a string
+     * @param $mixed string or array to replace markers in
+     * @return string where all occured markers are replaced
+     */
+    protected function _replaceMarkers ($mixed)
+    {
+        return bx_replace_markers($mixed, $this->_aMarkers);
+    }
+
+    protected function _getUserChoice()
+    {
+        $mixedDp = $mixedBpType = $mixedBpFilter = false;
+        if(!isLogged())
+            return array($mixedDp, $mixedBpType, $mixedBpFilter);
+
+        $iUserId = $this->_getAuthorId();
+
+        bx_import('BxDolSession');
+        $oSession = BxDolSession::getInstance();
+
+        $mixedDp = $oSession->getValue($this->_sDpSessionKey . $iUserId);
+        $mixedBpType = $oSession->getValue($this->_sBpSessionKeyType . $iUserId);
+        $mixedBpFilter = $oSession->getValue($this->_sBpSessionKeyFilter . $iUserId);
+
+        return array($mixedDp, $mixedBpType, $mixedBpFilter);
+    }
+
+    protected function _setUserChoice($sDp, $sBpType, $sBpFilter)
+    {
+        if(!isLogged())
+            return;
+
+        $iUserId = $this->_getAuthorId();
+
+        bx_import('BxDolSession');
+        $oSession = BxDolSession::getInstance();
+
+        if(!empty($sDp))
+            $oSession->setValue($this->_sDpSessionKey . $iUserId, $sDp);
+
+        if(!empty($sBpType))
+            $oSession->setValue($this->_sBpSessionKeyType . $iUserId, $sBpType);
+
+        if(!empty($sBpFilter))
+            $oSession->setValue($this->_sBpSessionKeyFilter . $iUserId, $sBpFilter);
+    }
+
+    protected function _sendNotificationEmail($iCmtId, $iCmtParentId)
+    {
+        $aCmt = $this->getCommentRow($iCmtId);
+        $aCmtParent = $this->getCommentRow($iCmtParentId);
+        if(empty($aCmt) || !is_array($aCmt) || empty($aCmtParent) || !is_array($aCmtParent) || (int)$aCmt['cmt_author_id'] == (int)$aCmtParent['cmt_author_id'])
+            return;
+
+        $oProfile = $this->_getAuthorObject($aCmtParent['cmt_author_id']);
+
+        bx_import('BxDolAccount');
+        $iAccount = $oProfile->getAccountId();
+        $aAccount = BxDolAccount::getInstance($iAccount)->getInfo();
+
+        $aPlus = array();
+        $aPlus['reply_text'] = bx_process_output($aCmt['cmt_text']);
+        $aPlus['comment_url'] = sprintf('%scmts.php?sys=%s&id=1&cmt_id=%d', BX_DOL_URL_ROOT, $this->_sSystem, $iCmtParentId);
+
+        bx_import('BxDolEmailTemplates');
+        $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate('t_CommentReplied', $aPlus);
+        return $aTemplate && sendMail($aAccount['email'], $aTemplate['Subject'], $aTemplate['Body']);
+    }
+}
+
+/** @} */

+ 177 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolInstallerUtils.php

@@ -0,0 +1,177 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+bx_import('BxDolIO');
+
+define('BX_FORCE_AUTOUPDATE_MAX_CHANGED_FILES_PERCENT', 0.05);
+define('BX_FORCE_USE_FTP_FILE_TRANSFER', false);
+
+class BxDolInstallerUtils extends BxDolIO
+{
+    protected $_aNonHashableFiles = array();
+
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    static public function isAllowUrlInclude()
+    {
+        if (version_compare(phpversion(), "5.2", ">") == 1) {
+            $sAllowUrlInclude = ini_get('allow_url_include');
+            return !($sAllowUrlInclude == 0);
+        };
+        return false;
+    }
+
+    static public function getModuleConfig($mixed)
+    {
+    	$sConfig = '';
+    	if(is_array($mixed) && !empty($mixed['path']))
+			$sConfig = BX_DIRECTORY_PATH_MODULES . $mixed['path'] . 'install/config.php';
+		else if(is_string($mixed))
+			$sConfig = $mixed;
+		else 
+			return array();
+
+    	if(!file_exists($sConfig))
+            return array();
+
+        include($sConfig);
+
+        return $aConfig;
+    }
+
+    static public function isModuleInstalled($sUri)
+    {
+        bx_import('BxDolModuleQuery');
+        return BxDolModuleQuery::getInstance()->isModule($sUri);
+    }
+
+    /**
+     * Set module for delayed uninstall
+     */
+    static public function setModulePendingUninstall($sUri, $bPendingUninstall = true)
+    {
+        bx_import('BxDolModuleQuery');
+        return BxDolModuleQuery::getInstance()->setModulePendingUninstall($sUri, $bPendingUninstall);
+    }
+
+    /**
+     * Check if module is pending for uninstall
+     */
+    static public function isModulePendingUninstall($sUri)
+    {
+        bx_import('BxDolModuleQuery');
+        $a = BxDolModuleQuery::getInstance()->getModuleByUri($sUri);
+        return $a['pending_uninstall'];
+    }
+
+    /**
+     * Check all pending for uninstallation modules and uninstall them if no pending for deletion files are found
+     */
+    static public function checkModulesPendingUninstall()
+    {
+        bx_import('BxDolModuleQuery');
+        $a = BxDolModuleQuery::getInstance()->getModules();
+        foreach ($a as $aModule) {
+
+            // after we make sure that all pending for deletion files are deleted
+            if (!$aModule['pending_uninstall'] || BxDolStorage::isQueuedFilesForDeletion($aModule['name']))
+                continue;
+
+            // remove pending uninstall flag
+            self::setModulePendingUninstall($aModule['uri'], false);
+
+            // perform uninstallation
+            bx_import('BxDolStudioInstallerUtils');
+            $aResult = BxDolStudioInstallerUtils::getInstance()->perform($aModule['path'], 'uninstall');
+
+            // send email nofitication
+            $aTemplateKeys = array(
+                'Module' => $aModule['title'],
+                'Result' => _t('_Success'),
+                'Message' => '',
+            );
+
+            if ($aResult['code'] > 0) {
+                $aTemplateKeys['Result'] = _t('_Failed');
+                $aTemplateKeys['Message'] = $aResult['message'];
+            }
+
+            bx_import('BxDolEmailTemplates');
+            $aMessage = BxDolEmailTemplates::getInstance()->parseTemplate('t_DelayedModuleUninstall', $aTemplateKeys);
+            sendMail (getParam('site_email'), $aMessage['Subject'], $aMessage['Body'], 0, array(), BX_EMAIL_SYSTEM);
+        }
+    }
+
+    /**
+     * Generate hash for module files.
+     * @param $sPath module's root folder
+     * @param $aFiles array to fill with files hashes
+     */
+    public function hashFiles($sPath, &$aFiles)
+    {
+        $aExcludes = array('.', '..', 'error_log', 'php.ini', '.DS_Store', 'Thumbs.db');
+        if (file_exists($sPath) && is_dir($sPath) && ($rSource = opendir($sPath))) {
+            while (($sFile = readdir($rSource)) !== false) {
+                if ('.' == $sFile[0] || in_array($sFile, $aExcludes))
+                    continue;
+                
+                if (in_array($this->filePathWithoutBase($sPath . $sFile), $this->_aNonHashableFiles))
+                    continue;                
+
+                if (is_dir($sPath . $sFile))
+                    $this->hashFiles($sPath . $sFile . '/', $aFiles);
+                else
+                    $aFiles[] = $this->hashInfo($sPath . $sFile);
+            }
+            closedir($rSource);
+        } elseif (file_exists($sPath) && is_file($sPath)) {
+            $aFiles[] = $this->hashInfo($sPath);
+        }
+    }
+
+    /** 
+     * Check module's files hashes. For system files use @see BxDolInstallerHasher class.
+     * @param $aFiles current files checksums 
+     * @param $iModuleId module id
+     * @return empty array on success, or array of files which checksum was changed 
+     */
+    public function hashCheck($aFiles, $iModuleId)
+    {
+        $oDb = bx_instance('BxDolStudioInstallerQuery');
+
+        $aFilesOrig = $oDb->getModuleTrackFiles($iModuleId);
+        $aFailesChanged = array();
+        foreach ($aFiles as $aFile)
+            if(!isset($aFilesOrig[$aFile['file']]) || $aFilesOrig[$aFile['file']]['hash'] != $aFile['hash'])
+                $aFailesChanged[] = $aFile['file'];
+
+		$fChangedPercent = 0;
+        if(count($aFilesOrig) != 0)
+            $fChangedPercent = count($aFailesChanged) / count($aFilesOrig);
+
+        return array($aFailesChanged, $fChangedPercent);
+    }
+    
+    protected function hashInfo($sPath)
+    {
+        return array(
+            'file' => $this->filePathWithoutBase($sPath),
+            'hash' => md5_file($sPath)
+        );
+    }
+
+    protected function filePathWithoutBase($sPath)
+    {
+        return bx_ltrim_str($sPath, BX_DIRECTORY_PATH_ROOT);
+    }
+}
+
+/** @} */

+ 363 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolMetatags.php

@@ -0,0 +1,363 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolMetatagsQuery');
+
+define('BX_METATAGS_KEYWORDS_MAX', 9);
+
+/**
+ * @page objects
+ * @section metatags Meta-tags
+ * @ref BxDolMetatags
+ */
+
+// TODO: client side controls: new form input for getting user's location, some js to help to enter @mentions and #keywords(optional)
+// TODO: implementation with some modules
+// TODO: integrate to comments, try to integrate to one metatags object - maybe specify metatags object in comments object and make appropriate changes in comments, comments content should be treated as main conetent and main content should be shown in search results)
+// TODO: pages for listing content by @keyword, @mention, country location, city location, or BETTER try integrate it to the search engine, to not make separate pages in each module
+// TODO: integration with notifications, when smbd is @mentioned
+
+/**
+ * Meta-tags for different content.
+ */
+class BxDolMetatags extends BxDol implements iBxDolFactoryObject
+{
+    protected $_sObject;
+    protected $_aObject;
+    protected $_oQuery;
+    protected $_aMetas = array ();
+
+    /**
+     * Constructor
+     * @param $aObject array of metags object options
+     */
+    public function __construct($aObject)
+    {
+        parent::__construct();
+
+        $this->_sObject = $aObject['object'];
+        $this->_aObject = $aObject;
+        
+        $a = array ('keywords', 'locations', 'mentions', 'pictures');
+        foreach ($a as $sMeta) {
+            if (empty($this->_aObject['table_' . $sMeta]))
+                continue;
+            $this->_aMetas[] = $sMeta;
+        }
+
+        $this->_oQuery = new BxDolMetatagsQuery($this->_aObject);
+    }
+
+    /**
+     * Get metatags object instance by object name
+     * @param $sObject object name
+     * @return object instance or false on error
+     */
+    static public function getObjectInstance($sObject)
+    {
+        if (isset($GLOBALS['bxDolClasses']['BxDolMetatags!'.$sObject])) 
+            return $GLOBALS['bxDolClasses']['BxDolMetatags!'.$sObject];
+
+        $aObject = BxDolMetatagsQuery::getMetatagsObject($sObject);
+        if (!$aObject || !is_array($aObject))
+            return false;
+
+        $sClass = $aObject['override_class_name'] ? $aObject['override_class_name'] : 'BxDolMetatags';
+        if (!empty($aObject['override_class_file']))
+            require_once(BX_DIRECTORY_PATH_ROOT . $aObject['override_class_file']);
+        else
+            bx_import($sClass);
+
+        $o = new $sClass($aObject);
+
+        return ($GLOBALS['bxDolClasses']['BxDolMetatags!'.$sObject] = $o);
+    }
+
+    /**
+     * Add all available meta tags to the head section 
+     * @return number of successfully added metas
+     */
+    public function metaAdd($iId, $mixedImage = false)
+    {
+        $i = 0;
+        foreach ($this->_aMetas as $sMeta) {
+            $sFunc = $sMeta . 'AddMeta';
+            $i += $this->$sFunc($iId);
+        }
+
+        if ($mixedImage && is_array($mixedImage)) {
+            bx_import('BxDolStorage');
+            $oStorage = BxDolStorage::getObjectInstance($mixedImage['object']);
+            $mixedImage = $oStorage ? $oStorage->getFileUrlById($mixedImage['id']) : false;
+        }
+        if ($mixedImage) 
+            BxDolTemplate::getInstance()->addPageMetaImage($mixedImage);
+
+        return $i;
+    }
+
+
+
+    /**
+     * Add #keywords from the string
+     * @param $iId content id
+     * @param $s string with #keywords
+     * @return number of found keywords
+     */
+    public function keywordsAdd($iId, $s) 
+    {
+        $a = array();
+        if (!preg_match_all('/(\#[\pL\pN]+)/u', strip_tags($s), $a)) {
+            $this->_oQuery->keywordsDelete($iId);
+            return 0;
+        }
+
+        $a[0] = array_unique($a[0]);
+
+        return $this->_oQuery->keywordsAdd($iId, array_slice($a[0], 0, BX_METATAGS_KEYWORDS_MAX));
+    }
+
+    /**
+     * Add links to the #keywords in the string
+     * @param $iId content id
+     * @param $s string with #keywords
+     * @return modified string where all #keywords are transformed to links with rel="tag" attribute
+     */
+    public function keywordsParse($iId, $s) 
+    {   
+        $a = $this->keywordsGet($iId);
+        if (empty($a))
+            return $s;
+    
+        foreach ($a as $sKeyword)
+            $s = str_ireplace('#' . $sKeyword, '<a rel="tag" href="' . BX_DOL_URL_ROOT . 'searchKeyword.php?type=keyword&keyword=' . rawurlencode($sKeyword) . '"><s>#</s><b>' . $sKeyword . '</b></a>', $s);
+
+        return $s;
+    }
+
+    /**
+     * Add keywords meta info to the head section
+     * @param $iId content id
+     */
+    protected function keywordsAddMeta($iId)
+    {
+        BxDolTemplate::getInstance()->addPageKeywords($this->keywordsGet($iId));
+    }
+
+    /**
+     * Get list of keywords associated with the content
+     * @return array of keywords
+     */
+    public function keywordsGet($iId)
+    {
+        return $this->_oQuery->keywordsGet($iId);
+    }
+
+    /**
+     * Set condition for search results object for meta keyword
+     * @param $oSearchResult search results object
+     * @param $sKeyword keyword
+     */
+    public function keywordsSetSearchCondition($oSearchResult, $sKeyword)
+    {
+        $oSearchResult->aCurrent['restriction']['meta_keyword'] = array(
+            'value' => $sKeyword,
+            'field' => 'keyword',
+            'operator' => '=',
+            'table' => $this->_aObject['table_keywords'],
+        );
+
+        $oSearchResult->aCurrent['join']['meta_keyword'] = array(
+            'type' => 'INNER',
+            'table' => $this->_aObject['table_keywords'],
+            'mainField' => $oSearchResult->aCurrent['ident'],
+            'onField' => 'object_id',
+            'joinFields' => array(),
+        );
+    }
+
+
+
+    /**
+     * Add location for the content
+     * @param $iId content id
+     * @param $sLatitude latitude
+     * @param $sLongitude longitude
+     * @param $sCountryCode optional 2 letters country code (ISO 3166-1)
+     * @param $sState optional state/province/territory name
+     * @param $sCity optional city name
+     * @param $sZip optional ZIP/postcode
+     * @return true if location was added, or false otherwise
+     */
+    public function locationsAdd($iId, $sLatitude, $sLongitude, $sCountryCode, $sState, $sCity, $sZip = '') 
+    {
+        return $this->_oQuery->locationsAdd($iId, $sLatitude, $sLongitude, $sCountryCode, $sState, $sCity, $sZip);
+    }
+    public function locationsAddFromForm($iId, $sName, $oForm = null) 
+    {
+        if (!$oForm)
+            $oForm = new BxDolForm(array(), false);
+        $this->locationsAdd($iId, $oForm->getCleanValue($sName.'_lat'), $oForm->getCleanValue($sName.'_lng'), $oForm->getCleanValue($sName.'_country'), $oForm->getCleanValue($sName.'_state'), $oForm->getCleanValue($sName.'_city'), $oForm->getCleanValue($sName.'_zip'));
+    }
+
+    /**
+     * Get locations string with links
+     * @param $iId content id
+     * @return string with links to country and city
+     */
+    public function locationsString($iId)
+    {
+        bx_import('BxDolFormQuery');
+        $aCountries = BxDolFormQuery::getDataItems('Country');
+        $aLocation = $this->locationGet($iId);
+        if (!$aLocation || !$aLocation['country'] || !isset($aCountries[$aLocation['country']]))
+            return '';
+
+        $sCountryUrl = '<a href="' . BX_DOL_URL_ROOT . 'searchKeyword.php?type=location_country&keyword=' . $aLocation['country'] . '">' . $aCountries[$aLocation['country']] . '</a>';
+        if (!$aLocation['city'])
+            return _t('_sys_location_country', $sCountryUrl);
+
+        $sCityUrl = '<a href="' . BX_DOL_URL_ROOT . 'searchKeyword.php?type=location_country_city&keyword=' . $aLocation['country'] . '&state=' . rawurlencode($aLocation['state']) . '&city=' . rawurlencode($aLocation['city']) . '">' . $aLocation['city'] . '</a>';
+            
+        return _t('_sys_location_country_city', $sCountryUrl, $sCityUrl);
+    }
+
+    /**
+     * Add keywords meta info to the head section
+     * @param $iId content id
+     */
+    protected function locationsAddMeta($iId) 
+    {
+        $aLocation = $this->locationGet($iId);
+        if (!empty($aLocation['lat']) && !empty($aLocation['lng']) && !empty($aLocation['country']))
+            BxDolTemplate::getInstance()->addPageMetaLocation($aLocation['lat'], $aLocation['lng'], $aLocation['country']);
+    }
+
+    /**
+     * Set condition for search results object for meta locations
+     * @param $oSearchResult search results object
+     * @param $sCountry country and other location info
+     */
+    public function locationsSetSearchCondition($oSearchResult, $sCountry, $sState = false, $sCity = false, $sZip = false)
+    {
+        $a = array('country' => 'sCountry', 'state' => 'sState', 'city' => 'sCity', 'zip' => 'sZip');
+        foreach ($a as $sIndex => $sVar) {
+            if (!$$sVar)
+                continue;
+
+            $oSearchResult->aCurrent['restriction']['meta_location_' . $sIndex] = array(
+                'value' => $$sVar,
+                'field' => $sIndex,
+                'operator' => '=',
+                'table' => $this->_aObject['table_locations'],
+            );
+        }
+
+        $oSearchResult->aCurrent['join']['meta_keyword'] = array(
+            'type' => 'INNER',
+            'table' => $this->_aObject['table_locations'],
+            'mainField' => $oSearchResult->aCurrent['ident'],
+            'onField' => 'object_id',
+            'joinFields' => array(),
+        );
+    }
+
+    /**
+     * Get location
+     * @return location array
+     */
+    public function locationGet($iId, $sPrefix = '')
+    {
+        $a = $this->_oQuery->locationGet($iId);
+        if (!$sPrefix)
+            return $a;
+
+        $aRet = array();
+        foreach ($a as $sKey => $sVal)
+            $aRet[$sPrefix . '_' . $sKey] = $sVal;
+        return $aRet;
+    }
+
+    /**
+     * Add @mentions from the string (most probably @mentions will be some sort of links already, so parsing may have to look for smth like <a data-mention="bx_persons|123">mention name</a> instead of @mention, since there is no usernames for profiles modules and name could contain spaces and othr characters)
+     * @param $iId content id
+     * @param $s string with @mentions
+     * @return number of found mentions 
+     */
+    public function mentionsAdd($iId, $s) 
+    {
+        // TODO:        
+    }
+
+    /**
+     * Add links to the @mentions in the string (actual tranformation may have to be performed with ready links like <a data-mention="bx_persons|123">mention name</a>)
+     * @param $iId content id
+     * @param $s string with @mentions
+     * @return modified string where all @mentions are transformed to links
+     */
+    public function mentionsParse($iId, $s) 
+    {
+        // TODO:
+    }
+
+    /**
+     * No mentions meta info in the head section
+     */
+    protected function mentionsAddMeta($iId) 
+    {
+        return 0;
+    }
+
+    /**
+     * Set condition for search results object for mentions
+     * @param $oSearchResult search results object
+     * @param $sMention smbd
+     */
+    public function mentionsSetSearchCondition($oSearchResult, $sMention)
+    {
+        // TODO:
+    }
+
+
+
+    /**
+     * Add thumb for the content
+     * @param $iId content id
+     * @param $sStorageObject storage object name where file is stored
+     * @param $mixedFileId file id in the storage
+     * @return true if file was added, or false otherwise
+     */
+    public function picturesAdd($iId, $sStorageObject, $mixedFileId) 
+    {
+        // TODO:        
+    }
+
+    /**
+     * Add thumb meta info to the head section
+     * @param $iId content id
+     */
+    protected function picturesAddMeta($iId) 
+    {
+        // TODO:
+    }
+
+
+
+    /**
+     * Delete all data associated with the content
+     * @param $iId content id
+     */
+    public function onDeleteContent($iId) 
+    {
+        // TODO: 
+    }
+
+}
+
+/** @} */

+ 2212 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolTemplate.php

@@ -0,0 +1,2212 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+ini_set('pcre.backtrack_limit', 1000000);
+
+define('BX_DOL_TEMPLATE_DEFAULT_CODE', 'lagoon');
+define('BX_DOL_TEMPLATE_FOLDER_ROOT', 'template');
+
+define('BX_DOL_TEMPLATE_INJECTIONS_CACHE', 'sys_injections.inc');
+
+define('BX_DOL_TEMPLATE_CHECK_IN_BOTH', 'both');
+define('BX_DOL_TEMPLATE_CHECK_IN_BASE', 'base');
+define('BX_DOL_TEMPLATE_CHECK_IN_TMPL', 'tmpl');
+
+define('BX_PAGE_DEFAULT', 0); ///< default, regular page
+define('BX_PAGE_CLEAR', 2); ///< clear page, without any headers and footers
+define('BX_PAGE_POPUP', 44); ///< popup page, without any headers and footers
+define('BX_PAGE_TRANSITION', 150); ///< transition page with redirect to display some msg, like 'please wait', without headers footers
+
+/**
+ * Template engine.
+ *
+ * An object of the class allows to:
+ *  1. Manage HTML templates.
+ *  2. Get URL/path for any template image/icon.
+ *  3. Attach CSS/JavaScript files to the output.
+ *  4. Add some content to any template key using Injection engine.
+ *
+ *
+ * Avalable constructions.
+ *  1. <bx_include_auto:template_name.html /> - the content of the file would be inserted. File would be taken from current template if it existes there, and from base directory otherwise.
+ *  2. <bx_include_base:template_name.html /> - the content of the file would be inserted. File would be taken from base directory.
+ *  3. <bx_include_tmpl:template_name.html /> - the content of the file would be inserted. File would be taken from tmpl_xxx directory.
+ *  4. <bx_url_root /> - the value of BX_DOL_URL_ROOT variable will be inserted.
+ *  5. <bx_url_admin /> - the value of BX_DOL_URL_ADMIN variable will be inserted.
+ *  6. <bx_text:_language_key /> - _language_key will be translated using language file(function _t()) and inserted.
+       <bx_text_js:_language_key /> - _language_key will be translated using language file(function _t()) and inserted, use it to insert text into js string.
+       <bx_text_attribute:_language_key /> - _language_key will be translated using language file(function _t()) and inserted, use it to insert text into html attribute.
+ *  7. <bx_image_url:image_file_name /> - image with 'image_file_name' file name will be searched in the images folder of current template.
+ *     If it's not found, then it will be searched in the images folder of base template. On success full URL will be inserted, otherwise an empty string.
+ *  8. <bx_icon_url:icon_file_name /> - the same with <bx_image_url:image_file_name />, but icons will be searched in the images/icons/ folders.
+ *  9. <bx_injection:injection_name /> - will be replaced with injections registered with the page and injection_name in the `sys_injections`/`sys_injections_admin`/ tables.
+ *  10. <bx_if:tag_name>some_HTML</bx_if:tag_name> - will be replaced with provided content if the condition is true, and with empty string otherwise.
+ *  11. <bx_repeat:cycle_name>some_HTML</bx_repeat:cycle_name> - an inner HTML content will be repeated in accordance with received data.
+ *
+ *
+ * Related classes:
+ *  BxDolTemplateAdmin - for processing admin templates.
+ *  Template classes in modules - for processing modiles' templates.
+ *
+ *
+ * Global variables:
+ *  oSysTemplate - is used for template processing in user part.
+ *  oAdmTemplate - is used for template processing in admin part.
+ *
+ *
+ * Add injection:
+ *  1. Register it in the `sys_injections` table or `sys_injections_admin` table for admin panel.
+ *  2. Clear injections cache(sys_injections.inc and sys_injections_admin.inc in cache folder).
+ *
+ *
+ * Predefined template keys to add injections:
+ *  1. injection_head - add injections in the <head> tag.
+ *  2. injection_body - add ingection(attribute) in the <body> tag.
+ *  3. injection_header - add injection inside the <body> tag at the very beginning.
+ *  4. injection_logo_before - add injection at the left of the main logo(inside logo's DIV).
+ *  5. injection_logo_after - add injection at the right of the main logo(inside logo's DIV).
+ *  6. injection_between_logo_top_menu - add injection between logo and top menu.
+ *  7. injection_top_menu_before - add injection at the left of the top menu(inside top menu's DIV).
+ *  8. injection_top_menu_after - add injection at the right of the top menu(inside top menu's DIV).
+ *  13. injection_content_before - add injection just before main content(inside content's DIV).
+ *  14. injection_content_after - add injection just after main content(inside content's DIV).
+ *  15. injection_between_content_footer - add injection between content and footer.
+ *  16. injection_footer_before - add injection at the left of the footer(inside footer's DIV).
+ *  17. injection_footer_after - add injection at the right of the footer(inside footer's DIV).
+ *  18. injection_footer - add injection inside the <body> tag at the very end.
+ *
+ *
+ * Example of usage:
+ * @code
+ *  $oSysTemplate = BxDolTemplate::getInstance();
+ *
+ *  $oSysTemplate->addCss(array('test1.css', 'test2.css'));
+ *  $oSysTemplate->addJs(array('test1.js', 'test2.js'));
+ *  $oSysTemplate->parseHtmlByName('messageBox.html', array(
+ *    'id' => $iId,
+ *     'msgText' => $sText,
+ *     'bx_if:timer' => array(
+ *        'condition' => $iTimer > 0,
+ *        'content' => array(
+ *           'id' => $iId,
+ *           'time' => 1000 * $iTimer,
+ *           'on_close' => $sOnClose,
+ *        )
+ *     ),
+ *     'bx_if:timer' => array(
+ *        array(
+ *           'name' => $sName,
+ *           'title' => $sTitle
+ *        ),
+ *        array(
+ *           'name' => $sName,
+ *           'title' => $sTitle
+ *        )
+ *     )
+ *  ));
+ * @endcode
+ *
+ *
+ * Memberships/ACL:
+ * Doesn't depend on user's membership.
+ *
+ *
+ * Alerts:
+ * no alerts available
+ *
+ */
+class BxDolTemplate extends BxDol implements iBxDolSingleton
+{
+    /**
+     * Main fields
+     */
+    protected $_sPrefix;
+    protected $_sRootPath;
+    protected $_sRootUrl;
+    protected $_sSubPath;
+    protected $_sInjectionsTable;
+    protected $_sInjectionsCache;
+    protected $_sCode;
+    protected $_sCodeKey;
+    protected $_sKeyWrapperHtml;
+    protected $_sFolderHtml;
+    protected $_sFolderCss;
+    protected $_sFolderImages;
+    protected $_sFolderIcons;
+    protected $_aTemplates;
+
+    protected $_aLocations;
+    protected $_aLocationsJs;
+
+    /**
+     * Cache related fields
+     */
+    protected $_bCacheEnable;
+    protected $_sCacheFolderUrl;
+    protected $_sCachePublicFolderUrl;
+    protected $_sCachePublicFolderPath;
+    protected $_sCacheFilePrefix;
+
+    protected $_bImagesInline;
+    protected $_iImagesMaxSize;
+
+    protected $_bCssCache;
+    protected $_bCssArchive;
+    protected $_sCssCachePrefix;
+
+    protected $_bJsCache;
+    protected $_bJsArchive;
+    protected $_sJsCachePrefix;
+
+    /**
+     * Less related fields
+     */
+    protected $_bLessEnable;
+    protected $_sLessCachePrefix;
+
+    /**
+     * Minify related fields
+     */
+    protected $_bMinifyEnable;
+
+    protected $aPage;
+    protected $aPageContent;
+
+    protected $_oConfigTemplate;
+
+    /**
+     * Constructor
+     */
+    protected function __construct($sRootPath = BX_DIRECTORY_PATH_ROOT, $sRootUrl = BX_DOL_URL_ROOT)
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
+
+        parent::__construct();
+
+        $this->_sPrefix = 'BxDolTemplate';
+
+        $this->_sRootPath = $sRootPath;
+        $this->_sRootUrl = $sRootUrl;
+        $this->_sInjectionsTable = 'sys_injections';
+        $this->_sInjectionsCache = BX_DOL_TEMPLATE_INJECTIONS_CACHE;
+
+        $this->_sCodeKey = 'skin';
+
+        $sCode = getParam('template');
+        if(empty($sCode))
+            $sCode = BX_DOL_TEMPLATE_DEFAULT_CODE;
+        $this->_checkCode($sCode, false);
+
+        //--- Check selected template in COOKIE(the lowest priority) ---//
+        $sCode = !empty($_COOKIE[$this->_sCodeKey]) ? $_COOKIE[$this->_sCodeKey] : '';
+        $this->_checkCode($sCode, false);
+
+        //--- Check selected template in GET(the highest priority) ---//
+        $sCode = !empty($_GET[$this->_sCodeKey]) ? $_GET[$this->_sCodeKey] : '';
+        $this->_checkCode($sCode, true);
+
+        if (!$this->_sSubPath)
+            $this->_sSubPath = 'boonex/' . BX_DOL_TEMPLATE_DEFAULT_CODE . '/';
+
+        if (!file_exists(BX_DIRECTORY_PATH_MODULES . $this->_sSubPath)) // just for 8.0.0-A6 upgrade
+            $this->_sSubPath = 'boonex/uni/';
+
+        if(isset($_GET[$this->_sCodeKey])) {
+            bx_import('BxDolPermalinks');
+            if(BxDolPermalinks::getInstance()->redirectIfNecessary(array($this->_sCodeKey)))
+                exit;
+        }
+
+        $this->_sKeyWrapperHtml = '__';
+        $this->_sFolderHtml = '';
+        $this->_sFolderCss = 'css/';
+        $this->_sFolderImages = 'images/';
+        $this->_sFolderIcons = 'images/icons/';
+        $this->_aTemplates = array();
+
+        $this->addLocation('system', $this->_sRootPath, $this->_sRootUrl);
+
+        $this->addLocationJs('system_inc_js', BX_DIRECTORY_PATH_INC . 'js/' , BX_DOL_URL_ROOT . 'inc/js/');
+        $this->addLocationJs('system_inc_js_classes', BX_DIRECTORY_PATH_INC . 'js/classes/' , BX_DOL_URL_ROOT . 'inc/js/classes/');
+        $this->addLocationJs('system_plugins_public', BX_DIRECTORY_PATH_PLUGINS_PUBLIC, BX_DOL_URL_PLUGINS_PUBLIC);
+
+        $this->_bCacheEnable = !defined('BX_DOL_CRON_EXECUTE') && getParam('sys_template_cache_enable') == 'on';
+        $this->_sCacheFolderUrl = '';
+        $this->_sCachePublicFolderUrl = BX_DOL_URL_CACHE_PUBLIC;
+        $this->_sCachePublicFolderPath = BX_DIRECTORY_PATH_CACHE_PUBLIC;
+        $this->_sCacheFilePrefix = "bx_templ_";
+
+        $this->_bImagesInline = getParam('sys_template_cache_image_enable') == 'on';
+        $this->_iImagesMaxSize = (int)getParam('sys_template_cache_image_max_size') * 1024;
+
+        $bArchive = getParam('sys_template_cache_compress_enable') == 'on';
+        $this->_bCssCache = !defined('BX_DOL_CRON_EXECUTE') && getParam('sys_template_cache_css_enable') == 'on';
+        $this->_bCssArchive = $this->_bCssCache && $bArchive;
+        $this->_sCssCachePrefix = $this->_sCacheFilePrefix . 'css_';
+
+        $this->_bJsCache = !defined('BX_DOL_CRON_EXECUTE') && getParam('sys_template_cache_js_enable') == 'on';
+        $this->_bJsArchive = $this->_bJsCache && $bArchive;
+        $this->_sJsCachePrefix = $this->_sCacheFilePrefix . 'js_';
+
+        $this->_bLessEnable = true;
+        $this->_sLessCachePrefix = $this->_sCacheFilePrefix . 'less_';
+
+        $this->_bMinifyEnable = true;
+
+        $this->aPage = array();
+        $this->aPageContent = array();
+    }
+
+    /**
+     * Prevent cloning the instance
+     */
+    public function __clone()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
+    }
+
+    /**
+     * Get singleton instance of the class
+     */
+    public static function getInstance()
+    {
+        if(!isset($GLOBALS['bxDolClasses'][__CLASS__])) {
+            $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolTemplate();
+            $GLOBALS['bxDolClasses'][__CLASS__]->init();
+        }
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    /**
+     * Load templates.
+     */
+    function loadTemplates()
+    {
+        $aResult = array();
+        foreach($this->_aTemplates as $sName)
+            $aResult[$sName] = $this->getHtml($sName . '.html');
+        $this->_aTemplates = $aResult;
+    }
+    /**
+     * Initialize template engine.
+     * Note. The method is executed with the system, you shouldn't execute it in your subclasses.
+     */
+    function init()
+    {
+        //--- Load page elements related static variables ---//
+        $this->aPage = array(
+            'name_index' => BX_PAGE_DEFAULT,
+            'header' => '',
+            'header_text' => '',
+            'keywords' => array(),
+            'location' => array(),
+            'description'  => '',
+            'robots' => '',
+            'css_name' => array(),
+            'css_compiled' => array(),
+            'css_system' => array(),
+            'js_name' => array(),
+            'js_compiled' => array(),
+            'js_system' => array(),
+            'js_options' => array(),
+            'js_translations' => array(),
+            'js_images' => array(),
+            'injections' => array()
+        );
+
+        //--- Load default CSS ---//
+        $this->addCssSystem(array(
+            'common.css',
+            'default.less',
+            'general.css',
+            'icons.css',
+            'colors.css',
+            'forms.css',
+            'media-desktop.css',
+            'media-tablet.css',
+            'media-phone.css',
+            'media-print.css',
+            BX_DOL_URL_PLUGINS_PUBLIC . 'marka/marka.min.css',
+        ));
+
+        //--- Load default JS ---//
+        $this->addJsSystem(array(
+            'jquery/jquery.min.js',
+            'jquery/jquery-migrate.min.js',
+            'jquery-ui/jquery.ui.position.min.js',
+            'spin.min.js',
+            'jquery.easing.js',
+            'jquery.cookie.min.js',
+            'moment-with-langs.js',
+            'functions.js',
+            'jquery.webForms.js',
+            'jquery.dolPopup.js',
+            'elementQuery.min.js',
+            'marka/marka.min.js',
+        ));
+
+        //--- Load default JS ---//
+        bx_import('BxDolLanguages');
+        $this->addJsTranslation(array(
+            '_are you sure?',
+            '_error occured',
+            '_sys_loading',
+        ));
+
+        //--- Load injection's cache ---//
+        if (getParam('sys_db_cache_enable')) {
+            $oDb = BxDolDb::getInstance();
+            $oCache = $oDb->getDbCacheObject();
+            $sCacheKey = $oDb->genDbCacheKey($this->_sInjectionsCache);
+
+            $aInjections = $oCache->getData($sCacheKey);
+            if ($aInjections === null) {
+                $aInjections = $this->getInjectionsData();
+                $oCache->setData ($sCacheKey, $aInjections);
+            }
+        } else {
+            $aInjections = $this->getInjectionsData();
+        }
+
+        $this->aPage['injections'] = $aInjections;
+
+        bx_import('BxTemplConfig');
+        $this->_oConfigTemplate = BxTemplConfig::getInstance();
+    }
+
+    protected function getInjectionsData ()
+    {
+        $oDb = BxDolDb::getInstance();
+
+        $aInjections = $oDb->getAll("SELECT `page_index`, `name`, `key`, `type`, `data`, `replace` FROM `" . $this->_sInjectionsTable . "` WHERE `active`='1'");
+        if (!$aInjections)
+            return array();
+
+        foreach ($aInjections as $aInjection)
+            $aInjections['page_' . $aInjection['page_index']][$aInjection['key']][] = $aInjection;
+
+        return $aInjections;
+    }
+
+    /**
+     * Set page name index
+     * @param int $i name index
+     */
+    function setPageNameIndex($i)
+    {
+        $this->aPage['name_index'] = $i;
+    }
+
+    /**
+     * Get page name index
+     * @return int $i name index
+     */
+    function getPageNameIndex()
+    {
+        return isset($this->aPage['name_index']) ? (int)$this->aPage['name_index'] : 0;
+    }
+
+    /**
+     * Set page header
+     * @param string $s page header
+     */
+    function setPageHeader($s)
+    {
+        $this->aPage['header'] = $s;
+    }
+
+    /**
+     * Get page header
+     * @return string $s page header
+     */
+    function getPageHeader()
+    {
+        return $this->aPage['header'];
+    }
+
+    /**
+     * Set page params. Available page params are: name_index, header
+     * @param array $a page params
+     */
+    function setPageParams($a)
+    {
+        if (!empty($this->aPage))
+            $this->aPage = array_merge($this->aPage, $a);
+        else
+            $this->aPage = $a;
+    }
+
+    /**
+     * Get page params.
+     * @return array $a page params
+     */
+    function getPageParams()
+    {
+        return $this->aPage;
+    }
+
+    /**
+     * Set page description.
+     *
+     * @param string $sDescription necessary page description.
+     */
+    function setPageDescription($sDescription)
+    {
+        $this->aPage['description'] = $sDescription;
+    }
+
+    /**
+     * Set page meta robots.
+     *
+     * @param string $s page meta robots.
+     */
+    function setPageMetaRobots($s)
+    {
+        $this->aPage['robots'] = $s;
+    }
+
+    /**
+     * Set page content for some variable.
+     * @param string $sVar     name of content variable
+     * @param string $sContent content for $sVar variable
+     * @param int    $iIndex   optional page index, default is index which was set before with @see setPageNameIndex function, or 0
+     */
+    function setPageContent($sVar, $sContent, $iIndex = false)
+    {
+        $i = false !== $iIndex ? $iIndex : $this->getPageNameIndex();
+        $this->aPageContent[$i][$sVar] = $sContent;
+    }
+
+    /**
+     * Get page content for some variable.
+     * @param  string $sVar   name of content variable
+     * @param  int    $iIndex optional page index, default is index which was set before with @see setPageNameIndex function, or 0
+     * @return string page content for some variable or for the whole page.
+     */
+    function getPageContent($sVar = false, $iIndex = false)
+    {
+        $i = false !== $iIndex ? $iIndex : $this->getPageNameIndex();
+        return false !== $sVar ? $this->aPageContent[$i][$sVar] : $this->aPageContent[$i];
+    }
+
+    /**
+     * Get currently active template code.
+     *
+     * @return string template's code.
+     */
+    function getCode()
+    {
+        return $this->_sCode;
+    }
+
+    /**
+     * Check whether code is associated with active template.
+     *
+     * @param string  $sCode      template's unique URI.
+     * @param boolean $bSetCookie save code in COOKIE or not.
+     */
+    protected function _checkCode($sCode, $bSetCookie)
+    {
+        if(empty($sCode) || !preg_match('/^[A-Za-z0-9_-]+$/', $sCode))
+            return;
+
+        bx_import('BxDolModuleQuery');
+        $aModule = BxDolModuleQuery::getInstance()->getModuleByUri($sCode);
+        if(empty($aModule) || !is_array($aModule) || (int)$aModule['enabled'] != 1 || !file_exists($this->_sRootPath . 'modules/' . $aModule['path'] . 'data/template/'))
+            return;
+
+        bx_import('BxDolModuleConfig');
+        $oConfig = new BxDolModuleConfig($aModule);
+
+        $this->_sCode = $oConfig->getUri();
+        $this->_sSubPath = $oConfig->getDirectory();
+
+        if(!$bSetCookie || bx_get('preview'))
+            return;
+
+        $aUrl = parse_url(BX_DOL_URL_ROOT);
+        $sPath = isset($aUrl['path']) && !empty($aUrl['path']) ? $aUrl['path'] : '/';
+
+        setcookie( $this->_sCodeKey, $this->_sCode, time() + 60*60*24*365, $sPath);
+    }
+
+    /**
+     * Get currently active template path.
+     *
+     * @return string template's path.
+     */
+    function getPath()
+    {
+        return $this->_sSubPath;
+    }
+
+    /**
+     * Set page title.
+     * @deprecated use setPageHeader
+     *
+     * @param string $sTitle necessary page title.
+     */
+    function setPageTitle($sTitle)
+    {
+        $this->setPageHeader($sTitle);
+    }
+
+    /**
+     * Set page's main box title.
+     * @deprecated use setPageParams
+     *
+     * @param string $sTitle necessary page's main box title.
+     */
+    function setPageMainBoxTitle($sTitle)
+    {
+        $this->setPageParams(array('header_text' => $sTitle));
+    }
+
+    /**
+     * Add location in array of locations.
+     * Note. Location is the path/url to folder where 'templates' folder is stored.
+     *
+     * @param string $sKey          - location's    unique key.
+     * @param string $sLocationPath - location's path. For modules: '[path_to_dolphin]/modules/[vendor_name]/[module_name]/'
+     * @param string $sLocationUrl  - location's url. For modules: '[url_to_dolphin]/modules/[vendor_name]/[module_name]/'
+     */
+    function addLocation($sKey, $sLocationPath, $sLocationUrl)
+    {
+        $this->_aLocations[$sKey] = array(
+            'path' => $sLocationPath,
+            'url' => $sLocationUrl,
+        );
+
+        return $sKey;
+    }
+    /**
+     * Add dynamic location.
+     *
+     * @param  string   $sLocationPath - location's path. For modules: '[path_to_dolphin]/modules/[vendor_name]/[module_name]/'
+     * @param  string   $sLocationUrl  - location's url. For modules: '[url_to_dolphin]/modules/[vendor_name]/[module_name]/'
+     * @return location key. Is needed to remove the location.
+     */
+    function addDynamicLocation($sLocationPath, $sLocationUrl)
+    {
+        $sLocationKey = time();
+        $this->addLocation($sLocationKey, $sLocationPath, $sLocationUrl);
+
+        return $sLocationKey;
+    }
+    /**
+     * Remove location from array of locations.
+     * Note. Location is the path/url to folder where templates are stored.
+     *
+     * @param string $sKey - location's    unique key.
+     */
+    function removeLocation($sKey)
+    {
+        if(isset($this->_aLocations[$sKey]))
+           unset($this->_aLocations[$sKey]);
+    }
+    /**
+     * Add JS location in array of JS locations.
+     * Note. Location is the path/url to folder where JS files are stored.
+     *
+     * @param string $sKey          - location's    unique key.
+     * @param string $sLocationPath - location's path. For modules: '[path_to_dolphin]/modules/[vendor_name]/[module_name]/js/'
+     * @param string $sLocationUrl  - location's url. For modules: '[url_to_dolphin]/modules/[vendor_name]/[module_name]/js/'
+     */
+    function addLocationJs($sKey, $sLocationPath, $sLocationUrl)
+    {
+        $this->_aLocationsJs[$sKey] = array(
+            'path' => $sLocationPath,
+            'url' => $sLocationUrl
+        );
+    }
+    /**
+     * Add dynamic JS location.
+     *
+     * @param  string   $sLocationPath - location's path. For modules: '[path_to_dolphin]/modules/[vendor_name]/[module_name]/'
+     * @param  string   $sLocationUrl  - location's url. For modules: '[url_to_dolphin]/modules/[vendor_name]/[module_name]/'
+     * @return location key. Is needed to remove the location.
+     */
+    function addDynamicLocationJs($sLocationPath, $sLocationUrl)
+    {
+        $sLocationKey = time();
+        $this->addLocationJs($sLocationKey, $sLocationPath, $sLocationUrl);
+
+        return $sLocationKey;
+    }
+    /**
+     * Remove JS location from array of locations.
+     * Note. Location is the path/url to folder where templates are stored.
+     *
+     * @param string $sKey - JS location's    unique key.
+     */
+    function removeLocationJs($sKey)
+    {
+        if(isset($this->_aLocationsJs[$sKey]))
+           unset($this->_aLocationsJs[$sKey]);
+    }
+    /**
+     * Add Option in JS output.
+     *
+     * @param mixed $mixedName option's name or an array of options' names.
+     */
+    function addJsOption($mixedName)
+    {
+        if(is_string($mixedName))
+            $mixedName = array($mixedName);
+
+        foreach($mixedName as $sName)
+            $this->aPage['js_options'][$sName] = getParam($sName);
+    }
+    /**
+     * Add language translation for key in JS output.
+     *
+     * @param mixed $mixedKey language key or an array of keys.
+     */
+    function addJsTranslation($mixedKey)
+    {
+        if(is_string($mixedKey))
+            $mixedKey = array($mixedKey);
+
+        foreach($mixedKey as $sKey)
+            $this->aPage['js_translations'][$sKey] = _t($sKey, '{0}', '{1}');
+    }
+    /**
+     * Add image in JS output.
+     *
+     * @param array $aImages an array of image descriptors.
+     *                       The descriptor is a key/value pear in the array of descriptors.
+     */
+    function addJsImage($aImages)
+    {
+        if(!is_array($aImages))
+            return;
+
+        foreach($aImages as $sKey => $sFile) {
+            $sUrl = $this->getImageUrl($sFile);
+            if(empty($sUrl))
+                continue;
+
+            $this->aPage['js_images'][$sKey] = $sUrl;
+        }
+    }
+    /**
+     * Add icon in JS output.
+     *
+     * @param array $aIcons an array of icons descriptors.
+     *                      The descriptor is a key/value pear in the array of descriptors.
+     */
+    function addJsIcon($aIcons)
+    {
+        if(!is_array($aIcons))
+            return;
+
+        foreach($aIcons as $sKey => $sFile) {
+            $sUrl = $this->getIconUrl($sFile);
+            if(empty($sUrl))
+                continue;
+
+            $this->aPage['js_images'][$sKey] = $sUrl;
+        }
+    }
+    /**
+     * Set page keywords.
+     *
+     * @param mixed  $mixedKeywords necessary page keywords(string - single keyword, array - an array of keywords).
+     * @param string $sDevider      - string devider.
+     */
+    function addPageKeywords($mixedKeywords, $sDevider = ',')
+    {
+        if(is_string($mixedKeywords))
+            $mixedKeywords = strpos($mixedKeywords, $sDevider) !== false ? explode($sDevider, $mixedKeywords) : array($mixedKeywords);
+
+        foreach($mixedKeywords as $iKey => $sValue)
+            $mixedKeywords[$iKey] = trim($sValue);
+
+        $this->aPage['keywords'] = isset($this->aPage['keywords']) && is_array($this->aPage['keywords']) ? array_merge($this->aPage['keywords'], $mixedKeywords) : $mixedKeywords;
+    }
+    /**
+     * Set page locatoin coordinates.
+     *
+     * @param $fLat latitude
+     * @param $fLng longitude
+     */
+    function addPageMetaLocation($fLat, $fLng, $sCountryCode)
+    {
+        $this->aPage['location'] = array('lat' => $fLat, 'lng' => $fLng, 'country' => $sCountryCode);
+    }
+    /**
+     * Set page meta image.
+     *
+     * @param $sImageUrl meta image url
+     */
+    function addPageMetaImage($sImageUrl)
+    {
+        $this->aPage['image'] = $sImageUrl;
+    }
+    /**
+     * Returns page meta info, like meta keyword, meta description, location, etc
+     */
+    function getMetaInfo()
+    {
+        $sRet = '';
+
+        if (!empty($this->aPage['keywords']) && is_array($this->aPage['keywords']))
+            $sRet .= '<meta name="keywords" content="' . bx_html_attribute(implode(',', $this->aPage['keywords'])) . '" />';
+
+        if (!empty($this->aPage['description']) && is_string($this->aPage['description']))
+            $sRet .= '<meta name="description" content="' . bx_html_attribute($this->aPage['description']) . '" />';
+
+        if (!empty($this->aPage['location']) && isset($this->aPage['location']['lat']) && isset($this->aPage['location']['lng']) && isset($this->aPage['location']['country']))
+            $sRet .= '
+                <meta name="ICBM" content="' . $this->aPage['location']['lat'] . ';' . $this->aPage['location']['lng'] . '" />
+                <meta name="geo.position" content="' . $this->aPage['location']['lat'] . ';' . $this->aPage['location']['lng'] . '" />
+                <meta name="geo.region" content="' . bx_html_attribute($this->aPage['location']['country']) . '" />';
+
+        if (!empty($this->aPage['image'])) {
+            $sRet .= '<meta property="og:image" content="' . $this->aPage['image'] . '" />';
+        } else {
+            bx_import('BxTemplFunctions');
+            $sRet .= BxTemplFunctions::getInstance()->getMetaIcons();
+        }
+
+        return $sRet;
+    }
+    /**
+     * Get template, which was loaded earlier.
+     * @see method this->loadTemplates and field this->_aTemplates
+     *
+     * @param  string $sName - template name.
+     * @return string template's content.
+     */
+    function getTemplate($sName)
+    {
+        return $this->_aTemplates[$sName];
+    }
+    /**
+     * Get full URL for the icon.
+     *
+     * @param  string $sName    icon's file name.
+     * @param  string $sCheckIn where the content would be searched(base, template, both)
+     * @return string full URL.
+     */
+    function getIconUrl($sName, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
+    {
+        $sContent = "";
+        if(($sContent = $this->_getInlineData('icon', $sName, $sCheckIn)) !== false)
+            return $sContent;
+
+        return $this->_getAbsoluteLocation('url', $this->_sFolderIcons, $sName, $sCheckIn);
+    }
+    /**
+     * Get absolute Path for the icon.
+     *
+     * @param  string $sName    - icon's file name.
+     * @param  string $sCheckIn where the content would be searched(base, template, both)
+     * @return string absolute path.
+     */
+    function getIconPath($sName, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
+    {
+        return $this->_getAbsoluteLocation('path', $this->_sFolderIcons, $sName, $sCheckIn);
+    }
+    /**
+     * Get full URL for the image.
+     *
+     * @param  string $sName    - images's file name.
+     * @param  string $sCheckIn where the content would be searched(base, template, both)
+     * @return string full URL.
+     */
+    function getImageUrl($sName, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
+    {
+        $sContent = "";
+        if(($sContent = $this->_getInlineData('image', $sName, $sCheckIn)) !== false)
+            return $sContent;
+
+        return $this->_getAbsoluteLocation('url', $this->_sFolderImages, $sName, $sCheckIn);
+    }
+    /**
+     * Get absolute Path for the image.
+     *
+     * @param  string $sName    - image's file name.
+     * @param  string $sCheckIn where the content would be searched(base, template, both)
+     * @return string absolute path.
+     */
+    function getImagePath($sName, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
+    {
+        return $this->_getAbsoluteLocation('path', $this->_sFolderImages, $sName, $sCheckIn);
+    }
+    /**
+     * Get full URL of CSS file.
+     *
+     * @param  string $sName    - CSS file name.
+     * @param  string $sCheckIn where the content would be searched(base, template, both)
+     * @return string full URL.
+     */
+    function getCssUrl($sName, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
+    {
+        return $this->_getAbsoluteLocation('url', $this->_sFolderCss, $sName, $sCheckIn);
+    }
+    /**
+     * Get full Path of CSS file.
+     *
+     * @param  string $sName    - CSS file name.
+     * @param  string $sCheckIn where the content would be searched(base, template, both)
+     * @return string full URL.
+     */
+    function getCssPath($sName, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
+    {
+        return $this->_getAbsoluteLocation('path', $this->_sFolderCss, $sName, $sCheckIn);
+    }
+
+    /**
+     * Get menu.
+     * @param $s menu object name
+     * @return html or empty string
+     */
+    function getMenu ($s)
+    {
+        bx_import('BxDolMenu');
+        $oMenu = BxDolMenu::getObjectInstance($s);
+        return $oMenu ? $oMenu->getCode () : '';
+    }
+
+    /**
+     * Get content of HTML file.
+     *
+     * @param  string $sName    - HTML file name.
+     * @param  string $sCheckIn where the content would be searched(base, template, both)
+     * @return string full content of the file and false on failure.
+     */
+    function getHtml($sName, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
+    {
+        $sAbsolutePath = $this->_getAbsoluteLocation('path', $this->_sFolderHtml, $sName, $sCheckIn);
+        return !empty($sAbsolutePath) ? trim(file_get_contents($sAbsolutePath)) : false;
+    }
+
+    /**
+     * Parse HTML template. Search for the template with accordance to it's file name.
+     *
+     * @see allows to use cache.
+     *
+     * @param  string $sName               - HTML file name.
+     * @param  array  $aVariables          - key/value pairs. key should be the same as template's key, but without prefix and postfix.
+     * @param  mixed  $mixedKeyWrapperHtml - key wrapper(string value if left and right parts are the same, array(left, right) otherwise).
+     * @param  string $sCheckIn            where the content would be searched(base, template, both)
+     * @return string the result of operation.
+     */
+    function parseHtmlByName($sName, $aVariables, $mixedKeyWrapperHtml = null, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
+    {
+        if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->beginTemplate($sName, $sRand = time().rand());
+
+        if (($sContent = $this->getCached($sName, $aVariables, $mixedKeyWrapperHtml, $sCheckIn)) !== false) {
+            if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->endTemplate($sName, $sRand, $sRet, true);
+            return $sContent;
+        }
+
+        $sRet = '';
+        if (($sContent = $this->getHtml($sName, $sCheckIn)) !== false)
+            $sRet = $this->_parseContent($sContent, $aVariables, $mixedKeyWrapperHtml);
+
+        if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->endTemplate($sName, $sRand, $sRet, false);
+
+        return $sRet;
+    }
+    /**
+     * Parse HTML template.
+     *
+     * @see Doesn't allow to use cache.
+     *
+     * @param  string $sContent            - HTML file content.
+     * @param  array  $aVariables          - key/value pairs. key should be the same as template's key, but without prefix and postfix.
+     * @param  mixed  $mixedKeyWrapperHtml - key wrapper(string value if left and right parts are the same, array(left, right) otherwise).
+     * @return string the result of operation.
+     */
+    function parseHtmlByContent($sContent, $aVariables, $mixedKeyWrapperHtml = null)
+    {
+        if(empty($sContent))
+            return "";
+
+        return $this->_parseContent($sContent, $aVariables, $mixedKeyWrapperHtml);
+    }
+    /**
+     * Parse earlier loaded HTML template.
+     *
+     * @see Doesn't allow to use cache.
+     *
+     * @param  string $sName      - template name.
+     * @param  array  $aVariables - key/value pairs. Key should be the same as template's key, excluding prefix and postfix.
+     * @return string the result of operation.
+     * @see $this->_aTemplates
+     */
+    function parseHtmlByTemplateName($sName, $aVariables, $mixedKeyWrapperHtml = null)
+    {
+        if(!isset($this->_aTemplates[$sName]) || empty($this->_aTemplates[$sName]))
+            return "";
+
+        return $this->_parseContent($this->_aTemplates[$sName], $aVariables, $mixedKeyWrapperHtml);
+    }
+    /**
+     * Parse page HTML template. Search for the page's template with accordance to it's file name.
+     *
+     * @see allows to use cache.
+     *
+     * @param  string $sName      - HTML file name.
+     * @param  array  $aVariables - key/value pairs. key should be the same as template's key, but without prefix and postfix.
+     * @return string the result of operation.
+     */
+    function parsePageByName($sName, $aVariables)
+    {
+        if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->beginPage($sName);
+
+        $sContent = $this->parseHtmlByName($sName, $aVariables, $this->_sKeyWrapperHtml, BX_DOL_TEMPLATE_CHECK_IN_BOTH);
+        if(empty($sContent))
+            $sContent = $this->parseHtmlByName('default.html', $aVariables, $this->_sKeyWrapperHtml, BX_DOL_TEMPLATE_CHECK_IN_BOTH);
+
+        //--- Add CSS and JS at the very last ---//
+        if(strpos($sContent , '<bx_include_css />') !== false) {
+            if (!empty($this->aPage['css_name']))
+                $this->addCss($this->aPage['css_name']);
+            $sContent = str_replace('<bx_include_css />', $this->includeFiles('css', true) . $this->includeFiles('css'), $sContent);
+        }
+
+        if(strpos($sContent , '<bx_include_js />') !== false) {
+            if (!empty($this->aPage['js_name']))
+                $this->addJs($this->aPage['js_name']);
+            $sContent = str_replace('<bx_include_js />', $this->includeFiles('js', true) . $this->includeFiles('js'), $sContent);
+        }
+
+        if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->endPage($sContent);
+
+        return $sContent;
+    }
+    /**
+     * Parse system keys.
+     *
+     * @param  string $sKey key
+     * @return string value associated with the key.
+     */
+    function parseSystemKey($sKey, $mixedKeyWrapperHtml = null, $bProcessInjection = true)
+    {
+        $aKeyWrappers = $this->_getKeyWrappers($mixedKeyWrapperHtml);
+
+        $sRet = '';
+        switch( $sKey ) {
+            case 'page_charset':
+                $sRet = 'UTF-8';
+                break;
+            case 'page_robots':
+                if(!empty($this->aPage['robots']) && is_string($this->aPage['robots']))
+                    $sRet = '<meta name="robots" content="' . bx_html_attribute($this->aPage['robots']) . '" />';
+                break;
+            case 'meta_info':
+                $sRet = $this->getMetaInfo();
+                break;
+            case 'page_header':
+                if(isset($this->aPage['header']))
+                    $sRet = bx_process_output(strip_tags($this->aPage['header']));
+                break;
+            case 'page_header_text':
+                if(isset($this->aPage['header_text']))
+                    $sRet = bx_process_output($this->aPage['header_text']);
+                break;
+            case 'popup_loading':
+                $sContent = $this->parsePageByName('popup_loading.html', array());
+
+                bx_import('BxTemplFunctions');
+                $sRet = BxTemplFunctions::getInstance()->transBox('bx-popup-loading', $sContent, true);
+                break;
+            case 'lang':
+                $sRet = bx_lang_name();
+                break;
+            case 'main_logo':
+                bx_import('BxTemplFunctions');
+                $sRet = BxTemplFunctions::getInstance()->getMainLogo();
+                break;
+            case 'informer':
+                bx_import('BxDolInformer');
+                $oInformer = BxDolInformer::getInstance($this);
+                $sRet = $oInformer ? $oInformer->display() : '';
+                break;
+            case 'dol_images':
+                $sRet = $this->_processJsImages();
+                break;
+            case 'dol_lang':
+                $sRet = $this->_processJsTranslations();
+                break;
+            case 'dol_options':
+                $sRet = $this->_processJsOptions();
+                break;
+            case 'bottom_text':
+                $sRet = _t( '_bottom_text', date('Y') );
+                break;
+            case 'copyright':
+                $sRet = _t( '_copyright',   date('Y') ) . getVersionComment();
+                break;
+            case 'extra_js':
+                $sRet = empty($this->aPage['extra_js']) ? '' : $this->aPage['extra_js'];
+                break;
+            case 'is_profile_page':
+                $sRet = (defined('BX_PROFILE_PAGE')) ? 'true' : 'false';
+                break;
+
+            default:
+                bx_import('BxTemplFunctions');
+                $sRet = ($sTemplAdd = BxTemplFunctions::getInstance()->TemplPageAddComponent($sKey)) !== false ? $sTemplAdd : $aKeyWrappers['left'] . $sKey . $aKeyWrappers['right'];
+        }
+
+        if($bProcessInjection)
+            $sRet = $this->processInjection($this->getPageNameIndex(), $sKey, $sRet);
+
+        return $sRet;
+    }
+    function getCacheFilePrefix($sType)
+    {
+    	$sResult = '';
+    	switch($sType) {
+    		case 'template':
+				$sResult = $this->_sCacheFilePrefix;
+				break;
+
+    		case 'css':
+    			$sResult = $this->_sCssCachePrefix;
+    			break;
+
+    		case 'js':
+    			$sResult = $this->_sJsCachePrefix;
+    			break;
+    	}
+
+    	return $sResult;
+    }
+    /**
+     * Get cache object for templates
+     * @return cache class instance
+     */
+    function getTemplatesCacheObject ()
+    {
+        $sCacheEngine = getParam('sys_template_cache_engine');
+        $oCacheEngine = bx_instance('BxDolCache' . $sCacheEngine);
+        if(!$oCacheEngine->isAvailable())
+            $oCacheEngine = bx_instance('BxDolCacheFileHtml');
+        return $oCacheEngine;
+    }
+    /**
+     * Get template from cache if it's enabled.
+     *
+     * @param  string  $sName               template name
+     * @param  string  $aVariables          key/value pairs. key should be the same as template's key, but without prefix and postfix.
+     * @param  mixed   $mixedKeyWrapperHtml - key wrapper(string value if left and right parts are the same, array(0 => left, 1 => right) otherwise).
+     * @param  string  $sCheckIn            where the content would be searched(base, template, both)
+     * @param  boolean $bEvaluate           need to evaluate the template or not.
+     * @return string  result of operation or false on failure.
+     */
+    function getCached($sName, &$aVariables, $mixedKeyWrapperHtml = null, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH, $bEvaluate = true)
+    {
+        // initialization
+
+        if(!$this->_bCacheEnable)
+            return false;
+
+        $sAbsolutePath = $this->_getAbsoluteLocation('path', $this->_sFolderHtml, $sName, $sCheckIn);
+        if(empty($sAbsolutePath))
+            return false;
+
+        $oCacheEngine = $this->getTemplatesCacheObject ();
+        $isFileBasedEngine = $bEvaluate && method_exists($oCacheEngine, 'getDataFilePath');
+
+        // try to get cached content
+
+        $sCacheVariableName = "a";
+        $sCacheKey = $this->_getCacheFileName('html', $sAbsolutePath) . '.php';
+        if ($isFileBasedEngine)
+            $sCacheContent = $oCacheEngine->getDataFilePath($sCacheKey);
+        else
+            $sCacheContent = $oCacheEngine->getData($sCacheKey);
+
+
+        // recreate cache if it is empty
+
+        if ($sCacheContent === null && ($sContent = file_get_contents($sAbsolutePath)) !== false && ($sContent = $this->_compileContent($sContent, "\$" . $sCacheVariableName, 1, $aVariables, $mixedKeyWrapperHtml)) !== false) {
+            if (false === $oCacheEngine->setData($sCacheKey, trim($sContent)))
+                return false;
+
+            if ($isFileBasedEngine)
+                $sCacheContent = $oCacheEngine->getDataFilePath($sCacheKey);
+            else
+                $sCacheContent = $sContent;
+        }
+
+        if ($sCacheContent === null)
+            return false;
+
+        // return simple cache content
+
+        if(!$bEvaluate)
+            return $sCacheContent;
+
+        // return evaluated cache content
+
+        ob_start();
+
+        $$sCacheVariableName = &$aVariables;
+
+        if ($isFileBasedEngine)
+            include($sCacheContent);
+        else
+            eval('?'.'>' . $sCacheContent);
+
+        $sContent = ob_get_clean();
+
+        return $sContent;
+    }
+
+    /**
+     * Add JS file(s) to global output.
+     *
+     * @param  mixed          $mixedFiles string value represents a single JS file name. An array - array of JS file names.
+     * @param  boolean        $bDynamic   in the dynamic mode JS file(s) are not included to global output, but are returned from the function directly.
+     * @return boolean/string result of operation.
+     */
+    function addJs($mixedFiles, $bDynamic = false)
+    {
+        return $this->_processFiles('js', 'add', $mixedFiles, $bDynamic);
+    }
+
+    /**
+     * Add System JS file(s) to global output.
+     * System JS files are the files which are attached to all pages. They will be cached separately from the others.
+     *
+     * @param  mixed          $mixedFiles string value represents a single JS file name. An array - array of JS file names.
+     * @param  boolean        $bDynamic   in the dynamic mode JS file(s) are not included to global output, but are returned from the function directly.
+     * @return boolean/string result of operation.
+     */
+    function addJsSystem($mixedFiles)
+    {
+        return $this->_processFiles('js', 'add', $mixedFiles, false, true);
+    }
+
+    /**
+     * Delete JS file(s) from global output.
+     *
+     * @param  mixed   $mixedFiles string value represents a single JS file name. An array - array of JS file names.
+     * @return boolean result of operation.
+     */
+    function deleteJs($mixedFiles)
+    {
+        return $this->_processFiles('js', 'delete', $mixedFiles);
+    }
+
+    /**
+     * Delete System JS file(s) from global output.
+     *
+     * @param  mixed   $mixedFiles string value represents a single JS file name. An array - array of JS file names.
+     * @return boolean result of operation.
+     */
+    function deleteJsSystem($mixedFiles)
+    {
+        return $this->_processFiles('js', 'delete', $mixedFiles, false, true);
+    }
+
+    /**
+     * Compile JS files in one file.
+     *
+     * @param  string $sAbsolutePath CSS file absolute path(full URL for external CSS/JS files).
+     * @param  array  $aIncluded     an array of already included JS files.
+     * @return string result of operation.
+     */
+    function _compileJs($sAbsolutePath, &$aIncluded)
+    {
+        if(isset($aIncluded[$sAbsolutePath]))
+           return '';
+
+        $bExternal = strpos($sAbsolutePath, "http://") !== false || strpos($sAbsolutePath, "https://") !== false;
+        if($bExternal) {
+            $sPath = $sAbsolutePath;
+            $sName = '';
+
+            $sContent = bx_file_get_contents($sAbsolutePath);
+        } else {
+            $aFileInfo = pathinfo($sAbsolutePath);
+            $sPath = $aFileInfo['dirname'] . DIRECTORY_SEPARATOR;
+            $sName = $aFileInfo['basename'];
+
+            $sContent = file_get_contents($sPath . $sName);
+        }
+
+        if(empty($sContent))
+            return '';
+
+        $sUrl = bx_ltrim_str($sPath, realpath(BX_DIRECTORY_PATH_ROOT), BX_DOL_URL_ROOT);
+        $sUrl = str_replace(DIRECTORY_SEPARATOR, '/', $sPath);
+
+        $sContent = "\r\n/*--- BEGIN: " . $sUrl . $sName . "---*/\r\n" . $sContent . ";\r\n/*--- END: " . $sUrl . $sName . "---*/\r\n";
+        $sContent = str_replace(array("\n\r", "\r\n", "\r"), "\n", $sContent);
+
+        $aIncluded[$sAbsolutePath] = 1;
+
+        return preg_replace(
+            array(
+                "'<bx_url_root />'",
+                "'\r\n'"
+            ),
+            array(
+                BX_DOL_URL_ROOT,
+                "\n"
+            ),
+            $sContent
+        );
+    }
+    /**
+     * Wrap an URL to JS file into JS tag.
+     *
+     * @param  string $sFile - URL to JS file.
+     * @return string the result of operation.
+     */
+    function _wrapInTagJs($sFile)
+    {
+        return "<script language=\"javascript\" type=\"text/javascript\" src=\"" . $sFile . "\"></script>";
+    }
+    /**
+     * Wrap JS code into JS tag.
+     *
+     * @param  string $sCode - JS code.
+     * @return string the result of operation.
+     */
+    function _wrapInTagJsCode($sCode)
+    {
+        return "<script language=\"javascript\" type=\"text/javascript\">\n<!--\n" . $sCode . "\n-->\n</script>";
+    }
+
+    /**
+     * Add CSS file(s) to global output.
+     *
+     * @param  mixed          $mixedFiles string value represents a single CSS file name. An array - array of CSS file names.
+     * @param  boolean        $bDynamic   in the dynamic mode CSS file(s) are not included to global output, but are returned from the function directly.
+     * @return boolean/string result of operation
+     */
+    function addCss($mixedFiles, $bDynamic = false)
+    {
+        return $this->_processFiles('css', 'add', $mixedFiles, $bDynamic);
+    }
+
+    /**
+     * Add System CSS file(s) to global output.
+     * System CSS files are the files which are attached to all pages. They will be cached separately from the others.
+     *
+     * @param  mixed          $mixedFiles string value represents a single CSS file name. An array - array of CSS file names.
+     * @return boolean/string result of operation
+     */
+    function addCssSystem($mixedFiles)
+    {
+        return $this->_processFiles('css', 'add', $mixedFiles, false, true);
+    }
+
+    /**
+     * Delete CSS file(s) from global output.
+     *
+     * @param  mixed   $mixedFiles string value represents a single CSS file name. An array - array of CSS file names.
+     * @return boolean result of operation.
+     */
+    function deleteCss($mixedFiles)
+    {
+        return $this->_processFiles('css', 'delete', $mixedFiles);
+    }
+    /**
+     * Delete System CSS file(s) from global output.
+     *
+     * @param  mixed   $mixedFiles string value represents a single CSS file name. An array - array of CSS file names.
+     * @return boolean result of operation.
+     */
+    function deleteCssSystem($mixedFiles)
+    {
+        return $this->_processFiles('css', 'delete', $mixedFiles, false, true);
+    }
+    /**
+     * Compile CSS files' structure(@see @import css_file_path) in one file.
+     *
+     * @param  string $sAbsolutePath CSS file absolute path(full URL for external CSS/JS files).
+     * @param  array  $aIncluded     an array of already included CSS files.
+     * @return string result of operation.
+     */
+    function _compileCss($sAbsolutePath, &$aIncluded)
+    {
+        if(isset($aIncluded[$sAbsolutePath]))
+           return '';
+
+        $bExternal = strpos($sAbsolutePath, "http://") !== false || strpos($sAbsolutePath, "https://") !== false;
+        if($bExternal) {
+            $sPath = $sAbsolutePath;
+            $sName = '';
+
+            $sContent = bx_file_get_contents($sAbsolutePath);
+        } else {
+            $aFileInfo = pathinfo($sAbsolutePath);
+            $sPath = $aFileInfo['dirname'] . DIRECTORY_SEPARATOR;
+            $sName = $aFileInfo['basename'];
+
+            $sContent = file_get_contents($sPath . $sName);
+        }
+
+        if(empty($sContent))
+            return '';
+
+        $sUrl = bx_ltrim_str($sPath, realpath(BX_DIRECTORY_PATH_ROOT), BX_DOL_URL_ROOT);
+        $sUrl = str_replace(DIRECTORY_SEPARATOR, '/', $sPath);
+
+        $sContent = "\r\n/*--- BEGIN: " . $sUrl . $sName . "---*/\r\n" . $sContent . "\r\n/*--- END: " . $sUrl . $sName . "---*/\r\n";
+        $aIncluded[$sAbsolutePath] = 1;
+
+        $sContent = str_replace(array("\n\r", "\r\n", "\r"), "\n", $sContent);
+        if($bExternal) {
+            $sContent = preg_replace(
+                array(
+                    "'@import\s+url\s*\(\s*[\'|\"]*\s*([a-zA-Z0-9\.\/_-]+)\s*[\'|\"]*\s*\)\s*;'",
+                    "'url\s*\(\s*[\'|\"]*\s*([a-zA-Z0-9\.\/\?\#_=-]+)\s*[\'|\"]*\s*\)'"
+                ),
+                array(
+                    "",
+                    "'url('" . $sPath . "'\\1)'"
+                ),
+                $sContent
+            );
+        }
+        else {
+        	try {
+        		$oTemplate = &$this;
+
+	            $sContent = preg_replace_callback(
+	                "'@import\s+url\s*\(\s*[\'|\"]*\s*([a-zA-Z0-9\.\/_-]+)\s*[\'|\"]*\s*\)\s*;'", function ($aMatches)  use($oTemplate, $sPath, $aIncluded) {
+	                	return $oTemplate->_compileCss(realpath($sPath . dirname($aMatches[1])) . DIRECTORY_SEPARATOR . basename($aMatches[1]), $aIncluded);
+	                }, $sContent);
+	
+	            $sContent = preg_replace_callback(
+	                "'url\s*\(\s*[\'|\"]*\s*([a-zA-Z0-9\.\/\?\#_=-]+)\s*[\'|\"]*\s*\)'", function ($aMatches)  use($oTemplate, $sPath) {
+						$sFile = basename($aMatches[1]);
+				        $sDirectory = dirname($aMatches[1]);
+				
+				        $sRootPath = realpath(BX_DIRECTORY_PATH_ROOT);
+				        $sAbsolutePath = realpath(addslashes($sPath) . $sDirectory) . DIRECTORY_SEPARATOR . $sFile;
+				
+				        $sRootPath = str_replace(DIRECTORY_SEPARATOR, '/', $sRootPath);
+				        $sAbsolutePath = str_replace(DIRECTORY_SEPARATOR, '/', $sAbsolutePath);
+				
+				        return 'url(' . bx_ltrim_str($sAbsolutePath, $sRootPath, BX_DOL_URL_ROOT) . ')';
+	                }, $sContent);
+			}
+	    	catch(Exception $oException) {
+	        	return '';
+	        }
+        }
+
+        return $sContent;
+    }
+
+    /**
+     * Less CSS
+     *
+     * @param  mixed $mixed CSS string to process with Less compiler or an array with CSS file's Path and URL.
+     * @return mixed string or an array with CSS file's Path and URL.
+     */
+    function _lessCss($mixed)
+    {
+        require_once(BX_DIRECTORY_PATH_PLUGINS . 'lessphp/lessc.inc.php');
+        $oLess = new lessc();
+        $oLess->setVariables($this->_oConfigTemplate->aLessConfig);
+
+        if(is_array($mixed) && isset($mixed['url']) && isset($mixed['path'])) {
+            $sPathFile = realpath($mixed['path']);
+            $aInfoFile = pathinfo($sPathFile);
+            if (!isset($aInfoFile['extension']) || $aInfoFile['extension'] != 'less')
+                return $mixed;
+
+            $sPathRoot = realpath(BX_DIRECTORY_PATH_ROOT);
+            $sFile = $this->_sLessCachePrefix . trim(str_replace(array('.' . $aInfoFile['extension'], DIRECTORY_SEPARATOR), array('', '_'), bx_ltrim_str($sPathFile, $sPathRoot)), '_') . '.css';
+
+            $oLess->checkedCompile($mixed['path'], $this->_sCachePublicFolderPath . $sFile);
+            return array('url' => $this->_sCachePublicFolderUrl . $sFile, 'path' => $this->_sCachePublicFolderPath . $sFile);
+        }
+
+        return $oLess->compile($mixed);
+    }
+
+    /**
+     * Minify CSS
+     *
+     * @param  string $s CSS string to minify
+     * @return string minified CSS string.
+     */
+    function _minifyCss($s)
+    {
+        require_once(BX_DIRECTORY_PATH_PLUGINS . 'minify/lib/Minify/CSS/Compressor.php');
+        return Minify_CSS_Compressor::process($s);
+    }
+
+    /**
+     * Wrap an URL to CSS file into CSS tag.
+     *
+     * @param  string $sFile - URL to CSS file.
+     * @return string the result of operation.
+     */
+    function _wrapInTagCss($sFile)
+    {
+        if (!$sFile)
+            return '';
+        return "<link href=\"" . $sFile . "\" rel=\"stylesheet\" type=\"text/css\" />";
+    }
+    /**
+     * Wrap CSS code into CSS tag.
+     *
+     * @param  string $sCode - CSS code.
+     * @return string the result of operation.
+     */
+    function _wrapInTagCssCode($sCode)
+    {
+        return "<link rel=\"stylesheet\" type=\"text/css\">" . $sCode . "</link>";
+    }
+    /**
+     * Include CSS/JS file(s) attached to the page in its head section.
+     * @see the method is system and would be called automatically.
+     *
+     * @param  string $sType the type of file('js' or 'css')
+     * @return string the result CSS code.
+     */
+    function includeFiles($sType, $bSystem = false)
+    {
+        $sUpcaseType = ucfirst($sType);
+
+        $sArrayKey = $sType . ($bSystem ? '_system' : '_compiled');
+        $aFiles = isset($this->aPage[$sArrayKey]) ? $this->aPage[$sArrayKey] : array();
+        if(empty($aFiles) || !is_array($aFiles))
+            return "";
+
+        if(!$this->{'_b' . $sUpcaseType . 'Cache'})
+            return $this->_includeFiles($sType, $aFiles);
+
+        //--- If cache already exists, return it ---//
+        $sMethodWrap = '_wrapInTag' . $sUpcaseType;
+        $sMethodCompile = '_compile' . $sUpcaseType;
+        $sMethodLess = '_less' . $sUpcaseType;
+        $sMethodMinify = '_minify' . $sUpcaseType;
+
+        ksort($aFiles);
+
+        $sName = "";
+        foreach($aFiles as $aFile)
+            $sName .= $aFile['url'];
+        $sName = $this->_getCacheFileName($sType, $sName);
+
+        $sCacheAbsoluteUrl = $this->_sCachePublicFolderUrl . $sName . '.' . $sType;
+        $sCacheAbsolutePath = $this->_sCachePublicFolderPath . $sName . '.' . $sType;
+        if(file_exists($sCacheAbsolutePath)) {
+            if($this->{'_b' . $sUpcaseType . 'Archive'})
+                $sCacheAbsoluteUrl = $this->_getLoaderUrl($sType, $sName);
+
+           return $this->$sMethodWrap($sCacheAbsoluteUrl);
+        }
+
+        //--- Collect all attached CSS/JS in one file ---//
+        $bLess = $this->_bLessEnable && method_exists($this, $sMethodLess);
+
+        $sResult = "";
+        $aIncluded = array();
+        foreach($aFiles as $aFile) {
+        	if($bLess)
+				$aFile = $this->$sMethodLess($aFile);
+
+            if(($sContent = $this->$sMethodCompile($aFile['path'], $aIncluded)) !== false)
+                $sResult .= $sContent;
+        }
+
+        if ($this->_bMinifyEnable && method_exists($this, $sMethodMinify))
+            $sResult = $this->$sMethodMinify($sResult);
+
+        $mixedWriteResult = false;
+        if(!empty($sResult) && ($rHandler = fopen($sCacheAbsolutePath, 'w')) !== false) {
+            $mixedWriteResult = fwrite($rHandler, $sResult);
+            fclose($rHandler);
+            @chmod ($sCacheAbsolutePath, 0666);
+        }
+
+        if($mixedWriteResult === false)
+            return $this->_includeFiles($sType, $aFiles);
+
+        if($this->{'_b' . $sUpcaseType . 'Archive'})
+            $sCacheAbsoluteUrl = $this->_getLoaderUrl($sType, $sName);
+
+        return $this->$sMethodWrap($sCacheAbsoluteUrl);
+    }
+    /**
+     * Include CSS/JS files without caching.
+     *
+     * @param  string $sType  the file type (css or js)
+     * @param  array  $aFiles CSS/JS files to be added to the page.
+     * @return string result of operation.
+     */
+    function _includeFiles($sType, &$aFiles)
+    {
+        $sUpcaseType = ucfirst($sType);
+
+        $sMethodWrap = '_wrapInTag' . $sUpcaseType;
+        $sMethodLess = '_less' . $sUpcaseType;
+
+        $sResult = "";
+        foreach($aFiles as $aFile) {
+            if($this->_bLessEnable && method_exists($this, $sMethodLess))
+                $aFile = $this->$sMethodLess($aFile);
+
+            $sResult .= $this->$sMethodWrap($aFile['url']);
+        }
+
+        return $sResult;
+    }
+    /**
+     * Insert/Delete CSS file from output stack.
+     *
+     * @param  string  $sType      the file type (css or js)
+     * @param  string  $sAction    add/delete
+     * @param  mixed   $mixedFiles string value represents a single CSS file name. An array - array of CSS file names.
+     * @return boolean result of operation.
+     */
+    function _processFiles($sType, $sAction, $mixedFiles, $bDynamic = false, $bSystem = false)
+    {
+        if(empty($mixedFiles))
+            return $bDynamic ? "" : false;
+
+        if(is_string($mixedFiles))
+            $mixedFiles = array($mixedFiles);
+
+        $sUpcaseType = ucfirst($sType);
+        $sMethodLocate = '_getAbsoluteLocation' . $sUpcaseType;
+        $sMethodWrap = '_wrapInTag' . $sUpcaseType;
+        $sResult = '';
+        foreach($mixedFiles as $sFile) {
+            //--- Process 3d Party CSS/JS file ---//
+            if(strpos($sFile, "http://") !== false || strpos($sFile, "https://") !== false) {
+                $sUrl = $sFile;
+                $sPath = $sFile;
+            }
+            //--- Process Custom CSS/JS file ---//
+            else if(strpos($sFile, "|") !== false) {
+                $sFile = implode('', explode("|", $sFile));
+
+                $sUrl = BX_DOL_URL_ROOT . $sFile;
+                $sPath = realpath(BX_DIRECTORY_PATH_ROOT . $sFile);
+            }
+            //--- Process Common CSS/JS file(check in default locations) ---//
+            else {
+                $sUrl = $this->$sMethodLocate('url', $sFile);
+                $sPath = $this->$sMethodLocate('path', $sFile);
+            }
+
+            if(empty($sPath) || empty($sUrl))
+                continue;
+
+            $sArrayKey = $sType . ($bSystem ? '_system' : '_compiled');
+            switch($sAction) {
+                case 'add':
+                    if($bDynamic)
+                        $sResult .= $this->$sMethodWrap($sUrl);
+                    else {
+                        $bFound = false;
+                        $aSearchIn = $bSystem ? $this->aPage[$sArrayKey] : array_merge($this->aPage[$sType . '_system'], $this->aPage[$sArrayKey]);
+                        foreach($aSearchIn as $iKey => $aValue)
+                            if($aValue['url'] == $sUrl && $aValue['path'] == $sPath) {
+                                $bFound = true;
+                                break;
+                            }
+
+                        if(!$bFound)
+                            $this->aPage[$sArrayKey][] = array('url' => $sUrl, 'path' => $sPath);
+                    }
+                    break;
+                case 'delete':
+                    if(!$bDynamic)
+                        foreach($this->aPage[$sArrayKey]  as $iKey => $aValue)
+                            if($aValue['url'] == $sUrl) {
+                                unset($this->aPage[$sArrayKey][$iKey]);
+                                break;
+                            }
+                    break;
+            }
+        }
+
+        return $bDynamic ? $sResult : true;
+    }
+
+    /**
+     * Parse content.
+     *
+     * @param  string $sContent            - HTML file's content.
+     * @param  array  $aVariables          - key/value pairs. key should be the same as template's key, but without prefix and postfix.
+     * @param  mixed  $mixedKeyWrapperHtml - key wrapper(string value if left and right parts are the same, array(0 => left, 1 => right) otherwise).
+     * @return string the result of operation.
+     */
+    function _parseContent($sContent, $aVariables, $mixedKeyWrapperHtml = null)
+    {
+        $aKeys = array_keys($aVariables);
+        $aValues = array_values($aVariables);
+
+        $aKeyWrappers = $this->_getKeyWrappers($mixedKeyWrapperHtml);
+
+        $iCountKeys = count($aKeys);
+        for ($i = 0; $i < $iCountKeys; $i++) {
+            if (strncmp($aKeys[$i], 'bx_repeat:', 10) === 0) {
+                $sKey = "'<" . $aKeys[$i] . ">(.*)<\/" . $aKeys[$i] . ">'s";
+
+                $aMatches = array();
+                preg_match($sKey, $sContent, $aMatches);
+
+                $sValue = '';
+                if(isset($aMatches[1]) && !empty($aMatches[1])) {
+                    if(is_array($aValues[$i]))
+                        foreach($aValues[$i] as $aValue)
+                            $sValue .= $this->parseHtmlByContent($aMatches[1], $aValue, $mixedKeyWrapperHtml);
+                    else if(is_string($aValues[$i]))
+                        $sValue = $aValues[$i];
+                }
+            } else if (strncmp($aKeys[$i], 'bx_if:', 6) === 0) {
+                $sKey = "'<" . $aKeys[$i] . ">(.*)<\/" . $aKeys[$i] . ">'s";
+
+                $aMatches = array();
+                preg_match($sKey, $sContent, $aMatches);
+
+                $sValue = '';
+                if(isset($aMatches[1]) && !empty($aMatches[1]))
+                    if(is_array($aValues[$i]) && isset($aValues[$i]['content']) && isset($aValues[$i]['condition']) && $aValues[$i]['condition'])
+                        $sValue .= $this->parseHtmlByContent($aMatches[1], $aValues[$i]['content'], $mixedKeyWrapperHtml);
+            } else {
+                $sKey = "'" . $aKeyWrappers['left'] . $aKeys[$i] . $aKeyWrappers['right'] . "'s";
+                $sValue = str_replace('$', '\\$', $aValues[$i]);
+            }
+
+            $aKeys[$i] = $sKey;
+            $aValues[$i] = $sValue;
+        }
+
+        try {
+        	$oTemplate = &$this;
+
+        	$aCallbackPatterns = array(
+        		"'<bx_include_auto:([^\s]+) \/>'s" => BX_DOL_TEMPLATE_CHECK_IN_BOTH,
+        		"'<bx_include_base:([^\s]+) \/>'s" => BX_DOL_TEMPLATE_CHECK_IN_BASE,
+        		"'<bx_include_tmpl:([^\s]+) \/>'s" => BX_DOL_TEMPLATE_CHECK_IN_TMPL
+        	);
+	        foreach($aCallbackPatterns as $sPattern => $sCheckIn)
+		        $sContent = preg_replace_callback($sPattern, function($aMatches) use($oTemplate, $aVariables, $mixedKeyWrapperHtml, $sCheckIn) {
+		        	return $oTemplate->parseHtmlByName($aMatches[1], $aVariables, $mixedKeyWrapperHtml, $sCheckIn);
+		        }, $sContent);
+
+			$sContent = $this->_parseContentKeys($sContent, array(
+				"'<bx_injection:([^\s]+) />'s" => 'get_injection',
+            	"'<bx_menu:([^\s]+) \/>'s" => 'get_menu',
+			));
+        }
+		catch(Exception $oException) {
+        	return '';
+        }
+
+        $aKeys = array_merge($aKeys, array(
+            "'<bx_url_root />'",
+            "'<bx_url_studio />'",
+        ));
+        $aValues = array_merge($aValues, array(
+            BX_DOL_URL_ROOT,
+            BX_DOL_URL_STUDIO,
+        ));
+
+        //--- Parse Predefined Keys ---//
+        $sContent = preg_replace($aKeys, $aValues, $sContent);
+        //--- Parse System Keys ---//
+        $sContent = preg_replace( "'" . $aKeyWrappers['left'] . "([a-zA-Z0-9_-]+)" . $aKeyWrappers['right'] . "'e", "\$this->parseSystemKey('\\1', \$mixedKeyWrapperHtml)", $sContent);
+
+        return $sContent;
+    }
+
+    /**
+     * Compile content
+     *
+     * @param  string  $sContent            template.
+     * @param  string  $aVarName            variable name to be saved in the output file.
+     * @param  integer $iVarDepth           depth is used to process nesting, for example, in cycles.
+     * @param  array   $aVarValues          values to be compiled in.
+     * @param  mixed   $mixedKeyWrapperHtml key wrapper(string value if left and right parts are the same, array(0 => left, 1 => right) otherwise).
+     * @return string  the result of operation.
+     */
+    function _compileContent($sContent, $aVarName, $iVarDepth, $aVarValues, $mixedKeyWrapperHtml = null)
+    {
+        $aKeys = array_keys($aVarValues);
+        $aValues = array_values($aVarValues);
+
+        $aKeyWrappers = $this->_getKeyWrappers($mixedKeyWrapperHtml);
+
+        for($i = 0; $i < count($aKeys); $i++) {
+            if(strpos($aKeys[$i], 'bx_repeat:') === 0) {
+                $sKey = "'<" . $aKeys[$i] . ">(.*)<\/" . $aKeys[$i] . ">'s";
+
+                $aMatches = array();
+                preg_match($sKey, $sContent, $aMatches);
+
+                $sValue = '';
+                if(isset($aMatches[1]) && !empty($aMatches[1])) {
+                    if(empty($aValues[$i]) || !is_array($aValues[$i]))
+                        return false;
+
+                    $sIndex = "\$" . str_repeat("i", $iVarDepth);
+                    $sValue .= "<?php if(is_array(" . $aVarName . "['" . $aKeys[$i] . "'])) for(" . $sIndex . "=0; " . $sIndex . "<count(" . $aVarName . "['" . $aKeys[$i] . "']); " . $sIndex . "++){ ?>";
+                    if(($sInnerValue = $this->_compileContent($aMatches[1], $aVarName . "['" . $aKeys[$i] . "'][" . $sIndex . "]", $iVarDepth + 1, current($aValues[$i]), $mixedKeyWrapperHtml)) === false)
+                        return false;
+                    $sValue .= $sInnerValue;
+                    $sValue .= "<?php } else if(is_string(" . $aVarName . "['" . $aKeys[$i] . "'])) echo " . $aVarName . "['" . $aKeys[$i] . "']; ?>";
+                }
+            } else if(strpos($aKeys[$i], 'bx_if:') === 0) {
+                $sKey = "'<" . $aKeys[$i] . ">(.*)<\/" . $aKeys[$i] . ">'s";
+
+                $aMatches = array();
+                preg_match($sKey, $sContent, $aMatches);
+
+                $sValue = '';
+                if(isset($aMatches[1]) && !empty($aMatches[1])) {
+                    if(!is_array($aValues[$i]) || !isset($aValues[$i]['content']) || empty($aValues[$i]['content']) || !is_array($aValues[$i]['content']))
+                        return false;
+
+                    $sValue .= "<?php if(" . $aVarName . "['" . $aKeys[$i] . "']['condition']){ ?>";
+                    if(($sInnerValue = $this->_compileContent($aMatches[1], $aVarName . "['" . $aKeys[$i] . "']['content']", $iVarDepth, $aValues[$i]['content'], $mixedKeyWrapperHtml)) === false)
+                        return false;
+                    $sValue .= $sInnerValue;
+                    $sValue .= "<?php } ?>";
+                }
+            } else {
+                $sKey = "'" . $aKeyWrappers['left'] . $aKeys[$i] . $aKeyWrappers['right'] . "'s";
+                $sValue = "<?php echo " . $aVarName . "['" . $aKeys[$i] . "'];?>";
+            }
+
+            $aKeys[$i] = $sKey;
+            $aValues[$i] = $sValue;
+        }
+
+        try {
+        	$oTemplate = &$this;
+
+        	$aCallbackPatterns = array(
+        		"'<bx_include_auto:([^\s]+) \/>'s" => BX_DOL_TEMPLATE_CHECK_IN_BOTH,
+        		"'<bx_include_base:([^\s]+) \/>'s" => BX_DOL_TEMPLATE_CHECK_IN_BASE,
+        		"'<bx_include_tmpl:([^\s]+) \/>'s" => BX_DOL_TEMPLATE_CHECK_IN_TMPL
+        	);
+	        foreach($aCallbackPatterns as $sPattern => $sCheckIn)
+		        $sContent = preg_replace_callback($sPattern, function($aMatches) use($oTemplate, $aVarValues, $mixedKeyWrapperHtml, $sCheckIn) {
+		        	$mixedResult = $oTemplate->getCached($aMatches[1], $aVarValues, $mixedKeyWrapperHtml, $sCheckIn, false);
+		        	if($mixedResult === false)
+		        		throw new Exception('Unable to create cache file.');
+
+		        	return $mixedResult;
+		        }, $sContent);
+
+			$sContent = $this->_parseContentKeys($sContent);
+        }
+        catch(Exception $oException) {
+        	return false;
+        }
+
+        $aKeys = array_merge($aKeys, array(
+            "'<bx_injection:([^\s]+) />'s",
+            "'<bx_menu:([^\s]+) \/>'s",
+            "'<bx_url_root />'",
+            "'<bx_url_studio />'"
+        ));
+        $aValues = array_merge($aValues, array(
+            "<?php echo \$this->processInjection(\$this->aPage['name_index'], '\\1'); ?>",
+            "<?php echo \$this->getMenu('\\1'); ?>",
+            BX_DOL_URL_ROOT,
+            BX_DOL_URL_STUDIO
+        ));
+
+        //--- Parse Predefined Keys ---//
+        $sContent = preg_replace($aKeys, $aValues, $sContent);
+        //--- Parse System Keys ---//
+        $sContent = preg_replace( "'" . $aKeyWrappers['left'] . "([a-zA-Z0-9_-]+)" . $aKeyWrappers['right'] . "'", "<?php echo \$this->parseSystemKey('\\1', \$mixedKeyWrapperHtml);?>", $sContent);
+
+        return $sContent;
+    }
+    protected function _parseContentKeys($sContent, $aCallbackPatterns = array())
+    {
+    	$oTemplate = &$this;
+
+    	$aCallbackPatterns = array_merge($aCallbackPatterns, array(
+			"'<bx_image_url:([^\s]+) \/>'s" => "get_image_url",
+			"'<bx_icon_url:([^\s]+) \/>'s" => "get_icon_url",
+			"'<bx_text:([_\{\}\w\d\s]+[^\s]{1}) \/>'s" => "get_text",
+			"'<bx_text_js:([^\s]+) \/>'s" => "get_text_js",
+			"'<bx_text_attribute:([^\s]+) \/>'s" => "get_text_attribute",
+		));
+
+		foreach($aCallbackPatterns as $sPattern => $sAction)
+			$sContent = preg_replace_callback($sPattern, function($aMatches) use($oTemplate, $sAction) {
+		    	$sResult = '';
+
+	        	switch($sAction) {
+	        		case 'get_image_url':
+	        			$sResult = $oTemplate->getImageUrl($aMatches[1]);
+	        			break;
+	        		case 'get_icon_url':
+	        			$sResult = $oTemplate->getIconUrl($aMatches[1]);
+	        			break;
+					case 'get_text':
+	        			$sResult = _t($aMatches[1]);
+	        			break;
+	        		case 'get_text_js':
+	        			$sResult = bx_js_string(_t($aMatches[1]));
+	        			break;
+	        		case 'get_text_attribute':
+	        			$sResult = bx_html_attribute(_t($aMatches[1]));
+	        			break;
+	        		case 'get_injection':
+	        			$sResult = $oTemplate->processInjection($oTemplate->getPageNameIndex(), $aMatches[1]);
+	        			break;
+	        		case 'get_menu':
+	        			$sResult = $oTemplate->getMenu($aMatches[1]);
+	        			break;
+	        	}
+
+	        	return $sResult;
+	        }, $sContent);
+
+		return $sContent;
+    }
+
+    /**
+     * Get absolute location of some template's part.
+     *
+     * @param  string $sType    - result type. Available values 'url' and 'path'.
+     * @param  string $sFolder  - folders to be searched in. @see $_sFolderHtml, $_sFolderCss, $_sFolderImages and $_sFolderIcons
+     * @param  string $sName    - requested part name.
+     * @param  string $sCheckIn where the content would be searched(base, template, both)
+     * @return string absolute location (path/url) of the part.
+     */
+    function _getAbsoluteLocation($sType, $sFolder, $sName, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
+    {
+        $sDirectory = $this->getPath();
+
+        if($sType == 'path') {
+            $sDivider = DIRECTORY_SEPARATOR;
+            $sRoot = BX_DIRECTORY_PATH_ROOT;
+        } else if($sType == 'url') {
+            $sDivider = '/';
+            $sRoot = BX_DOL_URL_ROOT;
+        }
+
+        if(strpos($sName,'|') !== false) {
+            $aParts = explode('|', $sName);
+            $sName = $aParts[1];
+
+            if(strpos($aParts[0],'@') !== false) {
+                $aLocationParts = explode('@', $aParts[0]);
+                $sLocationKey = $this->addLocation($aLocationParts[0], BX_DIRECTORY_PATH_ROOT . $aLocationParts[1], BX_DOL_URL_ROOT . $aLocationParts[1]);
+            }
+        }
+
+        $sResult = '';
+        $aLocations = array_reverse($this->_aLocations, true);
+        foreach($aLocations as $sKey => $aLocation) {
+            if(($sCheckIn == BX_DOL_TEMPLATE_CHECK_IN_BOTH || $sCheckIn == BX_DOL_TEMPLATE_CHECK_IN_TMPL) && extFileExists(BX_DIRECTORY_PATH_MODULES . $this->getPath(). 'data' . DIRECTORY_SEPARATOR . BX_DOL_TEMPLATE_FOLDER_ROOT . DIRECTORY_SEPARATOR . $sKey . DIRECTORY_SEPARATOR . $sFolder . $sName))
+                $sResult = $sRoot . 'modules' . $sDivider . $sDirectory. 'data' . $sDivider . 'template' . $sDivider . $sKey . $sDivider . $sFolder . $sName;
+            else if(($sCheckIn == BX_DOL_TEMPLATE_CHECK_IN_BOTH || $sCheckIn == BX_DOL_TEMPLATE_CHECK_IN_BASE) && extFileExists($aLocation['path'] . BX_DOL_TEMPLATE_FOLDER_ROOT . DIRECTORY_SEPARATOR . $sFolder . $sName))
+                $sResult = $aLocation[$sType] . BX_DOL_TEMPLATE_FOLDER_ROOT . $sDivider . $sFolder . $sName;
+            else
+                continue;
+            break;
+        }
+
+        /**
+         * try to find from received path
+         */
+        if(!$sResult && @is_file(BX_DIRECTORY_PATH_ROOT . $aParts[0] . DIRECTORY_SEPARATOR . $aParts[1])) {
+            $sResult = $sRoot . $aParts[0] . $sDivider . $aParts[1];
+        }
+
+        if(isset($sLocationKey))
+           $this->removeLocation($sLocationKey);
+
+        return $sType == 'path' && !empty($sResult) ? realpath($sResult) : $sResult;
+    }
+    /**
+     * Get absolute location of some template's part.
+     *
+     * @param  string $sType result type. Available values 'url' and 'path'.
+     * @param  string $sName requested part name.
+     * @return string absolute location (path/url) of the part.
+     */
+    function _getAbsoluteLocationJs($sType, $sName)
+    {
+        $sResult = '';
+        $aLocations = array_reverse($this->_aLocationsJs, true);
+        foreach($aLocations as $sKey => $aLocation) {
+            if(extFileExists($aLocation['path'] . $sName))
+                $sResult = $aLocation[$sType] . $sName;
+            else
+                continue;
+            break;
+        }
+        return $sType == 'path' && !empty($sResult) ? realpath($sResult) : $sResult;
+    }
+    function _getAbsoluteLocationCss($sType, $sName)
+    {
+        return $this->_getAbsoluteLocation($sType, $this->_sFolderCss, $sName);
+    }
+    /**
+     * Get inline data for Images and Icons.
+     *
+     * @param  string  $sType    image/icon
+     * @param  string  $sName    file name
+     * @param  string  $sCheckIn where the content would be searched(base, template, both)
+     * @return unknown
+     */
+    function _getInlineData($sType, $sName, $sCheckIn)
+    {
+        switch($sType) {
+            case 'image':
+                $sFolder = $this->_sFolderImages;
+                break;
+            case 'icon':
+                $sFolder = $this->_sFolderIcons;
+                break;
+        }
+        $sPath = $this->_getAbsoluteLocation('path', $sFolder, $sName, $sCheckIn);
+
+        $iFileSize = 0;
+        if($this->_bImagesInline && ($iFileSize = filesize($sPath)) !== false && $iFileSize < $this->_iImagesMaxSize) {
+            $aFileInfo = pathinfo($sPath);
+            return "data:image/" . strtolower($aFileInfo['extension']) . ";base64," . base64_encode(file_get_contents($sPath));
+        }
+
+        return false;
+    }
+    /**
+     * Get file name where the template would be cached.
+     *
+     * @param  string $sAbsolutePath template's real path.
+     * @return string the result of operation.
+     */
+    function _getCacheFileName($sType, $sAbsolutePath)
+    {
+        $sResult = bx_site_hash($sAbsolutePath);
+        switch($sType) {
+            case 'html':
+                $sResult = $this->_sCacheFilePrefix . bx_lang_name() . '_' . $this->_sCode .  '_' . $sResult;
+                break;
+            case 'css':
+                $sResult = $this->_sCssCachePrefix . $sResult;
+                break;
+            case 'js':
+                $sResult = $this->_sJsCachePrefix . $sResult;
+                break;
+        }
+
+        return $sResult;
+    }
+    /**
+     * Get template key wrappers(left, right)
+     *
+     * @param  mixed $mixedKeyWrapperHtml key wrapper(string value if left and right parts are the same, array(0 => left, 1 => right) otherwise).
+     * @return array result of operation.
+     */
+    function _getKeyWrappers($mixedKeyWrapperHtml)
+    {
+        $aResult = array();
+        if(!empty($mixedKeyWrapperHtml) && is_string($mixedKeyWrapperHtml))
+            $aResult = array('left' => $mixedKeyWrapperHtml, 'right' => $mixedKeyWrapperHtml);
+        else if(!empty($mixedKeyWrapperHtml) && is_array($mixedKeyWrapperHtml))
+            $aResult = array('left' => $mixedKeyWrapperHtml[0], 'right' => $mixedKeyWrapperHtml[1]);
+        else
+            $aResult = array('left' => $this->_sKeyWrapperHtml, 'right' => $this->_sKeyWrapperHtml);
+        return $aResult;
+    }
+
+    /**
+     * Process all added language translations and return them as a string.
+     *
+     * @return string with JS code.
+     */
+    function _processJsTranslations()
+    {
+        $sReturn = '';
+        if(isset($this->aPage['js_translations']) && is_array($this->aPage['js_translations'])) {
+            foreach($this->aPage['js_translations'] as $sKey => $sString)
+                $sReturn .= "'" .  bx_js_string($sKey) . "': '" . bx_js_string($sString) . "',";
+
+            $sReturn = substr($sReturn, 0, -1);
+        }
+
+        return '<script type="text/javascript" language="javascript">var aDolLang = {' . $sReturn . '};</script>';
+    }
+    /**
+     * Process all added options and return them as a string.
+     *
+     * @return string with JS code.
+     */
+    function _processJsOptions()
+    {
+        $sReturn = '';
+        if(isset($this->aPage['js_options']) && is_array($this->aPage['js_options'])) {
+            foreach($this->aPage['js_options'] as $sName => $mixedValue)
+                $sReturn .= "'" .  bx_js_string($sName) . "': '" . bx_js_string($mixedValue) . "',";
+
+            $sReturn = substr($sReturn, 0, -1);
+        }
+
+        return '<script type="text/javascript" language="javascript">var aDolOptions = {' . $sReturn . '};</script>';
+    }
+    /**
+     * Process all added images and return them as a string.
+     *
+     * @return string with JS code.
+     */
+    function _processJsImages()
+    {
+        $sReturn = '';
+        if(isset($this->aPage['js_images']) && is_array($this->aPage['js_images'])) {
+            foreach($this->aPage['js_images'] as $sKey => $sUrl)
+                $sReturn .= "'" .  bx_js_string($sKey) . "': '" . bx_js_string($sUrl) . "',";
+
+            $sReturn = substr($sReturn, 0, -1);
+        }
+
+        return '<script type="text/javascript" language="javascript">var aDolImages = {' . $sReturn . '};</script>';
+    }
+
+    /**
+     * Get Gzip loader URL.
+     *
+     * @param $sType content type CSS/JS
+     * @param $sName file name.
+     * @return string with URL
+     */
+    function _getLoaderUrl($sType, $sName)
+    {
+        return BX_DOL_URL_ROOT . 'gzip_loader.php?file=' . $sName . '.' . $sType;
+    }
+
+    /**
+     *
+     * Functions to display pages with errors, messages and so on.
+     *
+     */
+    function displayAccessDenied ($sMsg = '')
+    {
+        header("HTTP/1.0 403 Forbidden");
+        $this->displayMsg($sMsg ? $sMsg : _t('_Access denied'));
+    }
+    function displayNoData ($sMsg = '')
+    {
+        header("HTTP/1.0 204 No Content");
+        $this->displayMsg($sMsg ? $sMsg : _t('_Empty'));
+    }
+    function displayErrorOccured ($sMsg = '')
+    {
+        header("HTTP/1.0 500 Internal Server Error");
+        $this->displayMsg($sMsg ? $sMsg : _t('_error occured'));
+    }
+    function displayPageNotFound ($sMsg = '')
+    {
+        header("HTTP/1.0 404 Not Found");
+        $this->displayMsg($sMsg ? $sMsg : _t('_sys_request_page_not_found_cpt'));
+    }
+    function displayMsg ($s, $bTranslate = false)
+    {
+        $sTitle = $bTranslate ? _t($s) : $s;
+        $oTemplate = BxDolTemplate::getInstance();
+        $oTemplate->setPageNameIndex (BX_PAGE_DEFAULT);
+        $oTemplate->setPageHeader ($sTitle);
+        $oTemplate->setPageContent ('page_main_code', DesignBoxContent($sTitle, MsgBox($sTitle), BX_DB_PADDING_DEF));
+        $oTemplate->getPageCode();
+        exit;
+    }
+
+    /**
+     * * * * Static methods for work with template injections * * *
+     *
+     * Static method is used to add/replace the content of some key in the template.
+     * It's usefull when you don't want to modify existing template but need to add some data to existing template key.
+     *
+     * @param  integer $iPageIndex - page index where injections would processed. Use 0 if you want it to be done on all the pages.
+     * @param  string  $sKey       - template key.
+     * @param  string  $sValue     - the data to be added.
+     * @return string  the result of operation.
+     */
+    function processInjection($iPageIndex, $sKey, $sValue = "")
+    {
+        if($iPageIndex != 0 && isset($this->aPage['injections']['page_0'][$sKey]) && isset($this->aPage['injections']['page_' . $iPageIndex][$sKey]))
+           $aSelection = @array_merge($this->aPage['injections']['page_0'][$sKey], $this->aPage['injections']['page_' . $iPageIndex][$sKey]);
+        else if(isset($this->aPage['injections']['page_0'][$sKey]))
+           $aSelection = $this->aPage['injections']['page_0'][$sKey];
+        else if(isset($this->aPage['injections']['page_' . $iPageIndex][$sKey]))
+            $aSelection = $this->aPage['injections']['page_' . $iPageIndex][$sKey];
+        else
+            $aSelection = array();
+
+        if(is_array($aSelection))
+            foreach($aSelection as $aInjection) {
+
+                if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->beginInjection($sRand = time().rand());
+
+                $sInjData = '';
+                switch($aInjection['type']) {
+                    case 'text':
+                        $sInjData = $aInjection['data'];
+                        break;
+
+                    case 'service':
+                    	if(BxDolService::isSerializedService($aInjection['data']))
+                    		$sInjData = BxDolService::callSerialized($aInjection['data']);                    	
+                        break;
+                }
+
+                if((int)$aInjection['replace'] == 1)
+                    $sValue = $sInjData;
+                else
+                    $sValue .= $sInjData;
+
+                if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->endInjection($sRand, $aInjection);
+
+            }
+
+        return $sValue != '__' . $sKey . '__' ? str_replace('__' . $sKey . '__', '', $sValue) : $sValue;
+    }
+    /**
+     * Static method to add ingection available on the current page only.
+     *
+     * @param string  $sKey     - template's key.
+     * @param string  $sType    - injection type(text, php).
+     * @param string  $sData    - the data to be added.
+     * @param integer $iReplace - replace already existed data or not.
+     */
+    function addInjection($sKey, $sType, $sData, $iReplace = 0)
+    {
+        $this->aPage['injections']['page_0'][$sKey][] = array(
+            'page_index' => 0,
+            'key' => $sKey,
+            'type' => $sType,
+            'data' => $sData,
+            'replace' => $iReplace
+        );
+    }
+
+    function getPageCode($oTemplate = null)
+    {
+        if (empty($oTemplate))
+           $oTemplate = $this;
+
+        header( 'Content-type: text/html; charset=utf-8' );
+        header( 'X-Frame-Options: sameorigin' );
+        echo $oTemplate->parsePageByName('page_' . $oTemplate->getPageNameIndex() . '.html', $oTemplate->getPageContent());
+    }
+}
+
+/** @} */

+ 396 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/classes/BxDolVote.php

@@ -0,0 +1,396 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolObject');
+bx_import('BxDolVoteQuery');
+
+define('BX_DOL_VOTE_OLD_VOTES', 365 * 86400); ///< votes older than this number of seconds will be deleted automatically
+
+define('BX_DOL_VOTE_TYPE_STARS', 'stars');
+define('BX_DOL_VOTE_TYPE_LIKES', 'likes');
+
+define('BX_DOL_VOTE_USAGE_BLOCK', 'block');
+define('BX_DOL_VOTE_USAGE_INLINE', 'inline');
+define('BX_DOL_VOTE_USAGE_DEFAULT', BX_DOL_VOTE_USAGE_BLOCK);
+
+/**
+ * @page objects
+ * @section votes Votes
+ * @ref BxDolVote
+ */
+
+/**
+ * Vote for any content
+ *
+ * Related classes:
+ * - BxDolVoteQuery - vote database queries
+ * - BxBaseVote - vote base representation
+ * - BxTemplVote - custom template representation
+ *
+ * AJAX vote for any content. Stars and Plus based representations are supported.
+ *
+ * To add vote section to your feature you need to add a record to 'sys_objects_vote' table:
+ *
+ * - ID - autoincremented id for internal usage
+ * - Name - your unique module name, with vendor prefix, lowercase and spaces are underscored
+ * - TableMain - table name where summary votigs are stored
+ * - TableTrack - table name where each vote is stored
+ * - PostTimeout - number of seconds to not allow duplicate vote
+ * - MinValue - min vote value, 1 by default
+ * - MaxValue - max vote value, 5 by default
+ * - IsUndo - is Undo enabled for Plus based votes
+ * - IsOn - is this vote object enabled
+ * - TriggerTable - table to be updated upon each vote
+ * - TriggerFieldId - TriggerTable table field with unique record id, primary key
+ * - TriggerFieldRate - TriggerTable table field with average rate
+ * - TriggerFieldRateCount - TriggerTable table field with votes count
+ * - ClassName - your custom class name, if you overrride default class
+ * - ClassFile - your custom class path
+ *
+ * You can refer to BoonEx modules for sample record in this table.
+ *
+ *
+ *
+ * @section example Example of usage:
+ * To get Star based vote you need to have different values for MinValue and MaxValue (for example 1 and 5)
+ * and IsUndo should be equal to 0. To get Plus(Like) based vote you need to have equal values
+ * for MinValue and MaxValue (for example 1) and IsUndo should be equal to 1. After filling the other
+ * paramenters in the table you can show vote in any place, using the following code:
+ * @code
+ * bx_import('BxDolVote');
+ * $o = BxDolVote::getObjectInstance('system object name', $iYourEntryId);
+ * if (!$o->isEnabled()) return '';
+ *     echo $o->getElementBlock();
+ * @endcode
+ *
+ *
+ * @section acl Memberships/ACL:
+ * - vote
+ *
+ *
+ *
+ * @section alerts Alerts:
+ * Alerts type/unit - every module has own type/unit, it equals to ObjectName.
+ * The following alerts are rised:
+ *
+ * - rate - comment was posted
+ *      - $iObjectId - entry id
+ *      - $iSenderId - rater user id
+ *      - $aExtra['rate'] - rate
+ *
+ */
+
+class BxDolVote extends BxDolObject
+{
+    public function __construct($sSystem, $iId, $iInit = true)
+    {
+        parent::__construct($sSystem, $iId, $iInit);
+        if(empty($this->_sSystem))
+            return;
+
+        $this->_oQuery = new BxDolVoteQuery($this);
+    }
+
+    /**
+     * get votes object instanse
+     * @param $sSys vote object name
+     * @param $iId associated content id, where vote is available
+     * @param $iInit perform initialization
+     * @return null on error, or ready to use class instance
+     */
+    public static function getObjectInstance($sSys, $iId, $iInit = true)
+    {
+        if(isset($GLOBALS['bxDolClasses']['BxDolVote!' . $sSys . $iId]))
+            return $GLOBALS['bxDolClasses']['BxDolVote!' . $sSys . $iId];
+
+        $aSystems = self::getSystems();
+        if(!isset($aSystems[$sSys]))
+            return null;
+
+        bx_import('BxTemplVote');
+        $sClassName = 'BxTemplVote';
+        if(!empty($aSystems[$sSys]['class_name'])) {
+            $sClassName = $aSystems[$sSys]['class_name'];
+            if(!empty($aSystems[$sSys]['class_file']))
+                require_once(BX_DIRECTORY_PATH_ROOT . $aSystems[$sSys]['class_file']);
+            else
+                bx_import($sClassName);
+        }
+
+        $o = new $sClassName($sSys, $iId, $iInit);
+        return ($GLOBALS['bxDolClasses']['BxDolVote!' . $sSys . $iId] = $o);
+    }
+
+    public static function &getSystems()
+    {
+        if(!isset($GLOBALS['bx_dol_vote_systems']))
+            $GLOBALS['bx_dol_vote_systems'] = BxDolDb::getInstance()->fromCache('sys_objects_vote', 'getAllWithKey', '
+                SELECT
+                    `ID` as `id`,
+                    `Name` AS `name`,
+                    `TableMain` AS `table_main`,
+                    `TableTrack` AS `table_track`,
+                    `PostTimeout` AS `post_timeout`,
+                    `MinValue` AS `min_value`,
+                    `MaxValue` AS `max_value`,
+                    `IsUndo` AS `is_undo`,
+                    `IsOn` AS `is_on`,
+                    `TriggerTable` AS `trigger_table`,
+                    `TriggerFieldId` AS `trigger_field_id`,
+                    `TriggerFieldRate` AS `trigger_field_rate`,
+                    `TriggerFieldRateCount` AS `trigger_field_count`,
+                    `ClassName` AS `class_name`,
+                    `ClassFile` AS `class_file`
+                FROM `sys_objects_vote`', 'name');
+
+        return $GLOBALS['bx_dol_vote_systems'];
+    }
+
+    /**
+     * it is called on cron every day or similar period to clean old votes
+     */
+    public static function maintenance()
+    {
+        $iResult = 0;
+        $oDb = BxDolDb::getInstance();
+
+        $aSystems = self::getSystems();
+        foreach($aSystems as $aSystem) {
+            if(!$aSystem['is_on'])
+                continue;
+
+            $sQuery = $oDb->prepare("DELETE FROM `{$aSystem['table_track']}` WHERE `date` < (UNIX_TIMESTAMP() - ?)", BX_DOL_VOTE_OLD_VOTES);
+            $iDeleted = (int)$oDb->query($sQuery);
+            if($iDeleted > 0)
+                $oDb->query("OPTIMIZE TABLE `{$aSystem['table_track']}`");
+
+            $iResult += $iDeleted;
+        }
+
+        return $iResult;
+    }
+
+    public function isUndo()
+    {
+        return (int)$this->_aSystem['is_undo'] == 1;
+    }
+
+    public function isLikeMode()
+    {
+        $iMinValue = $this->getMinValue();
+        $iMaxValue = $this->getMaxValue();
+
+        return $iMinValue == $iMaxValue;
+    }
+
+    public function getMinValue()
+    {
+        return (int)$this->_aSystem['min_value'];
+    }
+
+    public function getMaxValue()
+    {
+        return (int)$this->_aSystem['max_value'];
+    }
+
+    /**
+     * Interface functions for outer usage
+     */
+    public function getStatCounter()
+    {
+        $aVote = $this->_oQuery->getVote($this->getId());
+        return $aVote['count'];
+    }
+
+    public function getStatRate()
+    {
+        $aVote = $this->_oQuery->getVote($this->getId());
+        return $aVote['rate'];
+    }
+
+    public function getSqlParts($sMainTable, $sMainField)
+    {
+        if(!$this->isEnabled())
+            return array();
+
+        return $this->_oQuery->getSqlParts($sMainTable, $sMainField);
+    }
+
+    /**
+     * Actions functions
+     */
+    public function actionVote()
+    {
+        if(!$this->isEnabled()) {
+            $this->_echoResultJson(array('code' => 1));
+            return;
+        }
+
+        $iObjectId = $this->getId();
+        $iAuthorId = $this->_getAuthorId();
+        $iAuthorIp = $this->_getAuthorIp();
+
+        $bUndo = $this->isUndo();
+        $bLikeMode = $this->isLikeMode();
+
+        $bVoted = $this->_oQuery->isVoted($iObjectId, $iAuthorId);
+        $bPerformUndo = $bVoted && $bUndo ? true : false;
+
+        if(!$bPerformUndo && !$this->isAllowedVote(true)) {
+            $this->_echoResultJson(array('code' => 2, 'msg' => $this->msgErrAllowedVote()));
+            return;
+        }
+
+        if((!$bLikeMode && !$this->_oQuery->isPostTimeoutEnded($iObjectId, $iAuthorIp)) || ($bLikeMode && $bVoted && !$bUndo)) {
+            $this->_echoResultJson(array('code' => 3, 'msg' => _t('_vote_err_duplicate_vote')));
+            return;
+        }
+
+        $iValue = bx_get('value');
+        if($iValue === false) {
+            $this->_echoResultJson(array('code' => 4));
+            return;
+        }
+
+        $iValue = bx_process_input($iValue, BX_DATA_INT);
+
+        $iMinValue = $this->getMinValue();
+        if($iValue < $iMinValue)
+            $iValue = $iMinValue;
+
+        $iMaxValue = $this->getMaxValue();
+        if($iValue > $iMaxValue)
+            $iValue = $iMaxValue;
+
+        if(!$this->_oQuery->putVote($iObjectId, $iAuthorId, $iAuthorIp, $iValue, $bPerformUndo)) {
+            $this->_echoResultJson(array('code' => 5));
+            return;
+        }
+
+        $this->_triggerVote();
+
+        $oZ = new BxDolAlerts($this->_sSystem, 'rate', $this->getId(), $iAuthorId, array('value' => $iValue, 'undo' => $bPerformUndo));
+        $oZ->alert();
+
+        $aVote = $this->_oQuery->getVote($iObjectId);
+        $this->_echoResultJson(array(
+            'code' => 0,
+            'rate' => $aVote['rate'],
+            'count' => $aVote['count'],
+            'countf' => (int)$aVote['count'] > 0 ? $this->_getLabelCounter($aVote['count']) : '',
+        	'label_icon' => $bLikeMode ? $this->_getIconDoLike(!$bVoted) : '',
+        	'label_title' => $bLikeMode ? _t($this->_getTitleDoLike(!$bVoted)) : '',
+        	'disabled' => !$bVoted && !$bUndo,
+        ));
+    }
+
+    public function actionGetVotedBy()
+    {
+        if (!$this->isEnabled())
+           return '';
+
+        return $this->_getVotedBy();
+    }
+
+    /**
+     * Permissions functions
+     */
+    public function checkAction ($sAction, $isPerformAction = false)
+    {
+        $iId = $this->_getAuthorId();
+        $a = checkActionModule($iId, $sAction, 'system', $isPerformAction);
+        return $a[CHECK_ACTION_RESULT] === CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    public function checkActionErrorMsg ($sAction)
+    {
+        $iId = $this->_getAuthorId();
+        $a = checkActionModule($iId, $sAction, 'system');
+        return $a[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED ? $a[CHECK_ACTION_MESSAGE] : '';
+    }
+
+    public function isAllowedVote($isPerformAction = false)
+    {
+        if(isAdmin())
+            return true;
+
+        return $this->checkAction('vote', $isPerformAction);
+    }
+
+    public function msgErrAllowedVote()
+    {
+        return $this->checkActionErrorMsg('vote');
+    }
+
+    function onObjectDelete($iObjectId = 0)
+    {
+        $this->_oQuery->deleteObjectVotes($iObjectId ? $iObjectId : $this->getId());
+    }
+
+    /**
+     * Internal functions
+     */
+    protected function _getAuthorId ()
+    {
+        return isMember() ? bx_get_logged_profile_id() : 0;
+    }
+
+    protected function _getAuthorIp ()
+    {
+        return getVisitorIP();
+    }
+
+    protected function _getAuthorInfo($iAuthorId = 0)
+    {
+        $oProfile = $this->_getAuthorObject($iAuthorId);
+
+        return array(
+            $oProfile->getDisplayName(),
+            $oProfile->getUrl(),
+            $oProfile->getThumb(),
+            $oProfile->getUnit()
+        );
+    }
+
+    protected function _getAuthorObject($iAuthorId = 0)
+    {
+        bx_import('BxDolProfile');
+        $oProfile = BxDolProfile::getInstance($iAuthorId);
+        if (!$oProfile) {
+            bx_import('BxDolProfileUndefined');
+            $oProfile = BxDolProfileUndefined::getInstance();
+        }
+
+        return $oProfile;
+    }
+
+    protected function _getIconDoLike($bVoted)
+    {
+    	return $bVoted && $this->isUndo() ?  'thumbs-down' : 'thumbs-up';
+    }
+
+    protected function _getTitleDoLike($bVoted)
+    {
+    	return $bVoted && $this->isUndo() ? '_vote_do_unlike' : '_vote_do_like';
+    }
+
+    protected function _triggerVote()
+    {
+        if(!$this->_aSystem['trigger_table'])
+            return false;
+
+        $iId = $this->getId();
+        if(!$iId)
+            return false;
+
+        return $this->_oQuery->updateTriggerTable($iId);
+    }
+}
+
+/** @} */

+ 150 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/js/classes/BxDolVote.js

@@ -0,0 +1,150 @@
+/**
+ * @package     Dolphin Core
+ * @copyright   Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * @license     CC-BY - http://creativecommons.org/licenses/by/3.0/
+ */
+
+function BxDolVote(options)
+{
+	this._sObjName = undefined == options.sObjName ? 'oVote' : options.sObjName; // javascript object name, to run current object instance from onTimer
+	this._sSystem = options.sSystem; // current comment system
+	this._iAuthorId = options.iAuthorId; // this comment's author ID.
+    this._iObjId = options.iObjId; // this object id comments
+    this._iLikeMode = undefined == options.iLikeMode ? 0 : options.iLikeMode;
+
+    this._sActionsUri = 'vote.php';
+    this._sActionsUrl = options.sRootUrl + this._sActionsUri; // actions url address
+
+    this._sAnimationEffect = 'fade';
+    this._iAnimationSpeed = 'slow';
+    this._sSP = undefined == options.sStylePrefix ? 'bx-vote' : options.sStylePrefix;
+    this._aHtmlIds = options.aHtmlIds;
+
+    this._iSaveWidth = -1;
+
+    //--- Init stars based vote ---//
+    if(!this._iLikeMode) {
+	    var oMainStars = $('#' + this._aHtmlIds['main_stars']);
+	    if(oMainStars) {
+	    	var fRate = oMainStars.attr('bx_vote_data_rate');
+	    	var iStarWidth = this._getStarWidth(oMainStars);
+
+	    	this._getSlider(oMainStars).width(Math.round(fRate * iStarWidth));
+	    }
+    }
+}
+
+BxDolVote.prototype.toggleByPopup = function(oLink) {
+	var $this = this;
+    var oData = this._getDefaultParams();
+    oData['action'] = 'GetVotedBy';
+
+	$(oLink).dolPopupAjax({
+		id: this._aHtmlIds['by_popup'], 
+		url: bx_append_url_params(this._sActionsUri, oData)
+	});
+};
+
+BxDolVote.prototype.over = function (oLink)
+{
+	var oSlider = this._getSlider(oLink);
+	var iIndex = this._getButtons(oLink).index(oLink);
+
+    this._iSaveWidth = parseInt(oSlider.width());
+    oSlider.width((iIndex + 1) * this._getStarWidth(oLink));
+};
+
+BxDolVote.prototype.out = function (oLink)
+{
+	var oSlider = this._getSlider(oLink);
+
+	oSlider.width(this._iSaveWidth);
+};
+
+BxDolVote.prototype.vote = function (oLink, iValue)
+{
+    var $this = this;
+    var oParams = this._getDefaultParams();
+    oParams['action'] = 'Vote';
+    oParams['value'] = iValue;
+
+    $.post(
+    	this._sActionsUrl,
+    	oParams,
+    	function(oData) {
+    		if(oData && oData.msg != undefined)
+                alert(oData.msg);
+
+    		if(oData && oData.code != 0)
+                return;
+
+    		if($this._iLikeMode) {
+	    		if(oData && oData.label_icon)
+	    			$(oLink).find('.sys-icon').attr('class', 'sys-icon ' + oData.label_icon);
+
+	    		if(oData && oData.label_title) {
+	    			$(oLink).attr('title', oData.label_title);
+	    			$(oLink).find('span').html(oData.label_title);
+	    		}
+
+	    		if(oData && oData.disabled)
+	    			$(oLink).removeAttr('onclick').addClass('bx-btn-disabled');
+    		}
+
+    		if(!$this._iLikeMode)
+    			$this._iSaveWidth = Math.round(oData.rate * $this._getStarWidth(oLink));
+
+            var oCounter = $this._getCounter(oLink);
+            if(oCounter)
+            	oCounter.html(oData.countf);
+
+            oCounter.parents('.' + $this._sSP + '-counter-holder:first').bx_anim(oData.count > 0 ? 'show' : 'hide');
+        },
+        'json'
+    );
+};
+
+BxDolVote.prototype._getButtons = function(oElement) {
+	if($(oElement).hasClass(this._sSP))
+		return $(oElement).find('.' + this._sSP + '-button');
+	else
+		return $(oElement).parents('.' + this._sSP + ':first').find('.' + this._sSP + '-button');
+};
+
+BxDolVote.prototype._getSlider = function(oElement) {
+	if($(oElement).hasClass(this._sSP))
+		return $(oElement).find('.' + this._sSP + '-slider');
+	else
+		return $(oElement).parents('.' + this._sSP + ':first').find('.' + this._sSP + '-slider');
+};
+
+BxDolVote.prototype._getStarWidth = function(oElement) {
+	var oSlider = this._getSlider(oElement);
+	return oSlider.find('.sys-icon').width();
+};
+
+BxDolVote.prototype._getCounter = function(oElement) {
+	if(this._aHtmlIds['counter'] && this._aHtmlIds['counter'].length > 0)
+		return $('#' + this._aHtmlIds['counter']);
+
+	if($(oElement).hasClass(this._sSP))
+		return $(oElement).find('.' + this._sSP + '-counter span');
+	else 
+		return $(oElement).parents('.' + this._sSP + ':first').find('.' + this._sSP + '-counter span');
+};
+
+BxDolVote.prototype._loadingInButton = function(e, bShow) {
+	if($(e).length)
+		bx_loading_btn($(e), bShow);
+	else
+		bx_loading($('body'), bShow);	
+};
+
+BxDolVote.prototype._getDefaultParams = function() {
+	var oDate = new Date();
+    return {
+        sys: this._sSystem,
+        id: this._iObjId,
+        _t: oDate.getTime()
+    };
+};

+ 555 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/js/functions.js

@@ -0,0 +1,555 @@
+/**
+ * @package     Dolphin Core
+ * @copyright   Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * @license     CC-BY - http://creativecommons.org/licenses/by/3.0/
+ */
+
+
+
+function getHtmlData( elem, url, callback, method , confirmation)
+{
+    if ('undefined' != typeof(confirmation) && confirmation && !confirm(_t('_are you sure?'))) 
+        return false;
+
+    // in most cases it is element ID, in other cases - object of jQuery
+    if (typeof elem == 'string')
+        elem = '#' + elem; // create selector from ID
+
+    var $block = $(elem);
+
+    var blockPos = $block.css('position');
+
+    $block.css('position', 'relative'); // set temporarily for displaying "loading icon"
+
+    bx_loading_content($block, true);
+    var $loadingDiv = $block.find('.bx-loading-ajax');
+
+    var iLeftOff = parseInt(($block.innerWidth() / 2.0) - ($loadingDiv.outerWidth()  / 2.0));
+    var iTopOff  = parseInt(($block.innerHeight() / 2.0) - ($loadingDiv.outerHeight()));
+    if (iTopOff<0) iTopOff = 0;
+
+    $loadingDiv.css({
+        position: 'absolute',
+        left: iLeftOff,
+        top:  iTopOff
+    });
+
+    if (undefined != method && (method == 'post' || method == 'POST')) {
+
+        $.post(url, function(data) {
+
+            $block.html(data);
+	        $block.css('position', blockPos).bxTime();
+            if ($.isFunction($.fn.addWebForms))
+                $block.addWebForms();
+
+            bx_activate_anim_icons();
+
+            if (typeof callback == 'function')
+                callback.apply($block);
+        });
+
+    } else {
+
+        $block.load(url + '&_r=' + Math.random(), function() {
+
+	        $(this).css('position', blockPos).bxTime();
+            if ($.isFunction($.fn.addWebForms))
+                $(this).addWebForms();
+
+            bx_activate_anim_icons();
+
+            if (typeof callback == 'function')
+                callback.apply(this);
+        });
+
+    }
+}
+
+function loadDynamicBlockAutoPaginate (e, iStart, iPerPage, sAdditionalUrlParams) {
+
+    sUrl = location.href;
+
+    sUrl = sUrl.replace(/start=\d+/, '').replace(/per_page=\d+/, '').replace(/[&\?]+$/, '');
+    sUrl = bx_append_url_params(sUrl, 'start=' + parseInt(iStart) + '&per_page=' + parseInt(iPerPage));
+    if ('undefined' != typeof(sAdditionalUrlParams))
+        sUrl = bx_append_url_params(sUrl, sAdditionalUrlParams);
+
+    return loadDynamicBlockAuto(e, sUrl);
+}
+
+/**
+ * This function reloads page block automatically, 
+ * just provide any element inside the block and this function will reload the block.
+ * @param e - element inside the block
+ * @return true on success, or false on error - particularly, if block isn't found
+ */
+function loadDynamicBlockAuto(e, sUrl) {
+    var sId = $(e).parents('.bx-page-block-container:first').attr('id');
+
+    if ('undefined' == typeof(sUrl))
+        sUrl = location.href;
+
+    if (!sId || !sId.length)
+        return false;
+
+    var aMatches = sId.match(/\-(\d+)$/);
+    if (!aMatches || !aMatches[1])
+        return false;
+        
+    loadDynamicBlock(parseInt(aMatches[1]), sUrl);
+    return true;
+}
+
+function loadDynamicBlock(iBlockID, sUrl) {
+    getHtmlData($('#bx-page-block-' + iBlockID), bx_append_url_params(sUrl, 'dynamic=tab&pageBlock=' + iBlockID));
+    return true;
+}
+
+function loadDynamicPopupBlock(iBlockID, sUrl) {
+    if (!$('#dynamicPopup').length) {
+        $('<div id="dynamicPopup" style="display:none;"></div>').prependTo('body');
+    }
+
+    $('#dynamicPopup').load(
+        (sUrl + '&dynamic=popup&pageBlock=' + iBlockID),
+        function() {
+            $(this).dolPopup({
+                left: 0,
+                top: 0
+            });
+        }
+    );
+}
+
+function closeDynamicPopupBlock() {
+    $('#dynamicPopup').dolPopupHide();
+}
+
+
+/**
+ * Translate string
+ */
+function _t(s, arg0, arg1, arg2) {
+    if (!window.aDolLang || !aDolLang[s])
+        return s;
+
+    cs = aDolLang[s];
+    cs = cs.replace(/\{0\}/g, arg0);
+    cs = cs.replace(/\{1\}/g, arg1);
+    cs = cs.replace(/\{2\}/g, arg2);
+    return cs;
+}
+
+function showPopupAnyHtml(sUrl, sId) {
+
+    var oPopupOptions = {};
+
+    if (!sId || !sId.length)
+        sId = 'login_div';
+
+    $('#' + sId).remove();
+    $('<div id="' + sId + '" style="display: none;"></div>').prependTo('body').load(
+        sUrl.match('^http[s]{0,1}:\/\/') ? sUrl : sUrlRoot + sUrl,
+        function() {
+            $(this).dolPopup(oPopupOptions);
+        }
+    );
+}
+
+
+function bx_loading_btn (e, b) {
+    e = $(e);
+
+    if (e.children('div').size())
+        e = e.children('div').first();
+
+    if (!b) {
+        e.find('.bx-loading-ajax-btn').remove();
+    } else if (!e.find('.bx-loading-ajax-btn').length) {
+        e.append('<b class="bx-loading-ajax-btn"></b>');
+        new Spinner(aSpinnerSmallOpts).spin(e.find('.bx-loading-ajax-btn').get(0));
+    }
+}
+
+function bx_loading_animate (e) {
+    e = $(e);
+    if (!e.length)
+        return false;
+    if (e.find('.bx-sys-spinner').length)
+        return false;
+    return new Spinner(aSpinnerOpts).spin(e.get(0));
+}
+
+function bx_loading_content (elem, b, isReplace) {
+    var block = $(elem);
+    if (!b) {
+        block.find(".bx-loading-ajax").remove();
+    } else if (!block.find('.bx-loading-ajax').length) {
+        if ('undefined' != typeof(isReplace) && isReplace)
+            block.html('<div class="bx-loading-ajax bx-def-z-index-front" style="position:static;"></div>');
+        else
+            block.append('<div class="bx-loading-ajax bx-def-z-index-front"></div>');
+        bx_loading_animate(block.find('.bx-loading-ajax'));
+    } 
+}
+
+function bx_loading (elem, b) {
+
+    if (typeof elem == 'string')
+        elem = '#' + elem;
+
+    var block = $(elem);
+
+    if (1 == b || true == b) {
+
+        bx_loading_content(block, b);
+
+        e = block.find(".bx-loading-ajax");
+        e.css('left', parseInt(block.width()/2.0 - e.width()/2.0));
+
+        var he = e.outerHeight();
+        var hc = block.outerHeight();
+
+        if (block.css('position') != 'relative' && block.css('position') != 'absolute') {
+            if (!block.data('css-save-position'))
+                block.data('css-save-position', block.css('position'));
+            block.css('position', 'relative');
+        }
+
+        if (hc > he) {
+            e.css('top', parseInt(hc/2.0 - he/2.0));
+        }
+
+        if (hc < he) {
+            if (!block.data('css-save-min-height'))
+                block.data('css-save-min-height', block.css('min-height'));
+            block.css('min-height', he);
+        }
+
+    } else {
+
+        if (block.data('css-save-position'))
+            block.css('position', block.data('css-save-position'));
+
+        if (block.data('css-save-min-height'))
+            block.css('min-height', block.data('css-save-min-height'));
+
+        bx_loading_content(block, b);
+
+    }
+
+}
+
+
+/**
+ * Center content with floating blocks.
+ * sSel - jQuery selector of content to be centered
+ * sBlockSel - jquery selector of blocks
+ */
+function bx_center_content (sSel, sBlockStyle) {
+    var sId = 'id' + (new Date()).getTime();
+    $(sSel).wrap('<div id="'+sId+'"></div>');
+
+    var eCenter = $('#' + sId);
+    var iAll = $('#' + sId + ' ' + sBlockStyle).size();
+    var iWidthUnit = $('#' + sId + ' ' + sBlockStyle + ':first').outerWidth(true);
+    var iWidthContainer = eCenter.innerWidth();           
+    var iPerRow = parseInt(iWidthContainer/iWidthUnit);
+    var iLeft = (iWidthContainer - (iAll > iPerRow ? iPerRow * iWidthUnit : iAll * iWidthUnit)) / 2;
+    eCenter.css("padding-left", iLeft);
+}
+
+/**
+ * Show pointer popup with menu from URL.
+ * @param o - menu object name
+ * @param e - element to show popup at
+ * @param options - popup options
+ * @param vars - additional GET variables
+ */
+function bx_menu_popup (o, e, options, vars) {
+    var options = options || {};
+    var vars = vars || {};
+    var o = $.extend({}, $.fn.dolPopupDefaultOptions, options, {id: o, url: bx_append_url_params('menu.php', $.extend({o:o}, vars))});
+    $(e).dolPopupAjax(o);
+}
+
+/**
+ * Show pointer popup with menu from existing HTML.
+ * @param jSel - jQuery selector for html to show in popup
+ * @param e - element to show popup at
+ * @param options - popup options
+ */
+function bx_menu_popup_inline (jSel, e, options) {
+    var options = options || {};
+    var o = $.extend({}, $.fn.dolPopupDefaultOptions, options, {pointer:{el:$(e)}});
+    if ($(jSel + ':visible').length) 
+        $(jSel).dolPopupHide(); 
+    else 
+        $(jSel).dolPopup(o);
+}
+
+/**
+ * Show pointer popup with menu from existing HTML.
+ * @param jSel - jQuery selector for html to show in popup
+ * @param e - element to click to open/close slider
+ * @param options - popup options
+ */
+function bx_menu_slide (jSel, e) {
+    var options = options || {};
+    var eSlider = $(jSel);
+    var eBlock = eSlider.parents('.bx-page-block-container');
+    var eIcon = $(e).find('.sys-icon-a');
+
+    var fClose = function () {
+        if (eIcon)
+            (new Marka(eIcon[0])).set(eIcon.attr('data-icon-orig'));
+        eSlider.slideUp()
+    };
+
+    var fOpen = function () {
+        if (eIcon) {
+            eIcon.attr('data-icon-orig', eIcon.attr('data-icon'));
+            (new Marka(eIcon[0])).set('times');
+        }
+        eSlider.css({
+            position: 'absolute',
+            top: eBlock.find('.bx-db-header').outerHeight(true),
+            left: 0,
+            width: eBlock.width()
+        });
+        eSlider.slideDown();
+    };    
+    
+    if ($(jSel + ':visible').length) {
+        fClose();
+    } 
+    else {
+        fOpen();
+        eSlider.find('a').each(function () {
+            $(this).on('click', function () {
+                fClose();
+            });
+        });
+    }
+}
+
+/**
+ * Set ACL level for specified profile
+ * @param iProfileId - profile id to set acl level for
+ * @param iAclLevel - acl level id to assign to a given rofile
+ */
+function bx_set_acl_level (iProfileId, iAclLevel, mixedLoadingElement) {
+
+    if ('undefined' != typeof(mixedLoadingElement))
+        bx_loading($(mixedLoadingElement), true);
+
+    $.post(sUrlRoot + 'menu.php', {o:'sys_set_acl_level', 'profile_id': iProfileId, 'acl_level_id': iAclLevel}, function(data) {
+
+        if ('undefined' != typeof(mixedLoadingElement))
+            bx_loading($(mixedLoadingElement), false);
+
+        if (data.length) {
+            alert(data);
+        } else if ($(mixedLoadingElement).hasClass('bx-popup-applied')) {
+            $(mixedLoadingElement).dolPopupHide().remove();
+        }
+    });
+}
+
+function validateLoginForm(eForm) {
+    return true;
+}
+
+/**
+ * convert <time> tags to human readable format
+ * @param sLang - use sLang localization
+ * @param isAutoupdate - for internal usage only
+ */
+function bx_time(sLang, isAutoupdate, sRootSel) {
+    if ('undefined' == typeof(sRootSel))
+        sRootSel = 'body';
+    var iAutoupdate = 22*60*60; // autoupdate time in realtime if less than 22 hours
+    var sSel = 'time';
+
+    if ('undefined' == typeof(sLang)) {
+        sLang = glBxTimeLang;
+    } else {
+        glBxTimeLang = sLang;
+    }
+
+    if ('undefined' != typeof(isAutoupdate) && isAutoupdate)
+        sSel += '.bx-time-autoupdate';
+
+    $(sRootSel).find(sSel).each(function () {
+        var s;
+        var sTime = $(this).attr('datetime');
+        var iSecondsDiff = moment(sTime).unix() - moment().unix();
+        if (iSecondsDiff < 0)
+            iSecondsDiff = -iSecondsDiff;
+
+        if (iSecondsDiff < $(this).attr('data-bx-autoformat'))
+            s = moment(sTime).lang(sLang).fromNow(); // 'time ago' format
+        else
+            s = moment(sTime).lang(sLang).format($(this).attr('data-bx-format')); // custom format
+
+        if (iSecondsDiff < iAutoupdate)
+            $(this).addClass('bx-time-autoupdate');
+        else
+            $(this).removeClass('bx-time-autoupdate');
+
+        $(this).html(s);
+    });
+
+    if ($('time.bx-time-autoupdate').size()) {
+        setTimeout(function () {
+            bx_time(sLang, true);
+        }, 20000);
+    }
+}
+
+(function($) {
+    $.fn.bxTime = function() {
+        bx_time(undefined, undefined, this);
+        return this;
+    }; 
+} (jQuery));
+
+
+/**
+ * Perform connections AJAX request. 
+ * In case of error - it shows js alert with error message.
+ * In case of success - the page is reloaded.
+ * 
+ * @param sObj - connections object
+ * @param sAction - 'add', 'remove' or 'reject'
+ * @param iContentId - content id, initiator is always current logged in user
+ * @param bConfirm - show confirmation dialog
+ */
+function bx_conn_action(e, sObj, sAction, iContentId, bConfirm) {
+    if ('undefined' != typeof(bConfirm) && bConfirm && !confirm(_t('_are you sure?')))
+        return;
+                
+    var aParams = {
+        obj: sObj,
+        act: sAction,
+        id: iContentId
+    };
+    var fCallback = function (data) {
+        bx_loading_btn(e, 0);
+        if ('object' != typeof(data))
+            return;
+        if (data.err) {
+            alert(data.msg);
+        } else {
+            if (!loadDynamicBlockAuto(e))
+                location.reload();
+            else
+                $('#bx-popup-ajax-wrapper-bx_persons_view_actions_more').remove();
+        }
+    };
+
+    bx_loading_btn(e, 1);
+
+    $.ajax({
+        dataType: 'json',
+        url: sUrlRoot + 'conn.php',
+        data: aParams,
+        type: 'POST',
+        success: fCallback
+    });
+}
+
+function bx_append_url_params (sUrl, mixedParams) {
+    var sParams = sUrl.indexOf('?') == -1 ? '?' : '&';
+
+    if(mixedParams instanceof Array) {
+    	for(var i in mixedParams)
+            sParams += i + '=' + mixedParams[i] + '&';
+        sParams = sParams.substr(0, sParams.length-1);
+    }
+    else if(mixedParams instanceof Object) {
+    	$.each(mixedParams, function(sKey, sValue) {
+    		sParams += sKey + '=' + sValue + '&';
+    	});
+        sParams = sParams.substr(0, sParams.length-1);
+    }
+    else
+        sParams += mixedParams;
+
+    return sUrl + sParams;
+}
+
+function bx_search_on_type (e, n, sFormSel, sResultsContSel, sLoadingContSel, bSortResults) {
+    if ('undefined' != typeof(e) && 13 == e.keyCode) {
+        $(sFormSel).find('input[name=live_search]').val(0);
+        $(sFormSel).submit();
+        return false;
+    }
+
+    if ('undefined' != typeof(glBxSearchTimeoutHandler) && glBxSearchTimeoutHandler)
+        clearTimeout(glBxSearchTimeoutHandler);
+
+    glBxSearchTimeoutHandler = setTimeout(function () {
+        bx_search (n, sFormSel, sResultsContSel, sLoadingContSel, bSortResults);
+    }, 500);
+
+    return true;
+}
+
+function bx_search (n, sFormSel, sResultsContSel, sLoadingContSel, bSortResults) {
+    if ('undefined' == typeof(sLoadingContSel))
+        sLoadingContSel = sResultsContSel;
+    if ('undefined' == typeof(bSortResults))
+        bSortResults = false;
+
+    var sQuery = $('input', sFormSel).serialize();
+
+    bx_loading($(sLoadingContSel), true);
+    $.post(sUrlRoot + 'searchKeywordContent.php', sQuery, function(data) {
+        bx_loading($(sLoadingContSel), false);
+        if (bSortResults) {
+            var aSortedUnits = $(data).find(".bx-def-unit-live-search").toArray().sort(function (a, b) {
+                return b.getAttribute('data-ts') - a.getAttribute('data-ts');
+            });
+            data = '';
+            $.each(aSortedUnits.slice(0, n), function (i, e) {
+                data += e.outerHTML;
+            });
+        } 
+        $(sResultsContSel).html(data).bxTime();
+    });
+
+    return false;
+}
+
+function on_filter_apply(e, sInputId, sFilterName)
+{
+	var oRegExp = new RegExp("[&]{0,1}" + sFilterName + "=.*");
+    var s = ('' + document.location).replace(oRegExp, ''); // remove filter
+    s = s.replace(/page=\d+/, 'page=1'); // goto 1st page
+    if (e.checked && $('#' + sInputId).val().length > 2)
+        s += (-1 == s.indexOf('?') ? '?' : '&') + sFilterName + '=' + $('#' + sInputId).val(); // append filter
+    document.location = s;
+}
+
+function on_filter_key_up (e, sCheckboxId)
+{
+    if (13 == e.keyCode) {
+        $('#' + sCheckboxId).click();
+        return false;
+    }
+    else {
+        $('#' + sCheckboxId).removeAttr('checked');
+        return true;
+    }
+}
+
+function bx_activate_anim_icons()
+{
+    $('.sys-icon-a').not('.marka').each(function () {
+        var e = $(this);
+        var m = new Marka(e.get(0)), r = e.attr('data-rotate'), c = e.attr('data-color');
+        m.set(e.attr('data-icon')).rotate(r ? r : 'up').color(c ? c : '#3e8685');
+    });
+}

+ 437 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/js/jquery.dolPopup.js

@@ -0,0 +1,437 @@
+/**
+ * @package     Dolphin Core
+ * @copyright   Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * @license     CC-BY - http://creativecommons.org/licenses/by/3.0/
+ */
+
+(function($) {
+
+    $.fn.dolPopupDefaultOptions = {
+        closeOnOuterClick: true,
+        closeElement: '.bx-popup-element-close', // link to element which will close popup
+        position: 'centered', // | 'absolute' | 'fixed' | event | element,
+        fog: {color: '#fff', opacity: .7}, // {color, opacity},
+        pointer: false, // {el:(string_id|jquery_object), align: (left|right|center)},
+        left: 0, // only for fixed or absolute
+        top: 0, // only for fixed
+        onBeforeShow: function () {},
+        onShow: function () {},
+        onBeforeHide: function () {},
+        onHide: function () {},
+        speed: 150
+    }; 
+
+    $.fn.dolPopupDefaultPointerOptions = {
+        align: 'right',
+        offset: '0 0',
+        offset_pointer: '0 0'
+    }; 
+
+    $.fn.dolPopup = function(options) {
+        var options = options || {};
+        var o = $.extend({}, $.fn.dolPopupDefaultOptions, options);
+
+        if (false != o.pointer) {
+            o.fog = false;
+            o.pointer = $.extend({}, $.fn.dolPopupDefaultPointerOptions, $(document).data('bx-popup-options') ? $(document).data('bx-popup-options') : {}, o.pointer);
+        }
+
+        if (o.fog && !$('#bx-popup-fog').length) {
+            $('<div id="bx-popup-fog" class="bx-def-z-index-overlay" style="display: none;">&nbsp;</div>')
+                .prependTo('body');
+        }
+
+        $('#bx-popup-fog').css({
+            position: 'fixed',
+            top: 0,
+            left: 0,
+            width: $(window).width(),
+            height: $(window).height(),
+            opacity: o.fog.opacity,
+            backgroundColor: o.fog.color
+        });
+        
+        $(window).on('resize.popupFog', function () {
+            $('#bx-popup-fog').css({
+                width: $(window).width(),
+                height: $(window).height()
+            });
+        });
+
+        return this.each(function() {
+            var $el = $(this);
+
+            $el.addClass('bx-def-z-index-modal');
+
+            // element must have id
+            if (!$el.attr('id'))
+                return false;
+
+            if ($el.is(":visible")) { // if popup is shown - hide it
+                $el.dolPopupHide();
+                return true;
+            } 
+
+            // default style for correct positioning
+            $el.css({
+                display: 'block',
+                visibility: 'hidden',
+                position: 'absolute',
+                top: 0,
+                left: 0
+            });
+
+            setTimeout(function() { // timeout is needed for some browsers to render element
+                $el._dolPopupSetPosition(o);
+
+                if (!$el.hasClass('bx-popup-applied')) { // do this only once
+                    $el.addClass('bx-popup-applied');
+
+                    // save options for element
+                    $el.data('bx-popup-options', o);
+
+                    // attach event for "close element"
+                    if (o.closeElement) {
+                        $(o.closeElement, $el)
+                            .css('cursor', 'hand')
+                            .click(function() {
+                                $el.dolPopupHide();
+                            });
+                    }
+
+                    // attach event for outer click checking
+                    if (o.closeOnOuterClick) {
+                        var fCallback = function(e) {
+                            if ($el.hasClass('bx-popup-applied') && $el.is(':visible')) {
+                                if ($(e.target).parents('#' + $el.attr('id')).length == 0) {
+                                    $el.dolPopupHide();
+                                }
+                            }
+
+                            return true;
+                        }
+
+                        $(document).on({
+                            click: fCallback,
+                            touchend: fCallback
+                        });
+                    }
+                }
+
+                o.onBeforeShow();
+
+                if (o.speed > 0) {
+                    $el.css({display: 'none', visibility: 'visible'}).fadeIn(o.speed, o.onShow);
+                    if (o.fog)
+                        $('#bx-popup-fog').fadeIn(o.speed);
+                } else {
+                    $el.css({display: 'block', visibility: 'visible'});
+                    if (o.fog)
+                        $('#bx-popup-fog').show(o.onShow);
+                }
+
+            }, 10);
+        });
+    };
+
+    $.fn.dolPopupHide = function(options) { 
+
+        if ('undefined' == typeof(options) || 'object' != typeof(options))
+            options = {};
+
+        return this.each(function() {
+            var $el = $(this);
+
+            if (!$el.hasClass('bx-popup-applied'))
+                return false;
+
+            if (!$el.is(':visible') || 'hidden' == $el.css('visibility') || 'none' == $el.css('display'))
+                return false;
+
+            var o = $.extend({}, $el.data('bx-popup-options'), options);
+
+            if (!o)
+                return false;
+
+            $(window).off('resize.popupWindow');
+            $(window).off('resize.popupPointer');
+            $(window).off('resize.popupFog');
+
+            o.onBeforeHide();
+
+            if (o.speed > 0) {                
+                if (o.fog)
+                    $('#bx-popup-fog').fadeOut(o.speed);
+                $el.fadeOut(o.speed, o.onHide);
+            } else {                
+                if (o.fog)
+                    $('#bx-popup-fog').hide();
+                $el.hide(o.onHide);
+            }
+        });
+    };
+
+
+    $.fn.dolPopupAjax = function(options) { 
+        
+        if ('undefined' == typeof(options) || 'object' != typeof(options) || 'undefined' == typeof(options.url))
+            return;
+
+        if ('undefined' == typeof(options.container))
+            options.container = '.bx-popup-content-wrapped';
+
+        var bx_menu_on = function (e, b) {
+            var li = $(e).parents('li:first');   
+            if (!li.length)
+                return;
+            if (b) {
+                var ul = $(e).parents('ul:first');   
+                ul.find('li').removeClass('bx-menu-tab-active');
+                li.addClass('bx-menu-tab-active');
+            } else {
+                li.removeClass('bx-menu-tab-active');
+            }
+        }
+
+        var bx_menu_is_on = function (e) {    
+            var li = $(e).parents('li:first');   
+            if (!li.length)
+                return false;
+            return li.hasClass('bx-menu-tab-active');
+        }
+
+        return this.each(function() {
+            var e = $(this);
+
+            // get id
+            var sPopupId = '';
+            var sIdPrefix = 'bx-popup-ajax-wrapper-';
+            if(typeof(e.attr('bx-popup-id')) != 'undefined')
+            	sPopupId = sIdPrefix + e.attr('bx-popup-id');
+            else if(typeof(options.id) != 'undefined')
+            	switch(typeof(options.id)) {
+            		case 'string':
+            			sPopupId = sIdPrefix + options.id;
+            			break;
+
+            		case 'object':
+            			sPopupId = typeof(options.id.force) != 'undefined' && options.id.force ? options.id.value : sIdPrefix + options.id.value;
+            			break;
+            	}
+            else
+            	sPopupId = sIdPrefix + parseInt(2147483647 * Math.random());
+
+            var oPointerOptions = $.isWindow(e[0]) ? false : $.extend({}, {el:$(e), align:'center'}, options.pointer);
+            if ($('#' + sPopupId + ':visible').length) { // if popup exists and is shown - hide it
+                
+                $('#' + sPopupId).dolPopupHide();
+
+            } else if ($('#' + sPopupId).length) { // if popup exists but not shown - unhide it
+
+                if (!$.isWindow(e[0]))
+                    bx_menu_on(e, true);
+
+                $('#' + sPopupId).dolPopup($.extend({}, options, {
+                    pointer: oPointerOptions,
+                    onHide: function () {
+                        if (!$.isWindow(e[0]))
+                            bx_menu_on(e, false);
+                    }
+                }));
+
+            } else { // if popup doesn't exists - create new one from provided url
+
+                if (!$.isWindow(e[0]))
+                    bx_menu_on(e, true);
+
+                $('<div id="' + sPopupId + '" style="display:none;">' + $('#bx-popup-loading').html() + '</div>').appendTo('body').find(options.container).hide();
+
+                var oLoading = $('#' + sPopupId + ' .bx-popup-loading-wrapped');
+                bx_loading_content(oLoading, true, true);
+
+                $('#' + sPopupId).dolPopup($.extend({}, options, {
+                    pointer: oPointerOptions,
+                    onHide: function () {
+                        if (!$.isWindow(e[0]))
+                            bx_menu_on(e, false);
+                    }
+                }));
+
+                var fOnLoad = function() {
+                	bx_loading_content(oLoading, false);
+
+                	$('#' + sPopupId + ' ' + options.container).bxTime().show();
+
+					$('#' + sPopupId)._dolPopupSetPosition({
+						pointer: oPointerOptions
+					});
+                };
+
+                var sUrl = (options.url.indexOf('http://') == 0 || options.url.indexOf('https://') == 0 || options.url.indexOf('/') == 0 ? '' : sUrlRoot) + options.url;
+
+                $('#' + sPopupId).find(options.container).load(sUrl, function () {
+                	if($('#' + sPopupId).find('img').length > 0)
+                		$('#' + sPopupId).find('img').load(fOnLoad);
+                	else
+                		fOnLoad();
+                });
+            }
+        });
+    };
+
+
+    $.fn.dolPopupImage = function(sUrl, sLoadingElement) {
+
+        return this.each(function() {
+
+            var ePopup = $(this);
+            var k = .95,
+                w = $(window).width(),
+                h = $(window).height(),
+                eImg = new Image();
+
+            if (!ePopup.find('img').size())
+                return;
+
+            ePopup.filter('.bx-popup-wrapper').addClass('bx-popup-image-wrapper');
+
+            eImg.src = sUrl;
+
+            if ('undefined' != typeof(sLoadingElement))
+                bx_loading (sLoadingElement, true);
+
+            $(eImg).on('load', function () {
+
+                if ('undefined' != typeof(sLoadingElement))
+                    bx_loading (sLoadingElement, false);
+
+                ePopup.find('img').attr('src', sUrl);
+
+                if ('undefined' != typeof(window.matchMedia) && window.matchMedia("(max-width:720px)").matches)
+                    k = 1.0;
+
+                // fit image into the browser window
+                if (eImg.width < w * k && eImg.height < h * k) {
+                    ePopup.find('img').css({
+                        width: 'auto',
+                        height: 'auto'
+                    });
+                }
+                else if ((w * k - eImg.width) < (h * k - eImg.height)) {
+                    ePopup.find('img').css({
+                        width: '' + (eImg.width > w * k ? parseInt(w * k) : eImg.width) + 'px',
+                        height: 'auto'
+                    }).addClass('bx-constrain-width');
+                } else {
+                    ePopup.find('img').css({
+                        width: 'auto',
+                        height: '' + (eImg.height > h * k ? parseInt(h * k) : eImg.height) + 'px'
+                    }).addClass('bx-constrain-height');
+                }
+
+                // show popup
+                ePopup.dolPopup();
+
+                // hide popup upon clicking/tapping on the image
+                var fCallback = function (e) {
+                    e.stopPropagation();
+                    ePopup.dolPopupHide();
+                };
+                ePopup.find('img').on({
+                    click: fCallback,
+                    touchend: fCallback
+                });
+
+            });
+        });
+    };
+
+    $.fn._dolPopupSetPosition = function(options) {
+
+        var o = $.extend({}, $.fn.dolPopupDefaultOptions, options);
+
+        if (undefined != o.pointer && false != o.pointer)
+            o.pointer = $.extend({}, $.fn.dolPopupDefaultPointerOptions, $(document).data('bx-popup-options') ? $(document).data('bx-popup-options') : {}, o.pointer);
+
+        return this.each(function() {
+            var $el = $(this);
+            
+            if (o.pointer != false) {                
+                
+                var yOffset = 3;
+                var ePointAt = 'string' == o.pointer.el ? $(o.pointer.el) : o.pointer.el;
+                if (!ePointAt)
+                    ePointAt = $('body');
+
+                var aOffset = ('' + o.pointer.offset).split(' ', 2);
+                var aOffsetPointer = ('' + o.pointer.offset_pointer).split(' ', 2);
+                if (undefined == aOffset[0] || undefined == aOffset[1])
+                    aOffset = [0, 0];
+                if (undefined == aOffsetPointer[0] || undefined == aOffsetPointer[1])
+                    aOffsetPointer = [0, 0];
+
+                $el.position({
+                    of: ePointAt,
+                    my: o.pointer.align + '+' + parseInt(aOffset[0]) + ' top+' + yOffset + '+' + parseInt(aOffset[1] - 0),
+                    at: o.pointer.align + ' bottom',
+                    collision: 'flip none'
+                });
+
+                $el.find('.bx-popup-box-pointer').css('display', 'block').position({
+                    of: ePointAt,
+                    my: 'center+' + (parseInt(aOffsetPointer[0]) + parseInt(aOffset[0])) + ' top+' + yOffset + '+' + (parseInt(aOffsetPointer[1]) + parseInt(aOffset[1]) + 0),
+                    at: 'center bottom'
+                });
+
+                $(window).on('resize.popupPointer', function() {
+                    $el.position({
+                        of: ePointAt,
+                        my: o.pointer.align + '+' + parseInt(aOffset[0]) + ' top+' + yOffset + '+' + parseInt(aOffset[1]),
+                        at: o.pointer.align + ' bottom',
+                        collision: 'flip none'
+                    });
+                });
+
+            } else if (o.position == 'fixed' || o.position == 'absolute') {
+
+                $el.css({
+                    position: o.position,
+                    left: o.left,
+                    top: o.top
+                });
+
+            } else if (o.position == 'centered') {
+
+                $el.position({
+                    of: window,
+                    my: 'center center+' + ($el.outerHeight() > $(window).height() ? parseInt(($el.outerHeight() - $(window).height()) / 2) : '0'),
+                    at: 'center center',
+                    collision: 'none none'
+                });
+
+                // attach window resize event
+                $(window).on('resize.popupWindow', function() {
+
+                    $el.position({
+                        of: window,
+                        my: 'center center+' + ($el.outerHeight() > $(window).height() ? parseInt(($el.outerHeight() - $(window).height()) / 2) : '0'),
+                        at: 'center center',
+                        collision: 'none none'
+                    });
+                });
+
+            } else if (typeof o.position == 'object') {
+
+                $el.position({
+                    of: o.position,
+                    my: 'center top',
+                    at: 'center bottom',
+                    collision: 'flip flip'
+                });
+
+            }
+        });
+    };
+
+})(jQuery);
+

+ 1201 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/utils.inc.php

@@ -0,0 +1,1201 @@
+<?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+define('BX_DATA_TEXT', 1); ///< regular text data type
+define('BX_DATA_TEXT_MULTILINE', 2); ///< regular multiline text data type
+define('BX_DATA_INT', 3); ///< integer data type
+define('BX_DATA_FLOAT', 4); ///< float data type
+define('BX_DATA_CHECKBOX', 5); ///< checkbox data type, 'on' or empty value
+define('BX_DATA_HTML', 6); ///< HTML data type
+define('BX_DATA_DATE', 7); ///< date data type stored as yyyy-mm-dd
+define('BX_DATA_DATE_TS', 8); ///< date data type stored as unixtimestamp
+define('BX_DATA_DATETIME_TS', 9); ///< date/time data type stored as unixtimestamp
+
+define('BX_SLASHES_AUTO', 0);
+define('BX_SLASHES_ADD', 1);
+define('BX_SLASHES_STRIP', 2);
+define('BX_SLASHES_NO_ACTION', 3);
+
+define('BX_ESCAPE_STR_AUTO', 0); ///< turn apostropes and quote signs into html special chars, for use in @see bx_js_string and @see bx_html_attribute
+define('BX_ESCAPE_STR_APOS', 1); ///< escape apostrophes only, for js strings enclosed in apostrophes, for use in @see bx_js_string and @see bx_html_attribute
+define('BX_ESCAPE_STR_QUOTE', 2); ///< escape quotes only, for js strings enclosed in quotes, for use in @see bx_js_string and @see bx_html_attribute
+
+define('BX_EMAIL_SYSTEM', 0); ///< system email without unsubscribe link, like forgot password or email verification
+define('BX_EMAIL_NOTIFY', 1); ///< notification message, with unsubscribe link
+define('BX_EMAIL_MASS', 2); ///< mass email, one mesage send to manu users, with unsubscribe link
+
+define('BX_MAINTENANCE_FILE', '.bx_maintenance'); ///< file name to use as mantenance mode indicator
+
+/**
+ * The following two functions are needed to convert title to uri and back.
+ * It usefull when titles are used in URLs, like in Categories and Tags.
+ */
+function title2uri($sValue)
+{
+    return str_replace(
+        array('&', '/', '\\', '"', '+'),
+        array('[and]', '[slash]', '[backslash]', '[quote]', '[plus]'),
+        $sValue
+    );
+}
+function uri2title($sValue)
+{
+    return str_replace(
+        array('[and]', '[slash]', '[backslash]', '[quote]', '[plus]'),
+        array('&', '/', '\\', '"', '+'),
+        $sValue
+    );
+}
+
+/*
+ * functions for limiting maximal text length
+ */
+function strmaxtextlen($sInput, $iMaxLen = 60, $sEllipsisSign = '&#8230;')
+{
+    $sTail = '';
+    $s = trim(strip_tags($sInput));
+    if (mb_strlen($s) > $iMaxLen) {
+        $s = mb_substr($s, 0, $iMaxLen);
+        $sTail = $sEllipsisSign;
+    }
+    return htmlspecialchars_adv($s) . $sTail;
+}
+
+function html2txt($content, $tags = "")
+{
+    while($content != strip_tags($content, $tags)) {
+        $content = strip_tags($content, $tags);
+    }
+
+    return $content;
+}
+
+function html_encode($text)
+{
+     $searcharray =  array(
+    "'([-_\w\d.]+@[-_\w\d.]+)'",
+    "'((?:(?!://).{3}|^.{0,2}))(www\.[-\d\w\.\/]+)'",
+    "'(http[s]?:\/\/[-_~\w\d\.\/]+)'");
+
+    $replacearray = array(
+    "<a href=\"mailto:\\1\">\\1</a>",
+    "\\1http://\\2",
+    "<a href=\"\\1\" target=_blank>\\1</a>");
+
+   return preg_replace($searcharray, $replacearray, stripslashes($text));
+}
+
+/**
+ * Functions to process user input.
+ * DON'T use to process data before passing to SQL query - use db prepare instead @see BxDolDb::prepare.
+ * It is ok to use bx_process_input and then db prepare.
+ * @param $mixedData data to process
+ * @param $iDataType how to handle data, possible valies:
+ *          BX_DATA_INT - integer value
+ *          BX_DATA_FLOAT - float values
+ *          BX_DATA_CHECKBOX - 'on' or empty string
+ *          BX_DATA_TEXT - text data, single line (default)
+ *          BX_DATA_TEXT_MULTILINE - text data, multiple lines
+ *          BX_DATA_HTML - HTML data
+ *          BX_DATA_DATE - date data type stored as yyyy-mm-dd
+ *          BX_DATA_DATE_TS' -  date data type stored as unixtimestamp
+ *          BX_DATA_DATETIME_TS - date/time data type stored as unixtimestamp
+ * @param $mixedParams optional parameters to pass for validation
+ * @return the filtered data, or FALSE if the filter fails.
+ */
+function bx_process_input ($mixedData, $iDataType = BX_DATA_TEXT, $mixedParams = false, $isCheckMagicQuotes = true)
+{
+    if (is_array($mixedData)) {
+        foreach ($mixedData as $k => $v)
+            $mixedData[$k] = bx_process_input($v, $iDataType, $mixedParams);
+        return $mixedData;
+    }
+
+    if (get_magic_quotes_gpc() && $isCheckMagicQuotes)
+        $mixedData = stripslashes($mixedData);
+
+    switch ($iDataType) {
+    case BX_DATA_INT:
+        return filter_var(trim($mixedData), FILTER_VALIDATE_INT);
+    case BX_DATA_FLOAT:
+        return filter_var(trim($mixedData), FILTER_VALIDATE_FLOAT);
+    case BX_DATA_CHECKBOX:
+        return 'on' == trim($mixedData) ? 'on' : '';
+
+    case BX_DATA_DATE:
+        // maybe consider using strtotime
+        $mixedData = trim($mixedData);
+        if (!preg_match('/^\d{4}-\d{1,2}-\d{1,2}$/', $mixedData))
+            return false;
+        list($iYear, $iMonth, $iDay) = explode('-', $mixedData); // 1985-10-28
+        $iDay   = intval($iDay);
+        $iMonth = intval($iMonth);
+        $iYear  = intval($iYear);
+        return sprintf("%04d-%02d-%02d", $iYear, $iMonth, $iDay);
+    case BX_DATA_DATE_TS:
+        $mixedData = trim($mixedData);
+        if (!preg_match('/^\d{4}-\d{1,2}-\d{1,2}$/', $mixedData))
+            return false;
+        list($iYear, $iMonth, $iDay) = explode('-', $mixedData);
+        $iDay   = intval($iDay);
+        $iMonth = intval($iMonth);
+        $iYear  = intval($iYear);
+        $iRet = mktime (0, 0, 0, $iMonth, $iDay, $iYear);
+        return $iRet > 0 ? $iRet : false;
+    case BX_DATA_DATETIME_TS:
+        if (!preg_match('#(\d+)\-(\d+)\-(\d+)[\sT]{1}(\d+):(\d+):(\d+)#', $mixedData, $m) && !preg_match('#(\d+)\-(\d+)\-(\d+)[\sT]{1}(\d+):(\d+)#', $mixedData, $m))
+            return bx_process_input ($mixedData, BX_DATA_DATE_TS, $mixedParams, $isCheckMagicQuotes);
+        $iDay   = $m[3];
+        $iMonth = $m[2];
+        $iYear  = $m[1];
+        $iH = $m[4];
+        $iM = $m[5];
+        $iS = isset($m[6]) ? $m[6] : 0;
+        $iRet = mktime ($iH, $iM, $iS, $iMonth, $iDay, $iYear);
+        return $iRet > 0 ? $iRet : false;
+
+    case BX_DATA_HTML:
+        return clear_xss($mixedData);
+    case BX_DATA_TEXT_MULTILINE:
+        return nl2br(htmlspecialchars_adv($mixedData));
+    case BX_DATA_TEXT:
+    default:
+        return $mixedData;
+    }
+}
+
+/*
+ * Functions to process user output.
+ * Always use this function before output data which was entered by user before.
+ * @param $mixedData string to process
+ * @param $iDataType how to handle data, possible valies the same as in bx_process_input function, see bx_process_input.
+ * @param $mixedParams optional parameters to pass for validation
+ * @return the filtered data, or FALSE if the filter fails.
+ */
+function bx_process_output ($mixedData, $iDataType = BX_DATA_TEXT, $mixedParams = false)
+{
+    if (is_array($mixedData)) {
+        foreach ($mixedData as $k => $v)
+            $mixedData[$k] = bx_process_output($v, $iDataType, $mixedParams);
+        return $mixedData;
+    }
+
+    switch ($iDataType) {
+    case BX_DATA_INT:
+        return filter_var($mixedData, FILTER_VALIDATE_INT);
+    case BX_DATA_FLOAT:
+        return filter_var($mixedData, FILTER_VALIDATE_FLOAT);
+    case BX_DATA_CHECKBOX:
+        return 'on' == trim($mixedData) ? 'on' : '';
+
+    case BX_DATA_DATE:
+        return $mixedData;
+    case BX_DATA_DATE_TS:
+        return empty($mixedData) ? '' : date("Y-m-d", $mixedData);
+    case BX_DATA_DATETIME_TS:
+        return empty($mixedData) ? '' : date("Y-m-d H:i", $mixedData);
+
+    case BX_DATA_HTML:
+        return $mixedData;
+    case BX_DATA_TEXT_MULTILINE:
+        return $mixedData;
+    case BX_DATA_TEXT:
+    default:
+        return htmlspecialchars_adv($mixedData);
+    }
+}
+
+/*
+ * This function apply bx_process_input and then bx_process_output.
+ * Use this function to output data immediately after receiving, without saving to database.
+ * Patams are the same as bx_process_input function - @see bx_process_input
+ */
+function bx_process_pass ($mixedData, $iDataType = BX_DATA_TEXT, $mixedParams = false, $isCheckMagicQuotes = true)
+{
+    return bx_process_output(bx_process_input ($mixedData, $iDataType, $mixedParams, $isCheckMagicQuotes), $iDataType, $mixedParams);
+}
+
+/**
+ * DEPRECATED
+ * use bx_process_input + bx_process_output instead
+ * --------
+ * function for processing pass data
+ *
+ * This function cleans the GET/POST/COOKIE data if magic_quotes_gpc() is on
+ * for data which should be outputed immediately after submit
+ */
+/*
+function process_pass_data( $text, $strip_tags = 0 )
+{
+    if ( $strip_tags )
+        $text = strip_tags($text);
+
+    if ( !get_magic_quotes_gpc() )
+        return $text;
+    else
+        return stripslashes($text);
+}
+*/
+
+/*
+ * function for output data from database into html
+ */
+function htmlspecialchars_adv( $string )
+{
+    return htmlspecialchars($string, ENT_COMPAT, 'UTF-8');
+}
+
+/**
+ * Send mail to user by parsing email template
+ */
+function sendMailTemplate($sTemplateName, $iAccountId = 0, $iProfileId = 0, $aReplaceVars = array(), $iEmailType = BX_EMAIL_NOTIFY)
+{
+    bx_import('BxDolLanguages');
+
+    bx_import('BxDolAccount');
+    $oAccount = BxDolAccount::getInstance($iAccountId);
+
+    bx_import('BxDolProfile');
+    $oProfile = BxDolProfile::getInstance($iProfileId);
+
+    bx_import('BxDolEmailTemplates');
+    $oEmailTemplates = BxDolEmailTemplates::getInstance();
+
+    if (!$oAccount || !$oProfile || !$oEmailTemplates)
+        return false;
+
+    $aTemplate = $oEmailTemplates->parseTemplate($sTemplateName, $aReplaceVars, $iAccountId, (int)$iProfileId);
+    if (!$aTemplate)
+        return false;
+
+    return sendMail($oAccount->getEmail(), $aTemplate['Subject'], $aTemplate['Body'], 0, array(), $iEmailType);
+}
+
+/**
+ * Send system email 
+ */
+function sendMailTemplateSystem($sTemplateName, $aReplaceVars = array(), $iEmailType = BX_EMAIL_SYSTEM)
+{
+    bx_import('BxDolLanguages');
+
+    bx_import('BxDolEmailTemplates');
+    $oEmailTemplates = BxDolEmailTemplates::getInstance();
+
+    if (!$oEmailTemplates)
+        return false;
+
+    $aTemplate = $oEmailTemplates->parseTemplate($sTemplateName, $aReplaceVars);
+    if (!$aTemplate)
+        return false;
+
+    return sendMail(getParam('site_email'), $aTemplate['Subject'], $aTemplate['Body'], 0, array(), $iEmailType);
+}
+/**
+ * Send email function
+ *
+ * @param $sRecipientEmail - Email where email should be send
+ * @param $sMailSubject - subject of the message
+ * @param $sMailBody - Body of the message
+ * @param $iRecipientID - ID of recipient profile
+ * @param $aPlus - Array of additional information
+ * @param $iEmailType - email message type: BX_EMAIL_SYSTEM, BX_EMAIL_NOTIFY or BX_EMAIL_MASS
+ * @return true if message was send or false otherwise
+ */
+function sendMail($sRecipientEmail, $sMailSubject, $sMailBody, $iRecipientID = 0, $aPlus = array(), $iEmailType = BX_EMAIL_NOTIFY, $sEmailFlag = 'html', $isDisableAlert = false)
+{
+    // make sure that recipient's email is valid and message isn't empty
+    if (!$sMailBody || !$sRecipientEmail || preg_match('/\(2\)$/', $sRecipientEmail))
+        return false;
+
+    // get recipient account
+    bx_import('BxDolAccount');
+    $oAccount = BxDolAccount::getInstance($sRecipientEmail);
+    $aAccountInfo = $oAccount ? $oAccount->getInfo() : false;
+
+    // don't send bulk emails if user didn't subscribed to site news or email is unconfirmed
+    if ($aAccountInfo && BX_EMAIL_MASS == $iEmailType && (!$aAccountInfo['email_confirmed'] || !$aAccountInfo['receive_news']))
+        return false;
+
+    // don't send email notifications if user didn't subscribed to notifications or email is unconfirmed
+    if ($aAccountInfo && BX_EMAIL_NOTIFY == $iEmailType && (!$aAccountInfo['email_confirmed'] || !$aAccountInfo['receive_updates']))
+        return false;
+
+    // if profile id is provided - get profile's info
+    $aRecipientInfo = false;
+    if ($iRecipientID) {
+        bx_import('BxDolProfile');
+        $oProfile = BxDolProfile::getInstance($iRecipientID);
+        if ($oProfile)
+            $aRecipientInfo = $oProfile->getInfo();
+    }
+
+    // get site vars
+    $sEmailNotify       = getParam('site_email_notify');
+    $sSiteTitle         = getParam('site_title');
+
+    // add unsubscribe link
+    if (empty($aPlus['unsubscribe'])) {
+        $aPlus['unsubscribe'] = '';
+        if ($oAccount && (BX_EMAIL_MASS == $iEmailType || BX_EMAIL_NOTIFY == $iEmailType))
+            $aPlus['unsubscribe'] = ($sLink = $oAccount->getUnsubscribeLink($iEmailType)) ? '<a href="' . BX_DOL_URL_ROOT . $sLink . '">' . _t('_sys_et_txt_unsubscribe') . '</a>' : '';
+    }
+
+    // parse template
+    if ($aPlus || $iRecipientID) {
+        if(!is_array($aPlus))
+            $aPlus = array();
+        bx_import('BxDolEmailTemplates');
+        $oEmailTemplates = BxDolEmailTemplates::getInstance();
+        $sMailSubject = $oEmailTemplates->parseContent($sMailSubject, $aPlus, $iRecipientID);
+        $sMailBody = $oEmailTemplates->parseContent($sMailBody, $aPlus, $iRecipientID);
+    }
+
+    // email message headers
+    $sMailHeader = "From: =?UTF-8?B?" . base64_encode( $sSiteTitle ) . "?= <{$sEmailNotify}>";
+    $sMailParameters = "-f{$sEmailNotify}";
+    $sMailSubject = '=?UTF-8?B?' . base64_encode( $sMailSubject ) . '?=';
+    $sMailHeader = "MIME-Version: 1.0\r\n" . $sMailHeader;
+
+    // build data for alert handler
+    $bResult = null;
+    $aAlert = array(
+        'email' => $sRecipientEmail,
+        'subject' => $sMailSubject,
+        'body' => $sMailBody,
+        'header' => $sMailHeader,
+        'params' => $sMailParameters,
+        'recipient' => $aRecipientInfo,
+        'html' => 'html' == $sEmailFlag ? true : false,
+        'override_result' => &$bResult,
+    );
+
+    // system alert
+    if (!$isDisableAlert) {
+        bx_alert('system', 'before_send_mail', (isset($aRecipientInfo['ID']) ? $aRecipientInfo['ID'] : 0), '', $aAlert);
+        if ($bResult !== null)
+            return $bResult;
+        unset($aAlert['override_result']);
+    }
+
+    // send mail
+    if( 'html' == $sEmailFlag) {
+        $sMailHeader = "Content-type: text/html; charset=UTF-8\r\n" . $sMailHeader;
+        $iSendingResult = mail( $sRecipientEmail, $sMailSubject, $sMailBody, $sMailHeader, $sMailParameters );
+    } else {
+        $sMailHeader = "Content-type: text/plain; charset=UTF-8\r\n" . $sMailHeader;
+        $sMailBody = html2txt($sMailBody);
+        $iSendingResult = mail( $sRecipientEmail, $sMailSubject, html2txt($sMailBody), $sMailHeader, $sMailParameters );
+    }
+
+    // system alert
+    if (!$isDisableAlert)
+        bx_alert('system', 'send_mail', (isset($aRecipientInfo['ID']) ? $aRecipientInfo['ID'] : 0), '', $aAlert);
+
+    return $iSendingResult;
+}
+
+/*
+ * Getting an array with Templates' Names
+ */
+function get_templates_array($bEnabledOnly = true, $bShortInfo = true)
+{
+    bx_import('BxDolDb');
+    $oDb = BxDolDb::getInstance();
+
+    $sWhereAddon = $bEnabledOnly ? " AND `enabled`='1'" : "";
+
+    if($bShortInfo)
+        return $oDb->getPairs("SELECT `uri`, `title` FROM `sys_modules` WHERE 1 AND `type`='" . BX_DOL_MODULE_TYPE_TEMPLATE . "'" . $sWhereAddon, "uri", "title");
+    else
+        return $oDb->getAllWithKey("SELECT * FROM `sys_modules` WHERE 1 AND `type`='" . BX_DOL_MODULE_TYPE_TEMPLATE . "'" . $sWhereAddon, "uri");
+}
+
+function extFileExists( $sFileSrc )
+{
+    return (file_exists( $sFileSrc ) && is_file( $sFileSrc )) ? true : false;
+}
+
+function getVisitorIP()
+{
+    $ip = "0.0.0.0";
+    if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+        $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
+    } elseif (isset( $_SERVER['HTTP_CLIENT_IP']) && !empty($_SERVER['HTTP_CLIENT_IP'])) {
+        $ip = explode(".",$_SERVER['HTTP_CLIENT_IP']);
+        $ip = $ip[3].".".$ip[2].".".$ip[1].".".$ip[0];
+    } elseif (!isset( $_SERVER['HTTP_X_FORWARDED_FOR']) || empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+        if (!isset( $_SERVER['HTTP_CLIENT_IP']) && empty($_SERVER['HTTP_CLIENT_IP']) && isset($_SERVER['REMOTE_ADDR']))
+            $ip = $_SERVER['REMOTE_ADDR'];
+    }
+    return $ip;
+}
+
+function genFlag( $country )
+{
+    return '<img src="' . genFlagUrl($country) . '" />';
+}
+
+function genFlagUrl($country)
+{
+    bx_import('BxDolTemplate');
+    return BxDolTemplate::getInstance()->getIconUrl('sys_fl_' . strtolower($country) . '.gif');
+}
+
+// print debug information ( e.g. arrays )
+function echoDbg( $what, $desc = '' )
+{
+    if ( $desc )
+        echo "<b>$desc:</b> ";
+    echo "<pre>";
+        print_r( $what );
+    echo "</pre>\n";
+}
+
+function echoDbgLog($mWhat, $sDesc = '', $sFileName = 'debug.log')
+{
+    $sCont =
+        '--- ' . date('r') . ' (' . BX_DOL_START_TIME . ") ---\n" .
+        $sDesc . "\n" .
+        print_r($mWhat, true) . "\n\n\n";
+
+    $rFile = fopen(BxDolConfig::getInstance()->get('path_dynamic', 'tmp') . $sFileName, 'a');
+    fwrite($rFile, $sCont);
+    fclose($rFile);
+}
+
+function clear_xss($val)
+{
+    if ($GLOBALS['logged']['admin'])
+        return $val;
+
+    // HTML Purifier plugin
+    global $oHtmlPurifier;
+    require_once( BX_DIRECTORY_PATH_PLUGINS . 'htmlpurifier/HTMLPurifier.standalone.php' );
+    if (!isset($oHtmlPurifier)) {
+
+        HTMLPurifier_Bootstrap::registerAutoload();
+
+        $oConfig = HTMLPurifier_Config::createDefault();
+
+        $oConfig->set('Cache.SerializerPath', rtrim(BX_DIRECTORY_PATH_CACHE, '/'));
+        $oConfig->set('Cache.SerializerPermissions', BX_DOL_DIR_RIGHTS);
+
+        $oConfig->set('HTML.SafeObject', 'true');
+        $oConfig->set('Output.FlashCompat', 'true');
+        $oConfig->set('HTML.FlashAllowFullScreen', 'true');
+
+        if (getParam('sys_add_nofollow')) {
+            $sHost = parse_url(BX_DOL_URL_ROOT, PHP_URL_HOST);
+            $oConfig->set('URI.Host', $sHost);
+            $oConfig->set('HTML.Nofollow', 'true');
+        }
+
+        $oConfig->set('Filter.Custom', array (new HTMLPurifier_Filter_YouTube(), new HTMLPurifier_Filter_YoutubeIframe()));
+
+        $oDef = $oConfig->getHTMLDefinition(true);
+        $oDef->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');
+
+        $oHtmlPurifier = new HTMLPurifier($oConfig);
+    }
+
+    return $oHtmlPurifier->purify($val);
+}
+
+//--------------------------------------- friendly permalinks --------------------------------------//
+//------------------------------------------- main functions ---------------------------------------//
+function uriGenerate ($s, $sTable, $sField, $sEmpty = '-')
+{
+    $s = uriFilter($s, $sEmpty);
+    if(uriCheckUniq($s, $sTable, $sField))
+        return $s;
+
+    // cut off redundant part
+    if(get_mb_len($s) > 240)
+        $s = get_mb_substr($s, 0, 240);
+
+    // try to add date
+    $s .= '-' . date('Y-m-d');
+    if(uriCheckUniq($s, $sTable, $sField))
+        return $s;
+
+    // try to add number
+    for($i = 0 ; $i < 999 ; ++$i)
+        if(uriCheckUniq($s . '-' . $i, $sTable, $sField))
+            return ($s . '-' . $i);
+
+    return rand(0, 999999999);
+}
+
+function uriFilter ($s, $sEmpty = '-')
+{
+    bx_import('BxTemplConfig');
+    if (BxTemplConfig::getInstance()->bAllowUnicodeInPreg)
+        $s = get_mb_replace ('/[^\pL^\pN]+/u', '-', $s); // unicode characters
+    else
+        $s = get_mb_replace ('/([^\d^\w]+)/u', '-', $s); // latin characters only
+
+    $s = get_mb_replace ('/([-^]+)/', '-', $s);
+    $s = get_mb_replace ('/([-]+)$/', '', $s); // remove trailing dash
+    if (!$s) $s = $sEmpty;
+    return $s;
+}
+
+function uriCheckUniq ($s, $sTable, $sField)
+{
+    bx_import('BxDolDb');
+    $oDb = BxDolDb::getInstance();
+
+    $sSql = $oDb->prepare("SELECT 1 FROM `$sTable` WHERE `$sField`=? LIMIT 1", $s);
+    return !$oDb->query($sSql);
+}
+
+function get_mb_replace ($sPattern, $sReplace, $s)
+{
+    return preg_replace ($sPattern, $sReplace, $s);
+}
+
+function get_mb_len ($s)
+{
+    return (function_exists('mb_strlen')) ? mb_strlen($s) : strlen($s);
+}
+
+function get_mb_substr ($s, $iStart, $iLen)
+{
+    return (function_exists('mb_substr')) ? mb_substr ($s, $iStart, $iLen) : substr ($s, $iStart, $iLen);
+}
+
+function bx_mb_substr_replace($s, $sReplace, $iPosStart, $iLength)
+{
+    return mb_substr($s, 0, $iPosStart) . $sReplace . mb_substr($s, $iPosStart + $iLength);
+}
+
+function bx_mb_strpos ($s, $sReplacement, $iStart = 0)
+{
+    return mb_strpos($s, $sReplacement, $iStart);
+}
+
+/**
+ * Import class file, it automatically detects class path by its prefix or module array/name
+ *
+ * @param $sClassName - full class name or class postfix(withoit prefix) in the case of module class
+ * @param $mixedModule - module array or module name in the case of module class
+ */
+function bx_import($sClassName, $mixedModule = array())
+{
+    if (class_exists($sClassName))
+        return;
+
+    $aModule = false;
+    if ($mixedModule) {
+        if (is_array($mixedModule)) {
+            $aModule = $mixedModule;
+        } elseif (is_string($mixedModule)) {
+            bx_import('BxDolModule');
+            $o = BxDolModule::getInstance($mixedModule);
+            $aModule = $o->_aModule;
+        } elseif (is_bool($mixedModule) && true === $mixedModule) {
+            $aModule = $GLOBALS['aModule'];
+        }
+    }
+
+    if ($aModule) {
+        if (class_exists($aModule['class_prefix'] . $sClassName))
+            return;
+        require_once (BX_DIRECTORY_PATH_MODULES . $aModule['path'] . 'classes/' . $aModule['class_prefix'] . $sClassName . '.php');
+        return;
+    }
+
+    if (0 == strncmp($sClassName, 'BxDol', 5)) {
+        if (0 == strncmp($sClassName, 'BxDolStudio', 11))
+            require_once(BX_DOL_DIR_STUDIO_CLASSES . $sClassName . '.php');
+        else
+            require_once(BX_DIRECTORY_PATH_CLASSES . $sClassName . '.php');
+        return;
+    }
+
+    if (0 == strncmp($sClassName, 'BxBase', 6)) {
+        if (0 == strncmp($sClassName, 'BxBaseMod', 9)) {
+            $aMatches = array();
+            if (preg_match('/BxBaseMod([A-Z][a-z]+)/', $sClassName, $aMatches)) {
+                require_once(BX_DIRECTORY_PATH_MODULES . 'base/' . strtolower($aMatches[1]) . '/classes/' . $sClassName . '.php');
+                return;
+            }
+        } if (0 == strncmp($sClassName, 'BxBaseStudio', 12)) {
+            require_once(BX_DOL_DIR_STUDIO_BASE . 'scripts/' . $sClassName . '.php');
+            return;
+        } else {
+            require_once(BX_DIRECTORY_PATH_BASE . 'scripts/' . $sClassName . '.php');
+            return;
+        }
+    }
+
+    if (0 == strncmp($sClassName, 'BxTempl', 7) && !class_exists($sClassName)) {
+        if(0 == strncmp($sClassName, 'BxTemplStudio', 13)) {
+            bx_import('BxDolStudioTemplate');
+            $sPath = BX_DIRECTORY_PATH_MODULES . BxDolStudioTemplate::getInstance()->getPath() . 'data/template/studio/scripts/' . $sClassName . '.php';
+        } else {
+            bx_import('BxDolTemplate');
+            $sPath = BX_DIRECTORY_PATH_MODULES . BxDolTemplate::getInstance()->getPath() . 'data/template/system/scripts/' . $sClassName . '.php';
+        }
+
+        if (file_exists($sPath)) {
+            require_once($sPath);
+            return;
+        }
+
+        trigger_error ("bx_import fatal error: class (" . $sClassName . ") wasn't found", E_USER_ERROR);
+    }
+}
+
+/**
+ * Gets an instance of class pathing necessary parameters if it's necessary.
+ *
+ * @param string $sClassName class name.
+ * @param array $aParams an array of parameters to be pathed to the constructor of the class.
+ * @param array $aModule an array with module description. Is used when the requested class is located in some module.
+ * @return unknown
+ */
+function bx_instance($sClassName, $aParams = array(), $aModule = array())
+{
+    if(isset($GLOBALS['bxDolClasses'][$sClassName]))
+        return $GLOBALS['bxDolClasses'][$sClassName];
+
+    bx_import((empty($aModule) ? $sClassName : str_replace($aModule['class_prefix'], '', $sClassName)), $aModule);
+
+    $oClass = new ReflectionClass($sClassName);
+
+    $GLOBALS['bxDolClasses'][$sClassName] = empty($aParams) ? $oClass->newInstance() : $oClass->newInstanceArgs($aParams);
+
+    return $GLOBALS['bxDolClasses'][$sClassName];
+
+}
+
+
+/**
+ * Escapes string/array ready to pass to js script with filtered symbols like ', " etc
+ *
+ * @param $mixedInput - string/array which should be filtered
+ * @param $iQuoteType - string escaping method: BX_ESCAPE_STR_AUTO(default), BX_ESCAPE_STR_APOS or BX_ESCAPE_STR_QUOTE
+ * @return converted string / array
+ */
+function bx_js_string ($mixedInput, $iQuoteType = BX_ESCAPE_STR_AUTO)
+{
+    $aUnits = array(
+        "\n" => "\\n",
+        "\r" => "",
+    );
+    if (BX_ESCAPE_STR_APOS == $iQuoteType) {
+        $aUnits["'"] = "\\'";
+        $aUnits['<script'] = "<scr' + 'ipt";
+        $aUnits['</script>'] = "</scr' + 'ipt>";
+    } elseif (BX_ESCAPE_STR_QUOTE == $iQuoteType) {
+        $aUnits['"'] = '\\"';
+        $aUnits['<script'] = '<scr" + "ipt';
+        $aUnits['</script>'] = '</scr" + "ipt>';
+    } else {
+        $aUnits['"'] = '&quote;';
+        $aUnits["'"] = '&apos;';
+        $aUnits["<"] = '&lt;';
+        $aUnits[">"] = '&gt;';
+    }
+    return str_replace(array_keys($aUnits), array_values($aUnits), $mixedInput);
+}
+
+/**
+ * Return input string/array ready to pass to html attribute with filtered symbols like ', " etc
+ *
+ * @param mixed $mixedInput - string/array which should be filtered
+ * @return converted string / array
+ */
+function bx_html_attribute ($mixedInput, $iQuoteType = BX_ESCAPE_STR_AUTO)
+{
+    $aUnits = array ();
+    if (BX_ESCAPE_STR_APOS == $iQuoteType)
+        $aUnits["'"] = "\\'";
+    elseif (BX_ESCAPE_STR_QUOTE == $iQuoteType)
+        $aUnits['"'] = '&quot;';
+    else
+        $aUnits = array("\"" => "&quot;", "'" => "&apos;");
+
+    return str_replace(array_keys($aUnits), array_values($aUnits), $mixedInput);
+}
+
+/**
+ * Escapes string/array ready to pass to php script with filtered symbols like ', " etc
+ *
+ * @param mixed $mixedInput - string/array which should be filtered
+ * @return converted string / array
+ */
+function bx_php_string_apos ($mixedInput)
+{
+    return str_replace("'", "\\'", $mixedInput);
+}
+function bx_php_string_quot ($mixedInput)
+{
+    return str_replace('"', '\\"', $mixedInput);
+}
+
+/**
+ * Gets file contents by URL.
+ *
+ * @param string $sFileUrl - file URL to be read.
+ * @param array $aParams - an array of parameters to be pathed with URL.
+ * @return string the file's contents.
+ */
+function bx_file_get_contents($sFileUrl, $aParams = array(), $bChangeTimeout = false)
+{
+    if ($aParams)
+        $sFileUrl = bx_append_url_params($sFileUrl, $aParams);
+
+    $sResult = '';
+    if(function_exists('curl_init')) {
+        $rConnect = curl_init();
+
+        curl_setopt($rConnect, CURLOPT_URL, $sFileUrl);
+        curl_setopt($rConnect, CURLOPT_HEADER, 0);
+        curl_setopt($rConnect, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($rConnect, CURLOPT_FOLLOWLOCATION, 1);
+
+        if ($bChangeTimeout) {
+            curl_setopt($rConnect, CURLOPT_CONNECTTIMEOUT, 3);
+            curl_setopt($rConnect, CURLOPT_TIMEOUT, 3);
+        }
+
+        $sAllCookies = '';
+        foreach($_COOKIE as $sKey=>$sValue){
+            $sAllCookies .= $sKey."=".$sValue.";";
+        }
+        curl_setopt($rConnect, CURLOPT_COOKIE, $sAllCookies);
+
+        $sResult = curl_exec($rConnect);
+        curl_close($rConnect);
+    } else {
+
+        $iSaveTimeout = false;
+        if ($bChangeTimeout) {
+            $iSaveTimeout = ini_get('default_socket_timeout');
+            ini_set('default_socket_timeout', 3);
+        }
+
+        $sResult = @file_get_contents($sFileUrl);
+
+        if ($bChangeTimeout && false !== $iSaveTimeout) {
+            ini_set('default_socket_timeout', $iSaveTimeout);
+        }
+    }
+
+    return $sResult;
+}
+
+// calculation ini_get('upload_max_filesize') in bytes as example
+function return_bytes($val)
+{
+    $val = trim($val);
+    $last = strtolower($val{strlen($val)-1});
+    $val = (int)$val;
+    switch($last) {
+        // The 'G' modifier is available since PHP 5.1.0
+        case 'k':
+            $val *= 1024;
+            break;
+        case 'm':
+            $val *= 1024 * 1024;
+            break;
+        case 'g':
+            $val *= 1024 * 1024 * 1024;
+            break;
+    }
+    return $val;
+}
+
+// Generate Random Password
+function genRndPwd($iLength = 8, $bSpecialCharacters = true)
+{
+    $sPassword = '';
+    $sChars = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789";
+
+    if($bSpecialCharacters === true)
+        $sChars .= "!?=/&+,.";
+
+    srand((double)microtime()*1000000);
+    for($i = 0; $i < $iLength; $i++) {
+        $x = mt_rand(0, strlen($sChars) -1);
+        $sPassword .= $sChars{$x};
+    }
+
+    return $sPassword;
+}
+
+// Generate Random Salt for Password encryption
+function genRndSalt()
+{
+    return genRndPwd(8, true);
+}
+
+// Encrypt User Password
+function encryptUserPwd($sPwd, $sSalt)
+{
+    return sha1(md5($sPwd) . $sSalt);
+}
+
+function bx_get ($sName, $sMethod = false)
+{
+    if (isset($_GET[$sName]) && ('get' == $sMethod || !$sMethod))
+        return $_GET[$sName];
+    elseif (isset($_POST[$sName]) && ('post' == $sMethod || !$sMethod))
+        return $_POST[$sName];
+    else
+        return false;
+}
+
+function bx_encode_url_params ($a, $aExcludeKeys = array (), $aOnlyKeys = false)
+{
+    $s = '';
+    foreach ($a as $sKey => $sVal) {
+        if (in_array($sKey, $aExcludeKeys))
+            continue;
+        if (false !== $aOnlyKeys && !in_array($sKey, $aOnlyKeys))
+            continue;
+        if (is_array($sVal)) {
+            foreach ($sVal as $sSubVal) {
+                $s .= rawurlencode($sKey) . '[]=' . rawurlencode(is_array($sSubVal) ? 'array' : $sSubVal) . '&';
+            }
+        } else {
+            $s .= rawurlencode($sKey) . '=' . rawurlencode($sVal) . '&';
+        }
+    }
+    return $s;
+}
+
+function bx_append_url_params ($sUrl, $mixedParams)
+{
+    if (!$mixedParams)
+        return $sUrl;
+
+    $sParams = false == strpos($sUrl, '?') ? '?' : '&';
+
+    if (is_array($mixedParams)) {
+        foreach($mixedParams as $sKey => $sValue)
+            $sParams .= $sKey . '=' . $sValue . '&';
+        $sParams = substr($sParams, 0, -1);
+    } else {
+        $sParams .= $mixedParams;
+    }
+    return $sUrl . $sParams;
+}
+
+function bx_rrmdir($directory)
+{
+    if (substr($directory,-1) == "/")
+        $directory = substr($directory,0,-1);
+
+    if (!file_exists($directory) || !is_dir($directory))
+        return false;
+    elseif (!is_readable($directory))
+        return false;
+
+    if (!($directoryHandle = opendir($directory)))
+        return false;
+
+    while ($contents = readdir($directoryHandle)) {
+        if ($contents != '.' && $contents != '..') {
+            $path = $directory . "/" . $contents;
+
+            if (is_dir($path))
+                bx_rrmdir($path);
+            else
+                unlink($path);
+        }
+    }
+
+    closedir($directoryHandle);
+
+    if (!rmdir($directory))
+        return false;
+
+    return true;
+}
+
+function bx_clear_folder ($sPath, $aExts = array ())
+{
+    if (substr($$sPath,-1) == "/")
+        $sPath = substr($sPath,0,-1);
+
+    if (!file_exists($sPath) || !is_dir($sPath))
+        return false;
+    elseif (!is_readable($sPath))
+        return false;
+
+    if (!($h = opendir($sPath)))
+        return false;
+
+    while ($sFile = readdir($h)) {
+        if ('.' == $sFile || '..' == $sFile)
+            continue;
+
+        $sFullPath = $sPath . '/' . $sFile;
+
+        if (is_dir($sFullPath))
+            continue;
+
+        if (!$aExts || (($sExt = pathinfo($sFullPath, PATHINFO_EXTENSION)) && in_array($sExt, $aExts)))
+            @unlink($sFullPath);
+    }
+
+    closedir($h);
+
+    return true;
+}
+
+function bx_ltrim_str ($sString, $sPrefix, $sReplace = '')
+{
+    if ($sReplace && substr($sString, 0, strlen($sReplace)) == $sReplace)
+        return $sString;
+    if (substr($sString, 0, strlen($sPrefix)) == $sPrefix)
+        return $sReplace . substr($sString, strlen($sPrefix));
+    return $sString;
+}
+
+/**
+ * Convert array to attributes string
+ *
+ * Example:
+ * @code
+ * $a = array('name' => 'test', 'value' => 5);
+ * $s = bx_convert_array2attrs($a);
+ * echo $s; // outputs: name="test" value="5"
+ * @endcode
+ *
+ * @param $a - array of attributes
+ * @param $sClasses - classes to merge with 'class' attribute
+ * @param $sStyles - styles to merge with 'style' attribute
+ * @return string
+ */
+function bx_convert_array2attrs ($a, $sClasses = false, $sStyles = false)
+{
+    $sRet = '';
+
+    if (!$a || !is_array($a))
+        $a = array();
+
+    if ($sClasses) {
+        $sClasses = trim($sClasses);
+        $a['class'] = $sClasses . (!empty($a['class']) ? ' ' . $a['class'] : '');
+    }
+
+    if ($sStyles) {
+        $sStyles = trim($sStyles);
+        if (';' != $sStyles[strlen($sStyles)-1])
+            $sStyles .= ';';
+        $a['style'] = $sStyles . (!empty($a['style']) ? ' ' . $a['style'] : '');
+    }
+
+    foreach ($a as $sKey => $sValue) {
+        if(is_null($sValue)) // pass NULL values
+            continue;
+
+        $sValueC = bx_html_attribute($sValue, BX_ESCAPE_STR_QUOTE);
+
+        $sRet .= " $sKey=\"$sValueC\"";
+    }
+
+    return $sRet;
+}
+
+function bx_unicode_urldecode($s)
+{
+    preg_match_all('/%u([[:alnum:]]{4})/', $s, $a);
+
+    foreach ($a[1] as $uniord) {
+        $dec = hexdec($uniord);
+        $utf = '';
+
+        if ($dec < 128) {
+            $utf = chr($dec);
+        } else if ($dec < 2048) {
+            $utf = chr(192 + (($dec - ($dec % 64)) / 64));
+            $utf .= chr(128 + ($dec % 64));
+        } else {
+            $utf = chr(224 + (($dec - ($dec % 4096)) / 4096));
+            $utf .= chr(128 + ((($dec % 4096) - ($dec % 64)) / 64));
+            $utf .= chr(128 + ($dec % 64));
+        }
+
+        $s = str_replace('%u'.$uniord, $utf, $s);
+    }
+
+    return urldecode($s);
+}
+
+/**
+ * Raise an alert
+ * @param string $sType - system type
+ * @param string $sAction - system action
+ * @param int $iObjectId - object id
+ * @param int $iSenderId - sender (action's author) profile id, if it is false - then currectly logged in profile id is used
+ */
+function bx_alert($sUnit, $sAction, $iObjectId, $iSender = false, $aExtras = array())
+{
+    $o = new BxDolAlerts($sUnit, $sAction, $iObjectId, $iSender, $aExtras);
+    $o->alert();
+}
+
+function bx_replace_markers($mixed, $aMarkers)
+{
+    if (empty($aMarkers))
+        return $mixed;
+
+    if (is_array($mixed)) {
+        foreach ($mixed as $sKey => $sValue)
+            $mixed[$sKey] = bx_replace_markers ($sValue, $aMarkers);
+    } else {
+        foreach ($aMarkers as $sKey => $sValue)
+            $mixed = str_replace('{' . $sKey . '}', $sValue, $mixed);
+    }
+
+    return $mixed;
+}
+
+function bx_site_hash($sSalt = '', $isSkipVersion = false)
+{
+    return md5($sSalt . ($isSkipVersion ? '' : bx_get_ver()) . BX_DOL_SECRET . BX_DOL_URL_ROOT);
+}
+
+/**
+ * Transform string to method name string, for example it changes 'some_method' string to 'SomeMethod' string
+ * @param string where words are separated with underscore
+ * @return string where every word begins with capital letter
+ */
+function bx_gen_method_name ($s, $sWordsDelimiter = '_')
+{
+    return str_replace(' ', '', ucwords(str_replace($sWordsDelimiter, ' ', $s)));
+}
+
+/**
+ * Trigger user error
+ * @param $sMsg message to display
+ * @param $iNumLevelsBack add additional debug backtracing N levels back
+ */
+function bx_trigger_error ($sMsg, $iNumLevelsBack = 0)
+{
+    $a = debug_backtrace();
+    $sMsgAdd = "<br />\n related code in <b>{$a[$iNumLevelsBack]['file']}</b> on line <b>{$a[$iNumLevelsBack]['line']}</b> <br />\n";
+    trigger_error ($sMsg . $sMsgAdd, E_USER_ERROR);
+}
+
+/**
+ * Get Dolphin system DB version, for files version @see BX_DOL_VERSION, these versions must match
+ */
+function bx_get_ver ($bInvalidateCache = false)
+{
+    bx_import('BxDolDb');
+    $oDb = BxDolDb::getInstance();
+
+    if ($bInvalidateCache)
+        $oDb->cleanMemory('sys_version');
+    
+    $sQuery = $oDb->prepare("SELECT `version` FROM `sys_modules` WHERE `name` = 'system'");
+    return $oDb->fromMemory('sys_version', 'getOne', $sQuery);
+}
+
+/**
+ * Check if site maintetance mode is enabled.
+ * Maintetance mode is enabled when '.bx_maintenance' file exists in Dolphin root folder, 
+ * please note that this is hidden file and some file managers don't show it.
+ * @param $bShowHttpError show 503 HTTP error if site is in mainenance mode
+ * @return true if site is in maintenance mode, or false otherwise
+ */
+function bx_check_maintenance_mode ($bShowHttpError = false)
+{
+    $bMaintetance = file_exists(BX_DIRECTORY_PATH_ROOT . BX_MAINTENANCE_FILE) && !defined('BX_DOL_UPGRADING');
+
+    if ($bMaintetance && $bShowHttpError)
+        bx_show_service_unavailable_error_and_exit ('Site is temporarily unavailable due to scheduled maintenance, please try again in a minute.', 600);
+
+    return $bMaintetance;
+}
+
+/**
+ * Check for minimal requirements.
+ * if DISABLE_DOLPHIN_REQUIREMENTS_CHECK is defined then this requirements checking is skipped.
+ * @param $bShowHttpError show 503 HTTP error if site doesn't meet minimal requirements
+ * @return false if requirements are met, or array of errors of requirements aren't met
+ */
+function bx_check_minimal_requirements ($bShowHttpError = false)
+{
+    if (defined('DISABLE_DOLPHIN_REQUIREMENTS_CHECK'))
+        return false;
+
+    $aErrors = array();
+
+    $aErrors[] = (ini_get('register_globals') == 0) ? '' : '<b>register_globals</b> is on (you need to disable it, or your site will be unsafe)';
+    $aErrors[] = (ini_get('safe_mode') == 0) ? '' : '<b>safe_mode</b> is on (you need to disable it)';
+    $aErrors[] = (version_compare(PHP_VERSION, '5.3.0', '<')) ? 'PHP version is too old (please update to <b>PHP 5.3.0</b> at least)' : '';
+    $aErrors[] = (!extension_loaded( 'mbstring')) ? '<b>mbstring</b> extension not installed (Dolphin cannot work without it)' : '';
+    $aErrors[] = (ini_get('allow_url_include') == 0) ? '' : '<b>allow_url_include</b> is on (you need to disable it, or your site will be unsafe)';
+
+    $aErrors = array_diff($aErrors, array('')); // delete empty
+
+    $bFailedMinimalRequirements = !empty($aErrors);
+
+    if ($bFailedMinimalRequirements && $bShowHttpError) {
+        $sErrors = implode(" <br /> ", $aErrors);
+        bx_show_service_unavailable_error_and_exit($sErrors);
+    }
+
+    return $bFailedMinimalRequirements ? $aErrors : false;
+}
+
+/**
+ * Check if redirect to the correct hostname is required, for example redirect from site.com to www.site.com
+ * @param $bProcessRedirect process redirect and exit if needed
+ */
+function bx_check_redirect_to_correct_hostname ($bProcessRedirect = false)
+{
+    $aUrl = parse_url(BX_DOL_URL_ROOT);
+
+    $bRedirectRequired = isset($_SERVER['HTTP_HOST']) && 0 != strcasecmp($_SERVER['HTTP_HOST'], $aUrl['host']) && 0 != strcasecmp($_SERVER['HTTP_HOST'], $aUrl['host'] . ':80');
+
+    if ($bRedirectRequired && $bProcessRedirect) {
+        header( "Location:http://{$aUrl['host']}{$_SERVER['REQUEST_URI']}" );
+        exit;
+    }
+    
+    return $bRedirectRequired;
+}
+
+/**
+ * Check if redirect to remove install folder.
+ * If BX_SKIP_INSTALL_CHECK is defined then this redirect checking is skipped.
+ * @param $bProcessRedirect process redirect and exit if needed
+ */
+function bx_check_redirect_to_remove_install_folder ($bProcessRedirect = false)
+{
+    $bRemoveInstallFolder = !defined ('BX_SKIP_INSTALL_CHECK') && file_exists(BX_DIRECTORY_PATH_ROOT . 'install');
+
+    if ($bRemoveInstallFolder && $bProcessRedirect) {
+        header('Location:' . BX_DOL_URL_ROOT . 'install/index.php?action=remove_install');
+        exit;
+    }
+
+    return $bRemoveInstallFolder;
+}
+
+/**
+ * Show HTTP 503 service unavailable error and exit
+ */
+function bx_show_service_unavailable_error_and_exit ($sMsg = false, $iRetryAfter = 86400)
+{
+    header('HTTP/1.0 503 Service Unavailable', true, 503);
+    header('Retry-After: 600');
+    echo $sMsg ? $sMsg : 'Service temporarily unavailable';
+    exit;
+}
+
+/** @} */

+ 12 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/inc/version.inc.php

@@ -0,0 +1,12 @@
+<?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+define('BX_DOL_VERSION', '8.0.0-A7'); ///< Dolphin system files version, for DB version @see bx_get_ver, DB and files version must match
+
+/** @} */

+ 170 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/notifications/classes/BxBaseModNotificationsConfig.php

@@ -0,0 +1,170 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    BaseNotifications Base classes for Notifications like modules
+ * @ingroup     DolphinModules
+ *
+ * @{
+ */
+
+bx_import('BxBaseModGeneralConfig');
+
+class BxBaseModNotificationsConfig extends BxBaseModGeneralConfig
+{
+	protected $_oDb;
+
+    protected $_aPrefixes;
+    protected $_aObjects;
+
+	protected $_aHandlerDescriptor;
+    protected $_sHandlersMethod;
+    protected $_aHandlers;
+    protected $_aHandlersHidden;
+
+    protected $_aJsClass;
+    protected $_aJsObjects;
+
+    protected $_aPerPage;
+    protected $_aHtmlIds;
+	
+    protected $_sAnimationEffect;
+    protected $_iAnimationSpeed;
+
+    protected $_iPrivacyViewDefault;
+
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+		$this->_aPrefixes = array();
+		$this->_aObjects = array(
+			'alert' => $this->_sName,
+			'conn_subscriptions' => 'sys_profiles_subscriptions'
+		);
+
+        $this->_aHandlerDescriptor = array();
+        $this->_sHandlersMethod = '';
+        $this->_aHandlersHidden = array();
+        $this->_aHandlers = array();
+
+		$this->_aJsClass = array();
+		$this->_aJsObjects = array();
+
+		$this->_aPerPage = array();
+		$this->_aHtmlIds = array();
+
+		$this->_sAnimationEffect = 'fade';
+        $this->_iAnimationSpeed = 'slow';
+
+        $this->_iPrivacyViewDefault = BX_DOL_PG_ALL;
+    }
+
+	public function init(&$oDb)
+    {
+        $this->_oDb = &$oDb;
+        $sOptionPrefix = $this->getPrefix('option');
+
+        $aHandlers = $this->_oDb->getHandlers();
+        foreach($aHandlers as $aHandler) {
+            if($aHandler['type'] === BX_BASE_MOD_NTFS_HANDLER_TYPE_INSERT && !empty($aHandler['content'])) {
+                $aContent = unserialize($aHandler['content']);
+                if(is_array($aContent) && !empty($aContent))
+                    $aHandler = array_merge($aHandler, $aContent);
+            }
+
+           $this->_aHandlers[$aHandler['alert_unit'] . (!empty($aHandler['alert_action']) ? '_' . $aHandler['alert_action'] :'')] = $aHandler;
+        }
+
+        $sHideTimeline = getParam($sOptionPrefix . 'events_hide');
+        if(!empty($sHideTimeline))
+            $this->_aHandlersHidden = explode(',', $sHideTimeline);
+    }
+
+    public function getPrefix($sType = '')
+    {
+    	if(empty($sType))
+            return $this->_aPrefixes;
+
+        return isset($this->_aPrefixes[$sType]) ? $this->_aPrefixes[$sType] : '';
+    }
+
+    public function getObject($sType = '')
+    {
+    	if(empty($sType))
+            return $this->_aObjects;
+
+        return isset($this->_aObjects[$sType]) ? $this->_aObjects[$sType] : '';
+    }
+
+	public function getHandlerDescriptor()
+    {
+    	return $this->_aHandlerDescriptor;
+    }
+    public function getHandlersMethod()
+    {
+    	return $this->_sHandlersMethod;
+    }
+
+    public function isHandler($sKey = '')
+    {
+        return isset($this->_aHandlers[$sKey]);
+    }
+
+    public function getHandlers($sKey = '')
+    {
+        if($sKey == '')
+            return $this->_aHandlers;
+
+        return isset($this->_aHandlers[$sKey]) ? $this->_aHandlers[$sKey] : '';
+    }
+
+    public function getHandlersHidden()
+    {
+        return $this->_aHandlersHidden;
+    }
+
+	public function getJsClass($sType)
+    {
+        return isset($this->_aJsClass[$sType]) ? $this->_aJsClass[$sType] : '';
+    }
+
+    public function getJsObject($sType)
+    {
+        return isset($this->_aJsObjects[$sType]) ? $this->_aJsObjects[$sType] : '';
+    }
+
+	public function getPerPage($sType = 'default')
+    {
+    	if(empty($sType))
+            return $this->_aPerPage;
+
+        return isset($this->_aPerPage[$sType]) ? $this->_aPerPage[$sType] : '';
+    }
+
+    public function getHtmlIds($sType, $sKey = '')
+    {
+        if(empty($sKey))
+            return isset($this->_aHtmlIds[$sType]) ? $this->_aHtmlIds[$sType] : array();
+
+        return isset($this->_aHtmlIds[$sType][$sKey]) ? $this->_aHtmlIds[$sType][$sKey] : '';
+    }
+
+    public function getAnimationEffect()
+    {
+        return $this->_sAnimationEffect;
+    }
+
+    public function getAnimationSpeed()
+    {
+        return $this->_iAnimationSpeed;
+    }
+
+    public function getPrivacyViewDefault()
+    {
+        return $this->_iPrivacyViewDefault;
+    }
+}
+
+/** @} */

+ 5 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/profile/template/unit.html

@@ -0,0 +1,5 @@
+
+<div class="bx-base-pofile-unit bx-def-unit bx-def-margin-bottom bx-def-margin-sec-right">
+    <a href="__content_url__"><img class="bx-def-thumb bx-def-thumb-size" src="__thumb_url__" /></a><div class="bx-def-unit-info bx-def-thumb-size-max-height bx-def-padding-sec-left"><a class="bx-def-unit-info-title" href="__content_url__">__title__</a></div>
+</div>
+

+ 5 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/author.html

@@ -0,0 +1,5 @@
+
+<div class="bx-base-text-author bx-def-unit">
+    <a href="__author_url__"><img class="bx-def-thumb bx-def-thumb-size" src="__author_thumb_url__" /></a><div class="bx-def-unit-info bx-def-thumb-size-max-height bx-def-padding-left"><a class="bx-def-unit-info-title" href="__author_url__">__author_title__</a><br /><span class="bx-def-font-grayed bx-def-font-small">__author_desc__</span></div>
+</div>
+

+ 72 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/css/main.css

@@ -0,0 +1,72 @@
+
+@import url(../../../general/template/css/main.css);
+
+/* unit */
+
+.bx-base-text-unit:first-child h1 {
+    padding-top:0;
+}
+
+.bx-base-text-unit h1 {
+    margin:0;
+}
+
+.bx-base-text-unit .sys-icon {
+    vertical-align:middle;
+}
+
+.bx-base-text-unit-no-thumb,
+.bx-base-text-unit-thumb {
+    display:block;
+    overflow:hidden;
+}
+
+.bx-base-text-unit-summary p {
+    margin:0;
+}
+
+.bx-base-text-unit-info {
+    white-space:nowrap;
+    overflow:hidden;
+    text-overflow:ellipsis;
+}
+
+.bx-base-text-unit-gallery .bx-base-text-unit-no-thumb,
+.bx-base-text-unit-gallery .bx-base-text-unit-thumb {
+    float:none;
+    line-height:1.25em;
+}
+
+.bx-base-text-unit-gallery h2 {
+    line-height:1.25em;
+    max-height:3.75em;
+    overflow:hidden; 
+}
+
+.bx-base-text-wrapper {
+    margin-bottom:0;
+}
+
+.bx-base-text-unit-ext .bx-base-text-unit-thumb {
+    float:left;
+}
+
+/* view page */
+
+.bx-base-text-entity-content p:first-child,
+.bx-base-text-entity-content h1:first-child {
+    margin-top:0;
+}
+
+.bx-base-text-entity-content p:last-child,
+.bx-base-text-entity-content h1:last-child {
+    margin-bottom:0;
+}
+
+.bx-base-text-entity-share .bx-vote,
+.bx-base-text-entity-share .bx-tl-share,
+.bx-base-text-entity-share .bx-social-sharing {
+	position: relative;
+	float: left;
+}
+

+ 7 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/entry-text.html

@@ -0,0 +1,7 @@
+
+<div class="bx-base-text-entity-content bx-def-constraint-list bx-def-constraint-pre bx-def-constraint-object bx-def-constraint-iframe bx-def-constraint-img">
+    <h1>__title__</h1>
+    <p class="bx-base-text-entity-location bx-def-font-grayed bx-def-font-middle">__entry_location__</p>
+    __text__
+</div>
+

+ 16 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/unit.html

@@ -0,0 +1,16 @@
+
+<div class="bx-base-text-unit bx-base-text-unit-ext bx-def-margin-bottom">
+    <h1 class="bx-def-padding-sec-top"><a href="__content_url__">__title__</a></h1>
+    <div class="bx-base-text-unit-info bx-def-margin-sec-top">
+        <span class="bx-def-font-grayed">__entry_posting_date__</span>
+        <span class="bx-base-text-unit-info-div bx-def-font-grayed">&#183;</span>
+        <a class="bx-base-text-unit-author" href="__author_url__">__author__</a>
+    </div>
+    <div class="bx-base-text-unit-summary bx-def-margin-sec-top bx-def-margin-sec-bottom bx-clearfix">
+        <bx_if:thumb>
+            <img class="bx-base-text-unit-thumb bx-def-gal-img-size bx-def-round-corners bx-def-margin-sec-right" alt="__title__" src="__thumb_url__" />
+        </bx_if:thumb>
+        __summary__
+    </div> 
+</div>
+

+ 23 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/modules/base/text/template/unit_gallery.html

@@ -0,0 +1,23 @@
+<div class="bx-base-text-unit bx-base-text-unit-gallery bx-def-gal-unit-size bx-def-padding">
+
+    <bx_if:thumb>
+        <a href="__content_url__"><img class="bx-base-text-unit-thumb bx-def-gal-img-size bx-def-round-corners bx-def-color-bg-box bx-def-font-middle" alt="__summary_attr__" src="__thumb_url__" /></a>
+    </bx_if:thumb>
+
+    <bx_if:no_thumb>
+        <div class="bx-def-gal-img-column">
+            <div class="bx-base-text-unit-no-thumb bx-def-gal-div-size bx-def-round-corners bx-def-color-bg-box bx-def-font-middle bx-def-font-grayed bx-def-padding bx-def-box-sizing">__summary_plain__</div>
+        </div>
+    </bx_if:no_thumb>
+
+    <div class="bx-def-gal-unit-column">
+        <h2 class="bx-def-margin-sec-topbottom"><a href="__content_url__">__title__</a></h2>
+    </div>
+
+    <div class="bx-base-text-unit-info">
+        <span class="bx-def-font-grayed">__entry_posting_date__</span>
+        <span class="bx-base-text-unit-info-div bx-def-font-grayed">&#183;</span>
+        <a class="bx-base-text-unit-author" href="__author_url__">__author__</a>
+    </div>
+
+</div>

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 7 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/plugins_public/marka/marka.min.css


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 8 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/plugins_public/marka/marka.min.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/plugins_public/marka/marka.min.js.map


+ 791 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioInstaller.php

@@ -0,0 +1,791 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxDolInstallerUtils');
+
+define("BX_DOL_STUDIO_INSTALLER_SUCCESS", 0);
+define("BX_DOL_STUDIO_INSTALLER_FAILED", 1);
+
+/**
+ * Base class for Installer classes in modules engine.
+ *
+ * The class contains different check functions which are used during the installation process.
+ * An object of the class is created automatically with Dolphin's modules installer.
+ * Installation/Uninstallation process can be controlled with config.php file located in  [module]/install/ folder.
+ *
+ *
+ * Example of usage:
+ * @see any module included in the default Dolphin's package.
+ *
+ *
+ * Memberships/ACL:
+ * Doesn't depend on user's membership.
+ *
+ *
+ * Alerts:
+ * no alerts available
+ *
+ */
+
+class BxDolStudioInstaller extends BxDolInstallerUtils
+{
+    protected $oDb;
+
+    protected $_aConfig;
+    protected $_sBasePath;
+    protected $_sHomePath;
+    protected $_sModulePath;
+
+    protected $_bUseFtp;
+    protected $_aActions;
+
+    protected $_bShowOnSuccess = false;
+
+    function __construct($aConfig)
+    {
+        parent::__construct();
+
+        $this->oDb = bx_instance('BxDolStudioInstallerQuery');
+
+        $this->_aConfig = $aConfig;
+        $this->_sBasePath = BX_DIRECTORY_PATH_MODULES;
+        $this->_sHomePath = $this->_sBasePath . $aConfig['home_dir'];
+        $this->_sModulePath = $this->_sBasePath . $aConfig['home_dir'];
+
+        $this->_bUseFtp = BX_FORCE_USE_FTP_FILE_TRANSFER;
+
+        $this->_aActions = array(
+            'perform_install' => array(
+                'title' => '',
+                'success' => _t('_adm_msg_modules_success_install'),
+                'failed' => ''
+            ),
+            'perform_uninstall' => array(
+                'title' => '',
+                'success' => _t('_adm_msg_modules_success_uninstall'),
+                'failed' => ''
+            ),
+            'perform_enable' => array(
+                'title' => '',
+                'success' => _t('_adm_msg_modules_success_enable'),
+                'failed' => ''
+            ),
+            'perform_disable' => array(
+                'title' => '',
+                'success' => _t('_adm_msg_modules_success_disable'),
+                'failed' => ''
+            ),
+            'check_script_version' => array(
+                'title' => _t('_adm_txt_modules_check_script_version'),
+            ),
+            'check_dependencies' => array(
+                'title' => _t('_adm_txt_modules_check_dependencies'),
+            ),
+            'show_introduction' => array(
+                'title' => _t('_adm_txt_modules_show_introduction'),
+            ),
+            'move_sources' => array(
+                'title' => _t('_adm_txt_modules_move_sources'),
+            ),
+            'execute_sql' => array(
+                'title' => _t('_adm_txt_modules_execute_sql'),
+            ),
+            'install_language' => array(
+                'title' => _t('_adm_txt_modules_install_language'),
+            ),
+            'update_languages' => array(
+                'title' => _t('_adm_txt_modules_update_languages'),
+            ),
+            'update_relations' => array(
+                'title' => _t('_adm_txt_modules_update_relations'),
+            ),
+            'process_deleted_profiles' => array(
+                'title' => _t('_adm_txt_modules_process_deleted_profiles'),
+            ),
+            'recompile_global_paramaters' => array(
+                'title' => _t('_adm_txt_modules_recompile_global_paramaters'),
+            ),
+            'recompile_menus' => array(
+                'title' => _t('_adm_txt_modules_recompile_menus'),
+            ),
+            'recompile_site_stats' => array(
+                'title' => _t('_adm_txt_modules_recompile_site_stats'),
+            ),
+            'recompile_comments' => array(
+                'title' => _t('_adm_txt_modules_recompile_comments'),
+            ),
+            'recompile_member_actions' => array(
+                'title' => _t('_adm_txt_modules_recompile_member_actions'),
+            ),
+            'recompile_votes' => array(
+                'title' => _t('_adm_txt_modules_recompile_votes'),
+            ),
+            'recompile_search' => array(
+                'title' => _t('_adm_txt_modules_recompile_search'),
+            ),
+            'recompile_injections' => array(
+                'title' => _t('_adm_txt_modules_recompile_injections'),
+            ),
+            'recompile_permalinks' => array(
+                'title' => _t('_adm_txt_modules_recompile_permalinks'),
+            ),
+            'recompile_alerts' => array(
+                'title' => _t('_adm_txt_modules_recompile_alerts'),
+            ),
+            'clear_db_cache' => array(
+                'title' => _t('_adm_txt_modules_clear_db_cache'),
+            ),
+            'show_conclusion' => array(
+                'title' => _t('_adm_txt_modules_show_conclusion'),
+            ),
+        );
+
+        $this->_aNonHashableFiles = array(
+            'install',
+            'updates'
+        );
+    }
+
+    public function install($aParams, $bAutoEnable = false)
+    {
+    	$bAutoEnable = $bAutoEnable || (isset($aParams['auto_enable']) && (bool)$aParams['auto_enable']);
+    	$bHtmlResponce = isset($aParams['html_response']) && (bool)$aParams['html_response'];
+
+        //--- Check whether the module was already installed ---//
+        if($this->oDb->isModule($this->_aConfig['home_uri']))
+            return array(
+                'message' => _t('_adm_err_modules_already_installed'),
+                'result' => false
+            );
+
+        $aResult = array();
+        bx_alert('system', 'before_install', 0, false, array ('config' => $this->_aConfig, 'result' => &$aResult));
+        if ($aResult && !$aResult['result'])
+            return $aResult;
+
+        //--- Check mandatory settings ---//
+        if($this->oDb->isModuleParamsUsed($this->_aConfig['home_uri'], $this->_aConfig['home_dir'], $this->_aConfig['db_prefix'], $this->_aConfig['class_prefix']))
+            return array(
+                'message' => _t('_adm_txt_modules_params_used'),
+                'result' => false
+            );
+
+        //--- Check version compatibility ---//
+        if(!$this->_isCompatibleWith())
+            return array(
+                'message' => $this->_displayResult('check_script_version', false, '_adm_err_modules_wrong_version_script', $bHtmlResponce),
+                'result' => false
+            );
+
+        //--- Check for available translations ---//
+        $oFile = $this->_getFileManager();
+        $sModuleUri = $this->_aConfig['home_uri'];
+
+        $aLanguages = $this->oDb->getModulesBy(array('type' => 'languages'));
+        foreach($aLanguages as $aLanguage) {
+            if($aLanguage['uri'] == 'en')
+                continue;
+
+			$aLanguageConfig = self::getModuleConfig(BX_DIRECTORY_PATH_MODULES . $aLanguage['path'] . '/install/config.php');
+			if(empty($aLanguageConfig))
+				continue;
+
+            if(!isset($aLanguageConfig['includes'][$sModuleUri]) || empty($aLanguageConfig['includes'][$sModuleUri]))
+                continue;
+
+            $sSrcPath = 'modules/' . $aLanguage['path'] . 'install/data/' . $aLanguageConfig['includes'][$sModuleUri];
+            $sDstPath = $aLanguageConfig['includes'][$sModuleUri];
+            $oFile->copy($sSrcPath, $sDstPath);
+        }
+
+        //--- Check actions ---//
+        $aResult = $this->_perform('install', $aParams);
+        if($aResult['result']) {
+            $sDependencies = "";
+            if(isset($this->_aConfig['install']['check_dependencies']) && (int)$this->_aConfig['install']['check_dependencies'] == 1 && isset($this->_aConfig['dependencies']) && is_array($this->_aConfig['dependencies']))
+                $this->_aConfig['dependencies'] = array_keys($this->_aConfig['dependencies']);
+
+            $iModuleId = $this->oDb->insertModule($this->_aConfig);
+
+            bx_import('BxDolModule');
+            $sTitleKey = BxDolModule::getTitleKey($this->_aConfig['home_uri']);
+
+            bx_import('BxDolStudioLanguagesUtils');
+            $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+            $oLanguages->addLanguageString($sTitleKey, $this->_aConfig['title']);
+            $oLanguages->compileLanguage();
+
+            bx_import('BxDolStudioInstallerUtils');
+            BxDolStudioInstallerUtils::getInstance()->checkModules();
+
+            $aFiles = array();
+            $this->hashFiles($this->_sModulePath, $aFiles);
+
+            foreach($aFiles as $aFile)
+                $this->oDb->insertModuleTrack($iModuleId, $aFile);
+
+            $this->oDb->cleanMemory('sys_modules_' . $this->_aConfig['home_uri']);
+            $this->oDb->cleanMemory('sys_modules_' . $iModuleId);
+            $this->oDb->cleanMemory('sys_modules');
+        }
+
+        bx_alert('system', 'install', 0, false, array ('config' => $this->_aConfig, 'result' => &$aResult));
+
+	    if($aResult['result'] && $bAutoEnable) {
+			$aResultEnable = $this->enable($aParams);
+
+			$aResult['result'] = $aResult['result'] & $aResultEnable['result'];
+			$aResult['message'] = $aResult['message'] . $aResultEnable['message'];
+		}
+
+        return $aResult;
+    }
+
+    public function uninstall($aParams, $bAutoDisable = false)
+    {
+    	$bAutoDisable = $bAutoDisable || (isset($aParams['auto_disable']) && (bool)$aParams['auto_disable']);
+    	$bHtmlResponce = isset($aParams['html_response']) && (bool)$aParams['html_response'];
+
+        //--- Check whether the module was already uninstalled ---//
+        if(!$this->oDb->isModule($this->_aConfig['home_uri']))
+            return array(
+                'message' => _t('_adm_err_modules_already_uninstalled'),
+                'result' => false
+            );
+
+		if($bAutoDisable) {
+            $aResultDisable = $this->disable($aParams);
+            if(!$aResultDisable['result'])
+            	return $aResultDisable;
+        }
+
+        $aResult = array();
+        bx_alert('system', 'before_uninstall', 0, false, array ('config' => $this->_aConfig, 'result' => &$aResult));
+        if ($aResult && !$aResult['result'])
+            return $aResult;
+
+        //--- Check for dependent modules ---//
+        $bDependent = false;
+        $aDependents = $this->oDb->getDependent($this->_aConfig['home_uri']);
+        if(is_array($aDependents) && !empty($aDependents)) {
+            $bDependent = true;
+
+            $sMessage = '<br />' . _t('_adm_err_modules_wrong_dependency_uninstall') . '<br />';
+            foreach($aDependents as $aDependent)
+                $sMessage .= $aDependent['title'] . '<br />';
+        }
+
+        if($bDependent)
+            return array(
+                'message' => $this->_displayResult('check_dependencies', false, $sMessage, $bHtmlResponce),
+                'result' => false
+            );
+
+        $aResult = $this->_perform('uninstall', $aParams);
+        if($aResult['result']) {
+            $iModuleId = $this->oDb->deleteModule($this->_aConfig);
+
+            bx_import('BxDolModule');
+            $sTitleKey = BxDolModule::getTitleKey($this->_aConfig['home_uri']);
+
+            bx_import('BxDolStudioLanguagesUtils');
+            $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+            $oLanguages->deleteLanguageString($sTitleKey);
+            $oLanguages->compileLanguage();
+
+            $this->oDb->cleanMemory ('sys_modules_' . $this->_aConfig['home_uri']);
+            $this->oDb->cleanMemory ('sys_modules_' . $iModuleId);
+            $this->oDb->cleanMemory ('sys_modules');
+        }
+
+        if($bAutoDisable) {
+	        $aResult['result'] = $aResultDisable['result'] & $aResult['result'];
+			$aResult['message'] = $aResultDisable['message'] . $aResult['message'];
+        }
+
+        bx_alert('system', 'uninstall', 0, false, array ('config' => $this->_aConfig, 'result' => &$aResult));
+        return $aResult;
+    }
+
+    public function delete($aParams)
+    {
+    	$aError = array(
+			'message' => _t('_adm_err_modules_cannot_remove_package'),
+			'result' => false
+		);
+
+    	$oFile = $this->_getFileManager();
+    	if(empty($oFile))
+    		return $aError;
+
+        if(!$oFile->delete('modules/' . $this->_aConfig['home_dir']))
+            return $aError;
+
+        return array(
+            'message' => '', //_t('_adm_msg_modules_success_delete'),
+            'result' => true
+        );
+    }
+
+    public function recompile($aParams)
+    {
+        bx_import('BxDolStudioLanguagesUtils');
+        $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+
+        $aResult = array('message' => '', 'result' => false);
+
+        $aLanguages = $this->oDb->getLanguages();
+        if (isAdmin() && !empty($aLanguages)) {
+            $this->_updateLanguage(false, current($aLanguages));
+
+            $bResult = false;
+            foreach($aLanguages as $aLanguage) {
+                $bResult = $this->_updateLanguage(true, $aLanguage) && $oLanguages->compileLanguage($aLanguage['id']);
+                $aResult['message'] .= $aLanguage['title'] . ': <span style="color:' . ($bResult ? 'green' : 'red') . '">' . _t($bResult ? '_adm_txt_modules_process_action_success' : '_adm_txt_modules_process_action_failed') . '</span><br />';
+
+                $aResult['result'] |= $bResult;
+            }
+        }
+
+        return $aResult;
+    }
+
+    public function enable($aParams)
+    {
+        $aModule = $this->oDb->getModuleByUri($this->_aConfig['home_uri']);
+
+        //--- Check whether the module is installed ---//
+        if(empty($aModule) || !is_array($aModule))
+            return array(
+                'message' => _t('_adm_err_modules_module_not_installed'),
+                'result' => false
+            );
+
+        //--- Check whether the module is already enabled ---//
+        if((int)$aModule['enabled'] != 0)
+            return array(
+                'message' => _t('_adm_err_modules_already_enabled'),
+                'result' => false
+            );
+
+		$aResult = array();
+        bx_alert('system', 'before_enable', 0, false, array ('config' => $this->_aConfig, 'result' => &$aResult));
+        if ($aResult && !$aResult['result'])
+            return $aResult;
+
+        $aResult = $this->_perform('enable', $aParams);
+        if($aResult['result']) {
+            $this->oDb->enableModuleByUri($aModule['uri']);
+
+            $this->oDb->cleanMemory('sys_modules_' . $aModule['uri']);
+            $this->oDb->cleanMemory('sys_modules_' . $aModule['id']);
+            $this->oDb->cleanMemory('sys_modules');
+        }
+
+        bx_alert('system', 'enable', 0, false, array ('config' => $this->_aConfig, 'result' => &$aResult));
+        return $aResult;
+    }
+
+    public function disable($aParams)
+    {
+    	$bHtmlResponce = isset($aParams['html_response']) && (bool)$aParams['html_response'];
+
+        $aModule = $this->oDb->getModuleByUri($this->_aConfig['home_uri']);
+
+        //--- Check whether the module is installed ---//
+        if(empty($aModule) || !is_array($aModule))
+            return array(
+                'message' => _t('_adm_err_modules_module_not_installed'),
+                'result' => false
+            );
+
+        //--- Check whether the module is already disabled ---//
+        if((int)$aModule['enabled'] == 0)
+            return array(
+                'message' => _t('_adm_err_modules_already_disabled'),
+                'result' => false
+            );
+
+		$aResult = array();
+        bx_alert('system', 'before_disable', 0, false, array ('config' => $this->_aConfig, 'result' => &$aResult));
+        if ($aResult && !$aResult['result'])
+            return $aResult;
+
+        //--- Check for dependent modules ---//
+        $bDependent = false;
+        $aDependents = $this->oDb->getDependent($this->_aConfig['home_uri']);
+        if(is_array($aDependents) && !empty($aDependents)) {
+            $bDependent = true;
+
+            $sMessage = '<br />' . _t('_adm_err_modules_wrong_dependency_disable') . '<br />';
+            foreach($aDependents as $aDependent)
+                $sMessage .= $aDependent['title'] . '<br />';
+        }
+
+        if($bDependent)
+            return array(
+                'message' => $this->_displayResult('check_dependencies', false, $sMessage, $bHtmlResponce),
+                'result' => false
+            );
+
+        $aResult = $this->_perform('disable', $aParams);
+        if($aResult['result']) {
+            $this->oDb->disableModuleByUri($aModule['uri']);
+
+            $this->oDb->cleanMemory('sys_modules_' . $aModule['uri']);
+            $this->oDb->cleanMemory('sys_modules_' . $aModule['id']);
+            $this->oDb->cleanMemory('sys_modules');
+        }
+
+        bx_alert('system', 'disable', 0, false, array ('config' => $this->_aConfig, 'result' => &$aResult));
+        return $aResult;
+    }
+
+    protected function filePathWithoutBase($sPath)
+    {
+        return bx_ltrim_str($sPath, $this->_sModulePath);
+    }
+
+    function _perform($sOperationName, $aParams = array())
+    {
+    	$bHtmlResponce = isset($aParams['html_response']) && (bool)$aParams['html_response'];
+
+        if(!defined('BX_SKIP_INSTALL_CHECK') && !defined('BX_DOL_CRON_EXECUTE') && !$GLOBALS['logged']['admin'])
+            return array('message' => '_adm_mod_err_only_admin_can_perform_operations_with_modules', 'result' => false);
+
+        $sMessage = '';
+        foreach($this->_aConfig[$sOperationName] as $sAction => $iEnabled) {
+            $sMethod = 'action' . bx_gen_method_name($sAction);
+            if($iEnabled == 0 || !method_exists($this, $sMethod))
+                continue;
+
+            $mixedResult = $this->$sMethod($sOperationName);
+
+            //--- On Success ---//
+            if((is_int($mixedResult) && (int)$mixedResult == BX_DOL_STUDIO_INSTALLER_SUCCESS) || (isset($mixedResult['code']) && (int)$mixedResult['code'] == BX_DOL_STUDIO_INSTALLER_SUCCESS)) {
+                $sMessage .= $this->_displayResult($sAction, true, isset($mixedResult['content']) ? $mixedResult['content'] : '', $bHtmlResponce);
+                continue;
+            }
+
+            //--- On Failed ---//
+            $sMethodFailed = $sMethod . 'Failed';
+            return array('message' => $this->_displayResult($sAction, false, method_exists($this, $sMethodFailed) ? $this->$sMethodFailed($mixedResult) : $this->actionOperationFailed($mixedResult), $bHtmlResponce), 'result' => false);
+        }
+
+        if($this->_bShowOnSuccess)
+            $sMessage = $this->_aActions['perform_' . $sOperationName]['success'] . $sMessage;
+
+        return array('message' => $sMessage, 'result' => true);
+    }
+
+    function _displayResult($sAction, $bResult, $sResult = '', $bHtmlResponse = true)
+    {
+    	if($bResult && !in_array($sAction, array('show_introduction', 'show_conclusion')) && !$this->_bShowOnSuccess)
+            return '';
+
+        $sTitle = $this->_aActions[$sAction]['title'] . ' ';
+        if(!empty($sResult))
+            $sResult = (substr($sResult, 0, 1) == '_' ? _t($sResult) : $sResult) . '<br />';
+
+		$sContent = !empty($sResult) ? $sResult : _t($bResult ? '_adm_txt_modules_process_action_success' : '_adm_err_modules_process_action_failed') . '<br />';
+		if(!$bHtmlResponse)
+			return $sTitle . $sContent;
+
+		bx_import('BxDolStudioTemplate');
+        return BxDolStudioTemplate::getInstance()->parseHtmlByName('mod_action_result_step.html', array(
+        	'color' => $bResult ? 'green' : 'red',
+            'title' => $sTitle,
+            'content' => $sContent
+        ));
+    }
+
+    //--- Action Methods ---//
+    function actionOperationFailed($mixedResult)
+    {
+        return _t('_adm_err_modules_process_action_failed');
+    }
+    function actionCheckDependencies($sOperation)
+    {
+        $sContent = '';
+
+        if(in_array($sOperation, array('install', 'enable', 'update'))) {
+            if(!isset($this->_aConfig['dependencies']) || !is_array($this->_aConfig['dependencies']))
+                return BX_DOL_STUDIO_INSTALLER_SUCCESS;
+
+            foreach($this->_aConfig['dependencies'] as $sModuleUri => $sModuleTitle)
+                if(!$this->oDb->isModule($sModuleUri))
+                    $sContent .= $sModuleTitle . '<br />';
+
+            if(!empty($sContent))
+                $sContent = '<br />' . _t('_adm_err_modules_wrong_dependency_install') . '<br />' . $sContent;
+        }
+
+        return empty($sContent) ? BX_DOL_STUDIO_INSTALLER_SUCCESS : array('code' => BX_DOL_STUDIO_INSTALLER_FAILED, 'content' => $sContent);
+    }
+    function actionCheckDependenciesFailed($mixedResult)
+    {
+        return $mixedResult['content'];
+    }
+    function actionShowIntroduction($sOperation)
+    {
+        if(!isset($this->_aConfig[$sOperation . '_info']['introduction']))
+            return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+        $sPath = $this->_sHomePath . 'install/info/' . $this->_aConfig[$sOperation . '_info']['introduction'];
+        return file_exists($sPath) ? array("code" => BX_DOL_STUDIO_INSTALLER_SUCCESS, "content" => file_get_contents($sPath)) : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionShowConclusion($sOperation)
+    {
+        if(!isset($this->_aConfig[$sOperation . '_info']['conclusion']))
+            return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+        $sPath = $this->_sHomePath . 'install/info/' . $this->_aConfig[$sOperation . '_info']['conclusion'];
+        return file_exists($sPath) ? array("code" => BX_DOL_STUDIO_INSTALLER_SUCCESS, "content" => file_get_contents($sPath)) : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionMoveSources($sOperation)
+    {
+        $oFile = $this->_getFileManager();
+        $aInstalled = array_merge(array('system'), $this->oDb->getModulesUri());
+
+        $bResult = true;
+        foreach($this->_aConfig['includes'] as $sUri => $sPath) {
+            if(!in_array($sUri, $aInstalled) || empty($sPath))
+                continue;
+
+            if($sOperation == 'install') {
+                $sSrcPath = 'modules/' . $this->_aConfig['home_dir'] . 'install/data/' . $sPath;
+                $sDstPath = $sPath;
+                $bResult &= $oFile->copy($sSrcPath, $sDstPath);
+            } else if($sOperation == 'uninstall')
+                $bResult &= $oFile->delete($sPath);
+        }
+
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionExecuteSql($sOperation)
+    {
+        switch($sOperation) {
+            case 'install':
+                $this->actionExecuteSql('disable');
+                $this->actionExecuteSql('uninstall');
+                break;
+            case 'enable':
+                $this->actionExecuteSql('disable');
+                break;
+        }
+
+        $mixedResult = $this->oDb->executeSQL($this->_sHomePath . 'install/sql/' . $sOperation . '.sql');
+
+        return $mixedResult === true ? BX_DOL_STUDIO_INSTALLER_SUCCESS : array('code' => BX_DOL_STUDIO_INSTALLER_FAILED, 'content' => $mixedResult);
+    }
+    function actionExecuteSqlFailed($mixedResult)
+    {
+    	if(is_int($mixedResult))
+    		return $this->actionOperationFailed($mixedResult);
+
+        $sResult = '<br />' . _t('_adm_err_modules_wrong_mysql_query') . '<br />';
+        foreach($mixedResult['content'] as $aQuery) {
+            $sResult .= _t('_adm_err_modules_wrong_mysql_query_msg', $aQuery['error']) . '<br />';
+            $sResult .= '<pre>' . $aQuery['query'] . '</pre>';
+        }
+        return $sResult;
+    }
+    function actionInstallLanguage($sOperation)
+    {
+        bx_import('BxDolStudioLanguagesUtils');
+        $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+
+        $sLanguage = isset($this->_aConfig['home_uri']) ? $this->_aConfig['home_uri'] : '';
+
+        $bResult = true;
+        if($sOperation == 'install')
+            $bResult = $oLanguages->installLanguage(array('path' => $this->_aConfig['home_dir'], 'uri' => $this->_aConfig['home_uri'], 'lang_category' => $this->_aConfig['language_category']),false);
+
+        return $bResult && $oLanguages->compileLanguage(0, true) ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionUpdateLanguages($sOperation)
+    {
+        if(!isset($this->_aConfig['language_category']) || empty($this->_aConfig['language_category']))
+            return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+        bx_import('BxDolStudioLanguagesUtils');
+        $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+
+        $bResult = true;
+        $aResult = array();
+
+        //--- Process Language Category ---//
+        if($sOperation == 'install')
+            $iCategoryId = $oLanguages->addLanguageCategory($this->_aConfig['language_category']);
+
+        //--- Process languages' key=>value pears ---//
+        $aModule = array(
+            'path' => $this->_aConfig['home_dir'],
+            'uri' => $this->_aConfig['home_uri'],
+            'lang_category' => $this->_aConfig['language_category']
+        );
+
+        if($sOperation == 'install')
+            $bResult = $oLanguages->restoreLanguage(0, $aModule, false);
+        else if($sOperation == 'uninstall')
+            $bResult = $oLanguages->removeLanguageByModule($aModule, false);
+
+        if($sOperation == 'uninstall' && $bResult)
+            $oLanguages->deleteLanguageCategory($this->_aConfig['language_category']);
+
+        return $bResult && $oLanguages->compileLanguage(0, true) ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+	function actionUpdateRelations($sOperation)
+    {
+        if(!in_array($sOperation, array('install', 'uninstall', 'enable', 'disable'))) 
+        	return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+		if(empty($this->_aConfig['relations']) || !is_array($this->_aConfig['relations']))
+            return BX_DOL_STUDIO_INSTALLER_SUCCESS;
+
+		foreach($this->_aConfig['relations'] as $sModule) {
+			if(!$this->oDb->isModuleByName($sModule))
+				continue;
+
+			$aRelation = $this->oDb->getRelationsBy(array('type' => 'module', 'value' => $sModule));
+			if(empty($aRelation) || empty($aRelation['on_' . $sOperation]) || !BxDolRequest::serviceExists($aRelation['module'], $aRelation['on_' . $sOperation]))
+				continue;
+
+			BxDolService::call($aRelation['module'], $aRelation['on_' . $sOperation], array($this->_aConfig['home_uri']));
+		}
+
+        return BX_DOL_STUDIO_INSTALLER_SUCCESS;
+    }
+	function actionProcessDeletedProfiles($sOperation)
+    {
+        if(!in_array($sOperation, array('install', 'uninstall', 'enable', 'disable'))) 
+        	return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+        bx_import('BxDolProfileQuery');
+        $o = BxDolProfileQuery::getInstance();
+        $o->processDeletedProfiles();
+
+        return BX_DOL_STUDIO_INSTALLER_SUCCESS;
+    }
+    function actionRecompileGlobalParamaters($sOperation)
+    {
+        $bResult = $this->oDb->cacheParamsClear();
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionRecompileMenus($sOperation)
+    {
+        $bResult = $this->oDb->cleanCache('sys_objects_menu');
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionRecompileSiteStats($sOperation)
+    {
+        $bResult = $this->oDb->cleanCache('sys_stat_site');
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionRecompileComments($sOperation)
+    {
+        $bResult = $this->oDb->cleanCache('sys_objects_cmts');
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionRecompileMemberActions($sOperation)
+    {
+        $bResult = $this->oDb->cleanCache('sys_objects_actions');
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionRecompileVotes($sOperation)
+    {
+        $bResult = $this->oDb->cleanCache('sys_objects_vote');
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionRecompileInjections($sOperation)
+    {
+        $bResult = $this->oDb->cleanCache('sys_injections.inc');
+        $bResult = $bResult && $this->oDb->cleanCache('sys_injections_admin.inc');
+
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionRecompilePermalinks($sOperation)
+    {
+        $bResult = true;
+
+        bx_import('BxDolPermalinks');
+        $bResult = $bResult && BxDolPermalinks::getInstance()->cacheInvalidate();
+
+        $bResult = $bResult && ($this->actionRecompileMenus($sOperation) == BX_DOL_STUDIO_INSTALLER_SUCCESS);
+
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionRecompileAlerts($sOperation)
+    {
+        $bResult = $this->oDb->cleanCache('sys_alerts');
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+    function actionClearDbCache($sOperation)
+    {
+        $oCache = $this->oDb->getDbCacheObject();
+
+        $bResult = $oCache->removeAllByPrefix('db_');
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+
+    //--- Get/Set Methods ---//
+    function getVendor()
+    {
+        return $this->_aConfig['vendor'];
+    }
+    function getName()
+    {
+        return $this->_aConfig['name'];
+    }
+    function getTitle()
+    {
+        return $this->_aConfig['title'];
+    }
+    function getHomeDir()
+    {
+        return $this->_aConfig['home_dir'];
+    }
+
+    //--- Protected methods ---//
+    protected function _getFileManager()
+    {
+        $oFile = null;
+
+        if($this->_bUseFtp) {
+            bx_import('BxDolFtp');
+            $oFile = new BxDolFtp(isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost', getParam('sys_ftp_login'), getParam('sys_ftp_password'), getParam('sys_ftp_dir'));
+
+            if(!$oFile->connect())
+                return null;
+
+            if(!$oFile->isDolphin())
+                return null;
+        }
+        else {
+            bx_import('BxDolFile');
+            $oFile = BxDolFile::getInstance();
+        }
+
+        return $oFile;
+    }
+
+    protected function _isCompatibleWith()
+    {
+    	$sVersionCur = bx_get_ver();
+
+    	$bCompatible = false;
+        if(isset($this->_aConfig['compatible_with']) && is_array($this->_aConfig['compatible_with']))
+            foreach($this->_aConfig['compatible_with'] as $iKey => $sVersionReq)
+            	$bCompatible = $bCompatible || (version_compare($sVersionCur, $sVersionReq, '>=') == 1);
+
+		return $bCompatible;
+    }
+}
+
+/** @} */

+ 545 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioInstallerUtils.php

@@ -0,0 +1,545 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxDolLanguages');
+bx_import('BxDolInstallerUtils');
+
+define('BX_DOL_UNITY_URL_ROOT', 'http://www.boonex.com/');
+define('BX_DOL_UNITY_URL_MARKET', BX_DOL_UNITY_URL_ROOT . 'market/');
+
+define('BX_DOL_STUDIO_IU_RC_SUCCESS', 0);
+define('BX_DOL_STUDIO_IU_RC_FAILED', 1);
+define('BX_DOL_STUDIO_IU_RC_SCHEDULED', 2);
+
+class BxDolStudioInstallerUtils extends BxDolInstallerUtils implements iBxDolSingleton
+{
+	protected $bUseFtp;
+	protected $sAuthorizedAccessClass;
+
+    public function __construct()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
+
+        parent::__construct();
+
+        $this->bUseFtp = BX_FORCE_USE_FTP_FILE_TRANSFER;
+        $this->sAuthorizedAccessClass = 'BxDolStudioOAuthPlugin';
+    }
+
+    /**
+     * Prevent cloning the instance
+     */
+    public function __clone()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
+    }
+
+    /**
+     * Get singleton instance of the class
+     */
+    public static function getInstance()
+    {
+        if (!isset($GLOBALS['bxDolClasses'][__CLASS__]))
+            $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolStudioInstallerUtils();
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    public static function getNamePerformAction($sParam)
+    {
+    	return 'sys_perform_action_' . md5($sParam);
+    }
+
+    public static function getNameDownloadFile($sParam)
+    {
+    	return 'sys_download_file_complete_' . $sParam;
+    }
+
+    /*
+     * Is used to complete Downloading inside Transient Cron Job.
+     */
+    public function serviceDownloadFileComplete($sFilePath, $aParams = array())
+    {
+    	return $this->downloadFileComplete($sFilePath, array_merge($aParams, array('transient' => true)));
+    }
+
+    /*
+     * Is used to perform action inside Transient Cron Job.
+     */
+	public function servicePerformAction($sDirectory, $sOperation, $aParams)
+    {
+    	return $this->perform($sDirectory, $sOperation, array_merge($aParams, array('transient' => true)));
+    }
+
+    public function getAccessObject($bAuthorizedAccess)
+    {
+    	$sClass = $bAuthorizedAccess ? $this->sAuthorizedAccessClass : 'BxDolStudioJson';
+
+		bx_import($sClass);
+		return $sClass::getInstance();
+    }
+
+    public function getModules($bTitleAsKey = true)
+    {
+        $aModules = array();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aInstalledInfo = $this->getInstalledInfo();
+
+        $sPath = BX_DIRECTORY_PATH_MODULES;
+        if(($rHandleVendor = opendir($sPath)) !== false) {
+            while(($sVendor = readdir($rHandleVendor)) !== false) {
+                if(substr($sVendor, 0, 1) == '.' || !is_dir($sPath . $sVendor))
+                    continue;
+
+                if(($rHandleModule = opendir($sPath . $sVendor)) !== false) {
+                    while(($sModule = readdir($rHandleModule)) !== false) {
+                        if(!is_dir($sPath . $sVendor . '/' . $sModule) || substr($sModule, 0, 1) == '.')
+                            continue;
+
+						$sModulePath = $sVendor . '/' . $sModule . '/';
+                        $aModule = $this->getConfigModule($sPath . $sModulePath . 'install/config.php', !empty($aInstalledInfo[$sModulePath]) ? $aInstalledInfo[$sModulePath] : array());
+                        if(empty($aModule))
+                            continue;
+
+                        $aModules[$bTitleAsKey ? $aModule['title'] : $aModule['name']] = $aModule;
+                    }
+                    closedir($rHandleModule);
+                }
+            }
+            closedir($rHandleVendor);
+        }
+
+        ksort($aModules);
+        return $aModules;
+    }
+
+    public function getUpdates()
+    {
+        $aUpdates = array();
+
+        bx_import('BxDolStudioTemplate');
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aInstalledInfo = $this->getInstalledInfo();
+
+        $sPath = BX_DIRECTORY_PATH_MODULES;
+        if(($rHandleVendor = opendir($sPath)) !== false) {
+            while(($sVendor = readdir($rHandleVendor)) !== false) {
+                if(substr($sVendor, 0, 1) == '.' || !is_dir($sPath . $sVendor))
+                    continue;
+
+                if(($rHandleModule = opendir($sPath . $sVendor . '/')) !== false) {
+                    while(($sModule = readdir($rHandleModule)) !== false) {
+                        if(!is_dir($sPath . $sVendor . '/' . $sModule) || substr($sModule, 0, 1) == '.')
+                            continue;
+
+						$sModulePath = $sVendor . '/' . $sModule . '/';
+						if(empty($aInstalledInfo[$sModulePath]))
+							continue;
+
+						$aUpdate = $this->getUpdate($aInstalledInfo[$sModulePath]);
+						if(!empty($aUpdate))
+							$aUpdates[$aUpdate['title']] = $aUpdate;
+                    }
+                    closedir($rHandleModule);
+                }
+            }
+            closedir($rHandleVendor);
+        }
+
+        ksort($aUpdates);
+        return $aUpdates;
+    }
+
+    public function getUpdate($mixedModule)
+    {
+    	$aResult = array();
+
+    	if(!is_array($mixedModule)) {
+    		bx_import('BxDolModuleQuery');
+			$mixedModule = BxDolModuleQuery::getInstance()->{is_numeric($mixedModule) ? 'getModuleById' : 'getModuleByName'}($mixedModule); 
+    	}
+
+    	if(empty($mixedModule) || !is_array($mixedModule))
+    		return $aResult;
+
+    	$sPathModule = BX_DIRECTORY_PATH_MODULES . $mixedModule['path'];
+    	$sPathUpdates = $sPathModule . 'updates/';
+
+    	$rHandleUpdate = @opendir($sPathUpdates);
+		if($rHandleUpdate === false)
+			return $aResult; 
+
+		while(($sUpdate = readdir($rHandleUpdate)) !== false) {
+			$sPathUpdate = $sPathUpdates . $sUpdate . '/';
+			if(substr($sUpdate, 0, 1) == '.' || !is_dir($sPathUpdate))
+            	continue;
+
+			$aUpdate = $this->getConfigUpdate($sPathModule . 'install/config.php', $sPathUpdate . 'install/config.php', $mixedModule);
+			if(empty($aUpdate) || !is_array($aUpdate) || version_compare($aUpdate['module_version'], $aUpdate['version_from']) != 0)
+            	continue;
+
+			$aResult = $aUpdate;
+			break;
+		}
+
+		closedir($rHandleUpdate);
+
+        return $aResult;
+    }
+
+    public function perform($sDirectory, $sOperation, $aParams = array())
+    {
+	    $bTransient = false;
+    	if(isset($aParams['transient']) && $aParams['transient'] === true) {
+    		$bTransient = true;
+    		unset($aParams['transient']);
+    	}
+
+    	if(!defined('BX_DOL_CRON_EXECUTE') && !self::isRealOwner() && !in_array($sOperation, array('install', 'uninstall', 'enable', 'disable'))) {
+    		if($this->addTransientJob(self::getNamePerformAction($sDirectory), 'perform_action', array($sDirectory, $sOperation, $aParams)))
+    			return array('code' => BX_DOL_STUDIO_IU_RC_SCHEDULED, 'message' => _t('_adm_mod_msg_process_operation_scheduled'));
+    		else 
+    			return array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory));
+    	}
+
+    	$sConfigFile = 'install/config.php';
+        $sInstallerFile = 'install/installer.php';
+        $sInstallerClass = $sOperation == 'update' ? 'Updater' : 'Installer';
+
+        $aConfig = self::getModuleConfig(BX_DIRECTORY_PATH_MODULES . $sDirectory . $sConfigFile);
+
+        $sPathInstaller = BX_DIRECTORY_PATH_MODULES . $sDirectory . $sInstallerFile;
+        if(empty($aConfig) || !file_exists($sPathInstaller)) {
+        	$sMessage = _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory);
+        	if($bTransient)
+        		$this->emailNotify($sMessage);
+
+        	return array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => $sMessage);
+        } 
+
+		require_once($sPathInstaller);
+
+		$sClassName = $aConfig['class_prefix'] . $sInstallerClass;
+		$oInstaller = new $sClassName($aConfig);
+		$aResult = $oInstaller->$sOperation($aParams);
+
+		if(!$aResult['result'] && $bTransient)
+			$this->emailNotify($aResult['message']);
+
+        return array('code' => $aResult['result'] ? BX_DOL_STUDIO_IU_RC_SUCCESS : BX_DOL_STUDIO_IU_RC_FAILED, 'message' => $aResult['message']);
+    }
+
+    public function checkModules($bAuthorizedAccess = false)
+    {
+    	if($bAuthorizedAccess)
+        	$aProducts = $this->getAccessObject(true)->loadItems(array('dol_type' => 'purchased_products', 'dol_domain' => BX_DOL_URL_ROOT));
+    	else
+			$aProducts = $this->getAccessObject(false)->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_purchased', array('key' => getParam('sys_oauth_key')));
+
+    	bx_import('BxDolModuleQuery');
+    	$oModuleDb = BxDolModuleQuery::getInstance();
+
+    	$oModuleDb->updateModule(array('hash' => ''));
+        if(!empty($aProducts) && is_array($aProducts))
+	        foreach ($aProducts as $aProduct)
+	        	$oModuleDb->updateModule(array('hash' => $aProduct['hash']), array('name' => $aProduct['name']));
+
+		return $aProducts;
+    }
+
+    public function checkUpdates($bAuthorizedAccess = false)
+    {
+		bx_import('BxDolModuleQuery');
+		$aModules = BxDolModuleQuery::getInstance()->getModules();
+
+        $aProducts = array();
+        foreach($aModules as $aModule) {
+            if(empty($aModule['name']))
+                continue;
+
+            $aProducts[] = array(
+                'name' => $aModule['name'],
+                'version' => $aModule['version'],
+            	'hash' => $aModule['hash'],
+            );
+        }
+
+        $sProducts = base64_encode(serialize($aProducts));
+
+        if($bAuthorizedAccess)
+	        return $this->getAccessObject(true)->loadItems(array('dol_type' => 'available_updates', 'dol_products' => $sProducts));
+
+		return $this->getAccessObject(false)->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_updates', array(
+			'key' => getParam('sys_oauth_key'),
+			'products' => $sProducts
+		));
+    }
+
+    public function downloadFileAuthorized($iFileId)
+    {
+		$aItem = $this->getAccessObject(true)->loadItems(array('dol_type' => 'product_file', 'dol_file_id' => $iFileId));
+
+		return $this->downloadFileInit($aItem, array('module_name' => $aItem['module_name']));
+    }
+
+    public function downloadUpdatePublic($sModuleName, $bAutoUpdate = false)
+    {
+    	bx_import('BxDolModuleQuery');
+		$aModule = BxDolModuleQuery::getInstance()->getModuleByName($sModuleName);
+
+		$aItem = $this->getAccessObject(false)->load(BX_DOL_UNITY_URL_MARKET . 'json_download_update', array(
+			'key' => getParam('sys_oauth_key'),
+			'product' => base64_encode(serialize(array(
+				'name' => $aModule['name'],
+	            'version' => $aModule['version'],
+	            'hash' => $aModule['hash'],
+			))),
+		));
+
+		return $this->downloadFileInit($aItem, array('module_name' => $sModuleName, 'auto_action' => $bAutoUpdate ? 'update' : ''));
+    }
+
+    protected function downloadFileInit($aItem, $aParams = array())
+    {
+    	if(empty($aItem) || !is_array($aItem))
+			return $aItem;
+
+    	//--- write ZIP archive.
+        $sFilePath = '';
+        $mixedResult = $this->performWrite($aItem, $sFilePath);
+        if($mixedResult !== true)
+        	return $mixedResult;
+
+		return $this->downloadFileComplete($sFilePath, $aParams);
+    }
+
+    protected function downloadFileComplete($sFilePath, $aParams = array())
+    {
+		$bTransient = false;
+    	if(isset($aParams['transient']) && $aParams['transient'] === true) {
+    		$bTransient = true;
+    		unset($aParams['transient']);
+    	}
+
+    	$sAutoAction = isset($aParams['auto_action']) ? $aParams['auto_action'] : '';
+    	$bAutoAction = !empty($sAutoAction);
+
+		if(!defined('BX_DOL_CRON_EXECUTE') && !self::isRealOwner()) {
+			if(!$this->addTransientJob(self::getNameDownloadFile($aParams['module_name']), 'download_file_complete', array($sFilePath, $aParams)))
+				return _t('_adm_str_err_download_failed');
+
+			return array('code' => BX_DOL_STUDIO_IU_RC_SCHEDULED, 'message' => _t('_adm_str_msg_download' . ($bAutoAction ? '_and_install' : '') . '_scheduled'));
+		}
+
+        //--- Unarchive package.
+        $sPackagePath = '';
+        $mixedResult = $this->performUnarchive($sFilePath, $sPackagePath);
+
+        @unlink($sFilePath);
+        if($mixedResult !== true) {
+        	if($bTransient)
+        		$this->emailNotify($mixedResult);
+
+        	return $mixedResult;
+        }
+
+        //--- Copy unarchived package.
+        $sHomePath = '';
+        $mixedResult = $this->performCopy($sPackagePath, $sHomePath);
+
+        @bx_rrmdir($sPackagePath);
+        if($mixedResult !== true) {
+        	if($bTransient)
+        		$this->emailNotify($mixedResult);
+
+        	return $mixedResult;
+        }
+
+		if(!$bAutoAction)
+			 return true;
+
+		//--- Autoinstall the downloaded package if it's needed.
+		$aResult = $this->perform($sHomePath, $sAutoAction, $aParams);
+		if((int)$aResult['code'] != 0) {
+			if($bTransient)
+        		$this->emailNotify($aResult['message']);
+
+			return $aResult['message'];
+		}
+
+        return true;
+    }
+
+    protected function performWrite($aItem, &$sFilePath)
+    {
+    	$iUmaskSave = umask(0);
+
+    	$sFilePath = BX_DIRECTORY_PATH_TMP . $aItem['name'];
+    	if(file_exists($sFilePath))
+        	@unlink($sFilePath);
+
+        if(!$rHandler = fopen($sFilePath, 'w'))
+            return _t('_adm_str_err_cannot_write');
+
+        $sContent = urldecode($aItem['content']);
+        if(!fwrite($rHandler, $sContent, strlen($sContent)))
+            return _t('_adm_str_err_cannot_write');
+
+        fclose($rHandler);
+        
+        umask($iUmaskSave);
+        return true;
+    }
+
+    protected function performUnarchive($sFilePath, &$sPackagePath)
+    {
+    	$iUmaskSave = umask(0);
+
+    	if(!class_exists('ZipArchive'))
+            return _t('_adm_str_err_zip_not_available');
+
+        $oZip = new ZipArchive();
+        if($oZip->open($sFilePath) !== true)
+            return _t('_adm_str_err_cannot_unzip_package');
+
+        $sPackageFolder = '';
+        if($oZip->numFiles > 0)
+        	$sPackageFolder = $oZip->getNameIndex(0);
+
+        if(empty($sPackageFolder))
+        	return _t('_adm_str_err_cannot_unzip_package');
+        
+		$sPackagePath = BX_DIRECTORY_PATH_TMP . $sPackageFolder;
+        if(file_exists($sPackagePath)) // remove existing tmp folder with the same name
+            @bx_rrmdir($sPackagePath);
+
+        if(!$oZip->extractTo(BX_DIRECTORY_PATH_TMP))
+            return _t('_adm_str_err_cannot_unzip_package');
+
+        $oZip->close();
+
+        umask($iUmaskSave);
+        return true;
+    }
+
+    protected function performCopy($sPackagePath, &$sHomePath)
+    {
+    	if($this->bUseFtp) {
+	        $sLogin = getParam('sys_ftp_login');
+	        $sPassword = getParam('sys_ftp_password');
+	        $sPath = getParam('sys_ftp_dir');
+	        if(empty($sLogin) || empty($sPassword) || empty($sPath))
+	            return _t('_adm_str_err_no_ftp_info');
+	
+	        bx_import('BxDolFtp');
+	        $oFile = new BxDolFtp($_SERVER['HTTP_HOST'], $sLogin, $sPassword, $sPath);
+	
+	        if(!$oFile->connect())
+	            return _t('_adm_str_err_cannot_connect_to_ftp');
+	
+	        if(!$oFile->isDolphin())
+	            return _t('_adm_str_err_destination_not_valid');
+        }
+        else {
+        	bx_import('BxDolFile');
+        	$oFile = BxDolFile::getInstance();
+        }
+
+        $aConfig = self::getModuleConfig($sPackagePath . 'install/config.php');
+        if(empty($aConfig) || !is_array($aConfig) || empty($aConfig['home_dir']))
+            return _t('_adm_str_err_wrong_package_format');
+
+		$sHomePath = $aConfig['home_dir'];
+        if(!$oFile->copy($sPackagePath, 'modules/' . $sHomePath))
+            return _t('_adm_str_err_files_copy_failed');
+
+		return true;
+    }
+
+    protected function getConfigModule($sConfigPath, $aInstalled = array())
+    {
+    	$aConfig = self::getModuleConfig($sConfigPath);
+        if(empty($aConfig) || !is_array($aConfig))
+            return array();
+
+        $bInstalled = !empty($aInstalled);
+        $bEnabled = $bInstalled && (int)$aInstalled['enabled'] == 1;
+        $sVersion = $bInstalled ? $aInstalled['version'] : $aConfig['version'];
+
+        return array(
+        	'type' => $aConfig['type'],
+            'name' => isset($aConfig['name']) ? $aConfig['name'] : $aConfig['home_uri'],
+            'title' => bx_process_output($aConfig['title']),
+            'vendor' => $aConfig['vendor'],
+            'version' => $sVersion,
+            'uri' => $aConfig['home_uri'],
+            'dir' => $aConfig['home_dir'],
+            'note' => isset($aConfig['note']) ? bx_process_output($aConfig['note']) : '',
+            'installed' => $bInstalled,
+            'enabled' => $bEnabled
+        );
+    }
+
+    protected function getConfigUpdate($sConfigPathModule, $sConfigPathUpdate, $aInstalledModule = array())
+    {
+        $aModule = $this->getConfigModule($sConfigPathModule, $aInstalledModule);
+        if(empty($aModule) || !$aModule['installed'])
+            return array();
+
+		$aConfig = self::getModuleConfig($sConfigPathUpdate);
+        if(empty($aConfig) || !is_array($aConfig))
+            return array();
+
+        return array(
+            'title' => bx_process_output($aConfig['title']),
+            'vendor' => $aConfig['vendor'],
+            'version_from' => $aConfig['version_from'],
+            'version_to' => $aConfig['version_to'],
+            'dir' => $aConfig['home_dir'],
+        	'module_type' => $aModule['type'],
+            'module_name' => $aModule['name'],
+            'module_dir' => $aModule['dir'],
+        	'module_version' => $aModule['version']
+        );
+    }
+
+    private function getInstalledInfo()
+    {
+        bx_import('BxDolModuleQuery');
+        $aModules = BxDolModuleQuery::getInstance()->getModules();
+
+        $aInstalledInfo = array();
+        foreach($aModules as $aModule)
+            $aInstalledInfo[$aModule['path']] = $aModule;
+
+        return $aInstalledInfo;
+    }
+
+    private function addTransientJob($sName, $sAction, $aParams)
+    {
+		bx_import('BxDolCronQuery');
+		if(BxDolCronQuery::getInstance()->addTransientJobService($sName, array('system', $sAction, $aParams, 'DolStudioInstallerUtils')))
+			return true;
+
+		return false;
+    }
+
+    private function emailNotify($sMessage)
+    {
+    	sendMailTemplateSystem('t_BgOperationFailed', array (
+			'conclusion' => strip_tags($sMessage),
+		));
+    }
+}
+
+/** @} */

+ 100 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioOAuth.php

@@ -0,0 +1,100 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxDolSession');
+bx_import('BxDolStudioInstallerUtils');
+
+define('BX_DOL_OAUTH_URL_BASE', BX_DOL_UNITY_URL_ROOT . 'scripts_public/');
+define('BX_DOL_OAUTH_URL_REQUEST_TOKEN', BX_DOL_OAUTH_URL_BASE . 'oauth_request_token.php5');
+define('BX_DOL_OAUTH_URL_AUTHORIZE', BX_DOL_OAUTH_URL_BASE . 'oauth_authorize.php5');
+define('BX_DOL_OAUTH_URL_ACCESS_TOKEN', BX_DOL_OAUTH_URL_BASE . 'oauth_access_token.php5');
+define('BX_DOL_OAUTH_URL_FETCH_DATA', BX_DOL_OAUTH_URL_BASE . 'oauth_fetch_data.php5');
+
+class BxDolStudioOAuth extends BxDol
+{
+    protected $oSession;
+
+	protected $sErrorCode;
+    protected $sErrorMessage;
+
+	protected $sKey;
+    protected $sSecret;
+    protected $sDataRetrieveMethod;
+
+    public function __construct()
+    {
+        parent::__construct ();
+
+        $this->oSession = BxDolSession::getInstance();
+
+        $this->sErrorCode = 'oauth_err_code';
+        $this->sErrorMessage = 'oauth_err_message';
+    }
+
+    static function isAuthorizedClient()
+    {
+        return (int)BxDolSession::getInstance()->getValue('sys_oauth_authorized') == 1;
+    }
+
+    static function getAuthorizedClient()
+    {
+        return (int)BxDolSession::getInstance()->getValue('sys_oauth_authorized_user');
+    }
+
+    public function loadItems($aParams = array())
+    {
+        if(empty($this->sKey) || empty($this->sSecret))
+            return _t('_adm_err_oauth_empty_key_secret');
+
+        $mixedResult = $this->authorize();
+        if($mixedResult !== true)
+            return $mixedResult;
+
+        $aItems = $this->fetch($aParams);
+        if(is_null($aItems))
+            return _t('_adm_err_oauth_cannot_read_answer');
+        else if(empty($aItems))
+            return MsgBox(_t('_Empty'));
+
+        return $aItems;
+    }
+
+    public function doAuthorize()
+    {
+    	if(empty($this->sKey) || empty($this->sSecret))
+            return _t('_adm_err_oauth_empty_key_secret');
+
+        return $this->authorize();
+    }
+
+    protected function isAuthorized()
+    {
+        return self::isAuthorizedClient();
+    }
+
+    protected function getAuthorizedUser()
+    {
+    	return self::getAuthorizedClient();
+    }
+
+	protected function unsetAuthorizedUser()
+	{
+		$this->oSession->unsetValue('sys_oauth_token');
+		$this->oSession->unsetValue('sys_oauth_secret');
+        $this->oSession->unsetValue('sys_oauth_authorized');
+		$this->oSession->unsetValue('sys_oauth_authorized_user');
+	}
+
+    protected function isServerError($aResult)
+    {
+        return isset($aResult[$this->sErrorCode]) && isset($aResult[$this->sErrorMessage]);
+    }
+}
+
+/** @} */

+ 138 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioOAuthLib.php

@@ -0,0 +1,138 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxDolStudioOAuth');
+
+class BxDolStudioOAuthLib extends BxDolStudioOAuth implements iBxDolSingleton
+{
+    public function __construct()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
+
+        parent::__construct();
+
+        $this->sKey = getParam('sys_oauth_key');
+        $this->sSecret = getParam('sys_oauth_secret');
+        $this->sDataRetrieveMethod = OAUTH_HTTP_METHOD_POST;
+    }
+
+    public function __clone()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
+    }
+
+    public static function getInstance()
+    {
+        if (!isset($GLOBALS['bxDolClasses'][__CLASS__])) {
+            $sClass = __CLASS__;
+            $GLOBALS['bxDolClasses'][__CLASS__] = new $sClass();
+        }
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    protected function authorize()
+    {
+        if($this->isAuthorized())
+            return true;
+
+		$oConsumer = $this->getServiceObject(false);
+
+        try {
+            $bToken = bx_get('oauth_token') !== false;
+            $mixedSecret = $this->oSession->getValue('sys_oauth_secret');
+            if(!$bToken && $mixedSecret !== false) {
+                $this->oSession->unsetValue('sys_oauth_secret');
+                $mixedSecret = false;
+            }
+
+            //--- Get request token and redirect to authorize.
+            if(!$bToken && $mixedSecret === false)
+                return $this->getRequestToken($oConsumer);
+
+            //--- Get access token.
+            if($bToken && $mixedSecret !== false)
+                return $this->getAccessToken(bx_get('oauth_token'), $mixedSecret, bx_get('oauth_verifier'), (int)bx_get('oauth_user'),  $oConsumer);
+        }
+        catch(OAuthException $e) {
+            return $this->getRequestToken();
+        }
+
+        return $this->getRequestToken();
+    }
+
+    protected function getRequestToken($oConsumer = null)
+    {
+    	if(empty($oConsumer))
+    		$oConsumer = $this->getServiceObject();
+
+    	$aRequestToken = $oConsumer->getRequestToken(BX_DOL_OAUTH_URL_REQUEST_TOKEN);
+		if(empty($aRequestToken))
+        	return _t('_adm_err_oauth_cannot_get_token');
+
+		if($this->isServerError($aRequestToken))
+			throw new OAuthException();
+
+		$this->oSession->setValue('sys_oauth_secret', $aRequestToken['oauth_token_secret']);
+		return _t('_adm_msg_oauth_need_authorize', bx_append_url_params(BX_DOL_OAUTH_URL_AUTHORIZE, array('oauth_token' => $aRequestToken['oauth_token'], 'sid' => bx_site_hash())));
+    }
+
+    protected function getAccessToken($sToken, $mixedSecret, $sVerifier, $iUser,  $oConsumer)
+    {
+		$oConsumer->setToken($sToken, $mixedSecret);
+		$aAccessToken = $oConsumer->getAccessToken(bx_append_url_params(BX_DOL_OAUTH_URL_ACCESS_TOKEN, array('oauth_verifier' => $sVerifier)));
+		if(empty($aAccessToken))
+			return _t('_adm_err_oauth_cannot_get_token');
+
+		if($this->isServerError($aAccessToken))
+			throw new OAuthException();
+
+		$this->oSession->setValue('sys_oauth_token', $aAccessToken['oauth_token']);
+		$this->oSession->setValue('sys_oauth_secret', $aAccessToken['oauth_token_secret']);
+		$this->oSession->setValue('sys_oauth_authorized', 1);
+		$this->oSession->setValue('sys_oauth_authorized_user', $iUser);
+
+		return true;
+    }
+    
+    protected function fetch($aParams = array())
+    {
+        if(!$this->isAuthorized())
+            return array();
+
+		$oConsumer = $this->getServiceObject(false);
+
+        try {
+            $oConsumer->setToken($this->oSession->getValue('sys_oauth_token'), $this->oSession->getValue('sys_oauth_secret'));
+            $oConsumer->fetch(BX_DOL_OAUTH_URL_FETCH_DATA, $aParams, $this->sDataRetrieveMethod);
+
+            //echo $oConsumer->getLastResponse(); exit;	//--- Uncomment to debug
+            return json_decode($oConsumer->getLastResponse(), true);
+        } 
+        catch(OAuthException $e) {
+            return array();
+        }
+    }
+
+	private function getServiceObject($bClearStorage = true)
+    {
+    	if($bClearStorage)
+    		$this->unsetAuthorizedUser();
+
+    	$oConsumer = new OAuth($this->sKey, $this->sSecret);
+		$oConsumer->setAuthType(OAUTH_AUTH_TYPE_URI);
+        $oConsumer->enableDebug();
+
+        return $oConsumer;
+    }
+}
+
+/** @} */

+ 151 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioOAuthPlugin.php

@@ -0,0 +1,151 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+use OAuth\ServiceFactory;
+use OAuth\OAuth1\Service\Dolphin;
+use OAuth\Common\Http\Uri\Uri;
+use OAuth\Common\Http\Exception\TokenResponseException;
+use OAuth\Common\Storage\Session;
+use OAuth\Common\Storage\Exception\TokenNotFoundException;
+use OAuth\Common\Consumer\Credentials;
+
+bx_import('BxDolStudioOAuth');
+
+require_once (BX_DIRECTORY_PATH_PLUGINS . 'OAuth/bootstrap.php');
+
+class BxDolStudioOAuthPlugin extends BxDolStudioOAuth implements iBxDolSingleton
+{
+    protected $sService;
+    protected $oStorage;
+
+    public function __construct()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
+
+        parent::__construct ();
+
+        $this->sKey = getParam('sys_oauth_key');
+        $this->sSecret = getParam('sys_oauth_secret');
+        $this->sDataRetrieveMethod = 'POST';
+
+        $this->sService = 'Dolphin';
+    }
+
+    public function __clone()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
+    }
+
+    public static function getInstance()
+    {
+        if (!isset($GLOBALS['bxDolClasses'][__CLASS__])) {
+            $sClass = __CLASS__;
+            $GLOBALS['bxDolClasses'][__CLASS__] = new $sClass();
+        }
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    protected function authorize()
+    {
+		if($this->isAuthorized())
+			return true;
+
+		$oService = $this->getServiceObject();
+
+        try {
+            $bToken = bx_get('oauth_token') !== false;
+            $mixedSecret = $this->oSession->getValue('sys_oauth_secret');
+            if(!$bToken && $mixedSecret !== false) {
+                $this->oSession->unsetValue('sys_oauth_secret');
+                $mixedSecret = false;
+            }
+
+            //--- Get request token and redirect to authorize.
+            if(!$bToken && $mixedSecret === false)
+            	return $this->getRequestToken($oService);
+
+            //--- Get access token.
+            if($bToken && $mixedSecret !== false)
+            	return $this->getAccessToken(bx_get('oauth_token'), bx_get('oauth_verifier'), (int)bx_get('oauth_user'), $oService);
+        }
+        catch(TokenResponseException $e) {
+        	$this->unsetAuthorizedUser();
+            return $this->getRequestToken();
+        }
+        catch(TokenNotFoundException $e) {
+        	$this->unsetAuthorizedUser();
+            return $this->getRequestToken();
+        }
+
+        return $this->getRequestToken();
+    }
+
+    protected function getRequestToken($oService = null)
+    {
+    	if(empty($oService))
+    		$oService = $this->getServiceObject();
+
+		$oToken = $oService->requestRequestToken();
+		if(empty($oToken))
+			return _t('_adm_err_oauth_cannot_get_token');
+
+		$this->oSession->setValue('sys_oauth_secret', $oToken->getRequestTokenSecret());
+
+		$oUrl = $oService->getAuthorizationUri(array('oauth_token' => $oToken->getRequestToken()));
+		return _t('_adm_msg_oauth_need_authorize', bx_append_url_params($oUrl, array('sid' => bx_site_hash())));
+    }
+
+    protected function getAccessToken($sToken, $sVerifier, $iUser,  $oService)
+    {
+		$oToken = $this->oStorage->retrieveAccessToken($this->sService);
+		$oAccessToken = $oService->requestAccessToken($sToken, $sVerifier, $oToken->getRequestTokenSecret());
+		if(empty($oAccessToken))
+			return _t('_adm_err_oauth_cannot_get_token');
+
+		$this->oSession->setValue('sys_oauth_token', $oAccessToken->getAccessToken());
+		$this->oSession->setValue('sys_oauth_secret', $oAccessToken->getAccessTokenSecret());
+		$this->oSession->setValue('sys_oauth_authorized', 1);
+		$this->oSession->setValue('sys_oauth_authorized_user', $iUser);
+
+		return true;
+    }
+
+    protected function fetch($aParams = array())
+    {
+        if(!$this->isAuthorized())
+            return array();
+
+        try {
+			$sResponse = $this->getServiceObject()->request(BX_DOL_OAUTH_URL_FETCH_DATA, $this->sDataRetrieveMethod, $aParams);
+
+            //echo $sResponse; exit;	//--- Uncomment to debug
+            return json_decode($sResponse, true);
+        }
+    	catch(TokenNotFoundException $e) {
+        	$this->unsetAuthorizedUser();
+            return $this->getRequestToken();
+        }
+    }
+
+    private function getServiceObject()
+    {
+	 	$this->oStorage = new Session();
+
+    	$oUrl = new Uri(BX_DOL_URL_STUDIO . 'store.php?page=purchases');
+		$oCredentials = new Credentials($this->sKey, $this->sSecret, $oUrl->getAbsoluteUri());
+
+    	$oServiceFactory = new ServiceFactory();
+		return $oServiceFactory->createService($this->sService, $oCredentials, $this->oStorage);
+    }
+}
+
+/** @} */

+ 338 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioStore.php

@@ -0,0 +1,338 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxTemplStudioPage');
+bx_import('BxDolStudioStoreQuery');
+bx_import('BxDolStudioInstallerUtils');
+
+define('BX_DOL_STUDIO_STR_TYPE_DEFAULT', 'downloaded');
+
+class BxDolStudioStore extends BxTemplStudioPage
+{
+    protected $sPage;
+    protected $aContent;
+
+    protected $iClient;
+
+    protected $aAlias;
+    protected $bAuthAccessUpdates;
+
+    function __construct($sPage = "")
+    {
+        parent::__construct('store');
+
+        $this->oDb = new BxDolStudioStoreQuery();
+
+        $this->aAlias = array(
+            'tag' => array(
+                'modules' => 'extensions',
+                'languages' => 'translations'
+            ),
+            'category' => array()
+        );
+        $this->bAuthAccessUpdates = false;
+
+        $this->sPage = BX_DOL_STUDIO_STR_TYPE_DEFAULT;
+        if(is_string($sPage) && !empty($sPage))
+            $this->sPage = $sPage;
+
+		bx_import('BxDolStudioOAuth');
+        $this->iClient = BxDolStudioOAuth::getAuthorizedClient();
+
+        //--- Check actions ---//
+        if(($sAction = bx_get('str_action')) !== false) {
+            $sAction = bx_process_input($sAction);
+
+            $aResult = array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => _t('_adm_mod_err_cannot_process_action'));
+            switch($sAction) {
+                case 'get-file':
+                    $iFileId = (int)bx_get('str_id');
+                    $aResult = $this->getFile($iFileId);
+                    break;
+
+                case 'get-product':
+                    $sModuleName = bx_process_input(bx_get('str_id'));
+                    $aResult = $this->getProduct($sModuleName);
+                    break;
+
+                case 'get-update':
+                    $sModuleName = bx_process_input(bx_get('str_id'));
+                    $aResult = $this->getUpdate($sModuleName);
+                    break;
+
+				case 'get-update-and-install':
+                    $sModuleName = bx_process_input(bx_get('str_id'));
+                    $aResult = $this->getUpdate($sModuleName, true);
+                    break;
+
+                case 'get-products-by-type':
+                    $this->sPage = bx_process_input(bx_get('str_value'));
+
+                    $sContent = $this->getPageCode();
+                    if(!empty($sContent))
+                        $aResult = array('code' => BX_DOL_STUDIO_IU_RC_SUCCESS, 'content' => $sContent);
+                    else
+                        $aResult = array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => _t('_adm_act_err_failed_page_loading'));
+                    break;
+
+                case 'get-products-by-page':
+                    $this->sPage = bx_process_input(bx_get('str_type'));
+
+                    $sContent = $this->getPageContent();
+                    if(!empty($sContent))
+                        $aResult = array('code' => BX_DOL_STUDIO_IU_RC_SUCCESS, 'content' => $sContent);
+                    else
+                        $aResult = array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => _t('_adm_act_err_failed_page_loading'));
+                    break;
+
+                case 'add-to-cart':
+                    $sVendor = bx_process_input(bx_get('str_vendor'));
+                    $iItem = (int)bx_get('str_item');
+                    $iItemCount = 1;
+
+                    if(empty($sVendor) || empty($iItem)) {
+                        $aResult = array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => _t('_adm_err_modules_cannot_add_to_cart'));
+                        break;
+                    }
+
+                    bx_import('BxDolStudioCart');
+                    BxDolStudioCart::getInstance()->add($sVendor, $iItem, $iItemCount);
+                    $aResult = array('code' => BX_DOL_STUDIO_IU_RC_SUCCESS, 'message' => _t('_adm_msg_modules_success_added_to_cart'));
+                    break;
+
+                case 'delete-from-cart':
+                    $sVendor = bx_process_input(bx_get('str_vendor'));
+                    $iItem = (int)bx_get('str_item');
+
+                    if(empty($sVendor)) {
+                        $aResult = array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => _t('_adm_err_modules_cannot_delete_from_cart'));
+                        break;
+                    }
+
+                    bx_import('BxDolStudioCart');
+                    BxDolStudioCart::getInstance()->delete($sVendor, $iItem);
+                    $aResult = array('code' => BX_DOL_STUDIO_IU_RC_SUCCESS, 'message' => '');
+                    break;
+
+                case 'checkout-cart':
+                    $sVendor = bx_process_input(bx_get('str_vendor'));
+                    if(empty($sVendor)) {
+                        $aResult = array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => _t('_adm_err_modules_cannot_checkout_empty_vendor'));
+                        break;
+                    }
+
+                    $sLocation = $this->checkoutCart($sVendor);
+                    $aResult = array('code' => BX_DOL_STUDIO_IU_RC_SUCCESS, 'message' => '', 'redirect' => $sLocation);
+                    break;
+
+                case 'install':
+                    $sValue = bx_process_input(bx_get('str_value'));
+                    if(empty($sValue))
+                        break;
+
+                    $aResult = BxDolStudioInstallerUtils::getInstance()->perform($sValue, 'install', array('auto_enable' => true, 'html_response' => true));
+                    break;
+
+                case 'update':
+                    $sValue = bx_process_input(bx_get('str_value'));
+                    if(empty($sValue))
+                        break;
+
+                    $aResult = BxDolStudioInstallerUtils::getInstance()->perform($sValue, 'update', array('html_response' => true));
+                    break;
+
+                case 'delete':
+                    $sValue = bx_process_input(bx_get('str_value'));
+                    if(empty($sValue))
+                        break;
+
+                    $aResult = BxDolStudioInstallerUtils::getInstance()->perform($sValue, 'delete', array('html_response' => true));
+                    break;
+            }
+
+            if(!empty($aResult['message'])) {
+                bx_import('BxDolStudioTemplate');
+                $aResult['message'] = BxDolStudioTemplate::getInstance()->parseHtmlByName('mod_action_result.html', array('content' => $aResult['message']));
+
+                bx_import('BxTemplStudioFunctions');
+                $aResult['message'] = BxTemplStudioFunctions::getInstance()->transBox('', $aResult['message']);
+            }
+
+            echo json_encode($aResult);
+            exit;
+        }
+    }
+
+    protected function loadGoodies()
+    {
+        $iPerPage = 5;
+        $aProducts = array();
+        $sJsObject = $this->getPageJsObject();
+
+        bx_import('BxDolStudioJson');
+        $oJson = BxDolStudioJson::getInstance();
+
+        // Load featured
+        $aProducts[] = array(
+            'caption' => '_adm_block_cpt_last_featured',
+            'actions' => array(
+                array('name' => 'featured', 'caption' => '_adm_action_cpt_see_all_featured', 'url' => 'javascript:void(0)', 'onclick' => $sJsObject . ".changePage('featured', this)")
+            ),
+            'items' => $oJson->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_featured', array('start' => 0, 'per_page' => $iPerPage, 'client' => $this->iClient))
+        );
+
+
+        // Load modules
+        $aProducts[] = array(
+            'caption' => '_adm_block_cpt_last_modules',
+            'actions' => array(
+                array('name' => 'modules', 'caption' => '_adm_action_cpt_see_all_modules', 'url' => 'javascript:void(0)', 'onclick' => $sJsObject . ".changePage('modules', this)")
+            ),
+            'items' => $oJson->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_by_tag', array('value' => 'extensions', 'start' => 0, 'per_page' => $iPerPage, 'client' => $this->iClient))
+        );
+
+        // Load templates
+        $aProducts[] = array(
+            'caption' => '_adm_block_cpt_last_templates',
+            'actions' => array(
+                array('name' => 'templates', 'caption' => '_adm_action_cpt_see_all_templates', 'url' => 'javascript:void(0)', 'onclick' => $sJsObject . ".changePage('templates', this)")
+            ),
+            'items' => $oJson->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_by_tag', array('value' => 'templates', 'start' => 0, 'per_page' => $iPerPage, 'client' => $this->iClient))
+        );
+
+        // Load languages
+        $aProducts[] = array(
+            'caption' => '_adm_block_cpt_last_languages',
+            'actions' => array(
+                array('name' => 'languages', 'caption' => '_adm_action_cpt_see_all_languages', 'url' => 'javascript:void(0)', 'onclick' => $sJsObject . ".changePage('languages', this)")
+            ),
+            'items' => $oJson->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_by_tag', array('value' => 'translations', 'start' => 0, 'per_page' => $iPerPage, 'client' => $this->iClient))
+        );
+
+        return $aProducts;
+    }
+
+    protected function loadFeatured($iStart, $iPerPage)
+    {
+        bx_import('BxDolStudioJson');
+        return BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_featured', array('start' => $iStart, 'per_page' => $iPerPage, 'client' => $this->iClient));
+    }
+
+    protected function loadTag($sTag, $iStart, $iPerPage)
+    {
+        bx_import('BxDolStudioJson');
+        return BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_by_tag', array('value' => $this->aliasToNameTag($sTag), 'start' => $iStart, 'per_page' => $iPerPage, 'client' => $this->iClient));
+    }
+
+    protected function loadPurchases()
+    {
+        return BxDolStudioInstallerUtils::getInstance()->checkModules(true);
+    }
+
+    protected function loadUpdates()
+    {
+        return BxDolStudioInstallerUtils::getInstance()->checkUpdates($this->bAuthAccessUpdates);
+    }
+
+    protected function loadCheckout()
+    {
+        bx_import('BxDolStudioJson');
+        $oJson = BxDolStudioJson::getInstance();
+
+        bx_import('BxDolStudioCart');
+        $aVendors = BxDolStudioCart::getInstance()->parseByVendor();
+
+        $aResult = array();
+        foreach($aVendors as $sVendor => $aItems) {
+            $aIds = $aCounts = array();
+            foreach($aItems as $aItem) {
+                $aIds[] = $aItem['item_id'];
+                $aCounts[$aItem['item_id']] = $aItem['item_count'];
+            }
+
+            $aProducts = $oJson->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_selected', array('products' => base64_encode(serialize($aIds))));
+            if(!empty($aProducts))
+                $aResult[$sVendor] = array(
+                    'ids' => $aIds,
+                    'counts' => $aCounts,
+                    'products' => $aProducts
+                );
+        }
+
+        return $aResult;
+    }
+
+    protected function loadDownloaded()
+    {
+        $oInstallerUtils = BxDolStudioInstallerUtils::getInstance();
+
+        return array(
+            'modules' => $oInstallerUtils->getModules(),
+            'updates' => $oInstallerUtils->getUpdates()
+        );
+    }
+
+    protected function loadProduct($sModuleName)
+    {
+        bx_import('BxDolStudioJson');
+        $oJson = BxDolStudioJson::getInstance();
+
+        return $oJson->load(BX_DOL_UNITY_URL_MARKET . 'json_get_product_by_name', array('value' => $sModuleName, 'client' => $this->iClient));
+    }
+
+    /*
+     * Load package (module, update) using OAuth authorization.
+     */
+    protected function loadFile($iFileId)
+    {
+        return BxDolStudioInstallerUtils::getInstance()->downloadFileAuthorized($iFileId);
+    }
+
+    /*
+     * Load update's package publicly.
+     */
+	protected function loadUpdate($sModuleName, $bAutoUpdate = false)
+    {
+        return BxDolStudioInstallerUtils::getInstance()->downloadUpdatePublic($sModuleName, $bAutoUpdate);
+    }
+
+    private function checkoutCart($sVendor)
+    {
+        bx_import('BxDolStudioCart');
+        $oCart = BxDolStudioCart::getInstance();
+
+        $aItems = $oCart->getByVendor($sVendor);
+        if(empty($aItems) || !is_array($aItems))
+            return false;
+
+        $aIds = array();
+        foreach($aItems as $aItem)
+            $aIds[] = $aItem['item_id'];
+
+        $sSid = bx_site_hash();
+        return BX_DOL_UNITY_URL_MARKET . 'purchase/' . $sVendor . '?sid=' . $sSid . '&products=' . base64_encode(implode(',', $aIds));
+    }
+
+    private function aliasToNameTag($sAlias)
+    {
+        return $this->aliasToName('tag', $sAlias);
+    }
+
+    private function aliasToNameCategory($sAlias)
+    {
+        return $this->aliasToName('category', $sAlias);
+    }
+
+    private function aliasToName($sType, $sAlias)
+    {
+        return isset($this->aAlias[$sType][$sAlias]) ? $this->aAlias[$sType][$sAlias] : $sAlias;
+    }
+}
+
+/** @} */

+ 39 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioStoreQuery.php

@@ -0,0 +1,39 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxDolStudioModulesQuery');
+
+class BxDolStudioStoreQuery extends BxDolStudioModulesQuery
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    public function isQueued($sAction, $sName)
+    {
+    	bx_import('BxDolStudioInstallerUtils');
+
+    	$sJobName = '';
+    	switch($sAction) {
+    		case 'download':
+    			$sJobName = BxDolStudioInstallerUtils::getNameDownloadFile($sName);
+    			break;
+
+    		case 'action':
+    			$sJobName = BxDolStudioInstallerUtils::getNamePerformAction($sName);
+    			break;
+    	}
+
+    	$sSql = $this->prepare("SELECT `id` FROM `sys_cron_jobs` WHERE name=? LIMIT 1", $sJobName);
+    	return (int)$this->getOne($sSql) > 0;
+    }
+}
+
+/** @} */

+ 648 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioToolsAudit.php

@@ -0,0 +1,648 @@
+<?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+define('BX_DOL_AUDIT_FAIL', 'fail');
+define('BX_DOL_AUDIT_WARN', 'warn');
+define('BX_DOL_AUDIT_UNDEF', 'undef');
+define('BX_DOL_AUDIT_OK', 'ok');
+
+class BxDolStudioToolsAudit extends BxDol
+{
+    protected $aType2ClassCSS;
+    protected $aType2Title;
+
+    protected $aLatestPhp;
+    protected $sMinPhpVer;
+    protected $aPhpSettings;
+    protected $iPhpErrorReporting;
+
+    protected $sMinMysqlVer;
+    protected $aMysqlOptimizationSettings;
+
+    protected $aDolphinOptimizationSettings;
+
+    protected $aRequiredApacheModules;
+
+    function __construct()
+    {
+        parent::__construct();
+
+        $this->aType2ClassCSS = array (
+            BX_DOL_AUDIT_FAIL => 'fail',
+            BX_DOL_AUDIT_WARN => 'warn',
+            BX_DOL_AUDIT_UNDEF => 'undef',
+            BX_DOL_AUDIT_OK => 'ok',
+        );
+
+        $this->aType2Title = array (
+            BX_DOL_AUDIT_FAIL => _t('_sys_audit_title_fail'),
+            BX_DOL_AUDIT_WARN => _t('_sys_audit_title_warn'),
+            BX_DOL_AUDIT_UNDEF => _t('_sys_audit_title_undef'),
+            BX_DOL_AUDIT_OK => _t('_sys_audit_title_ok'),
+        );
+
+        $this->aLatestPhp = array(
+            '5.3' => '5.3.29',
+            '5.4' => '5.4.34',
+            '5.5' => '5.5.18',
+        );
+        $this->sMinPhpVer = '5.3.0';
+        $this->aPhpSettings = array (
+            'allow_url_fopen' => array('op' => '=', 'val' => true, 'type' => 'bool'),
+            'allow_url_include' => array('op' => '=', 'val' => false, 'type' => 'bool'),
+            'magic_quotes_gpc' => array('op' => '=', 'val' => false, 'type' => 'bool', 'warn' => 1),
+            'memory_limit' => array('op' => '>=', 'val' => 128*1024*1024, 'type' => 'bytes', 'unlimited' => -1),
+            'post_max_size' => array('op' => '>=', 'val' => 2*1024*1024, 'type' => 'bytes', 'warn' => 1),
+            'upload_max_filesize' => array('op' => '>=', 'val' => 2*1024*1024, 'type' => 'bytes', 'warn' => 1),
+            'register_globals' => array('op' => '=', 'val' => false, 'type' => 'bool'),
+            'safe_mode' => array('op' => '=', 'val' => false, 'type' => 'bool'),
+            'short_open_tag' => array('op' => '=', 'val' => true, 'type' => 'bool'),
+            'disable_functions' => array('op' => '=', 'val' => ''),
+            'php module: mysql' => array('op' => 'module', 'val' => 'mysql'),
+            'php module: curl' => array('op' => 'module', 'val' => 'curl'),
+            'php module: gd' => array('op' => 'module', 'val' => 'gd'),
+            'php module: mbstring' => array('op' => 'module', 'val' => 'mbstring'),
+            'php module: json' => array('op' => 'module', 'val' => 'json', 'warn' => 1),
+            'php module: openssl' => array('op' => 'module', 'val' => 'openssl', 'warn' => 1),
+            'php module: zip' => array('op' => 'module', 'val' => 'zip', 'warn' => 1),
+        );
+
+        $this->sMinMysqlVer = '4.1.2';
+        $this->aMysqlOptimizationSettings = array (
+            'key_buffer_size' => array('op' => '>=', 'val' => 128*1024, 'type' => 'bytes'),
+            'query_cache_limit' => array('op' => '>=', 'val' => 1000000),
+            'query_cache_size' => array('op' => '>=', 'val' => 16*1024*1024, 'type' => 'bytes'),
+            'query_cache_type' => array('op' => 'strcasecmp', 'val' => 'on'),
+            'max_heap_table_size' => array('op' => '>=', 'val' => 16*1024*1024, 'type' => 'bytes'),
+            'tmp_table_size' => array('op' => '>=', 'val' => 16*1024*1024, 'type' => 'bytes'),
+            'thread_cache_size ' => array('op' => '>', 'val' => 0),
+        );
+
+        $this->aDolphinOptimizationSettings = array (
+            'DB cache' => array('enabled' => 'sys_db_cache_enable', 'cache_engine' => 'sys_db_cache_engine', 'check_accel' => true),
+            'Page blocks cache' => array('enabled' => 'sys_pb_cache_enable', 'cache_engine' => 'sys_pb_cache_engine', 'check_accel' => true),
+            'Member menu cache' => array('enabled' => 'always_on', 'cache_engine' => 'sys_mm_cache_engine', 'check_accel' => true),
+            'Templates Cache' => array('enabled' => 'sys_template_cache_enable', 'cache_engine' => 'sys_template_cache_engine', 'check_accel' => true),
+            'CSS files cache' => array('enabled' => 'sys_template_cache_css_enable', 'cache_engine' => '', 'check_accel' => false),
+            'JS files cache' => array('enabled' => 'sys_template_cache_js_enable', 'cache_engine' => '', 'check_accel' => false),
+            'Compression for CSS/JS cache' => array('enabled' => 'sys_template_cache_compress_enable', 'cache_engine' => '', 'check_accel' => false),
+        );
+
+        $this->aRequiredApacheModules = array (
+            'rewrite_module' => 'mod_rewrite',
+        );
+
+        if (isset($_GET['action'])) {
+            $sOutput = null;
+            switch ($_GET['action']) {
+                case 'audit_send_test_email':
+                    $sOutput = $this->sendTestEmail();
+                    break;
+                case 'phpinfo':
+                    ob_start();
+                    phpinfo();
+                    $sOutput = ob_get_clean();
+                    break;
+                case 'phpinfo_popup':
+                    $sUrlSelf = bx_js_string($_SERVER['PHP_SELF'], BX_ESCAPE_STR_APOS);
+                    $sUrlSelf = bx_append_url_params($sUrlSelf, array('action' => 'phpinfo'));
+                    $sOutput = '<iframe width="640" height="480" src="' . $sUrlSelf . '"></iframe>';
+                    break;
+            }
+            if ($sOutput) {
+                header('Content-type: text/html; charset=utf-8');
+                echo $sOutput;
+                exit;
+            }
+        }
+    }
+
+    public function generate()
+    {
+        ob_start();
+
+        $this->setErrorReporting();
+
+        $this->generateStyles();
+        $this->generateJs();
+
+        $this->requirements();
+
+        if (!defined('BX_DOL_INSTALL'))
+            $this->siteSetup();
+
+        $this->optimization();
+
+        $this->manualCheck();
+
+        $this->restoreErrorReporting();
+
+        return ob_get_clean();
+    }
+
+    public function generateStyles($bReturn = false)
+    {
+    	ob_start();
+        ?>
+<style>
+    .ok {
+        color:green;
+    }
+    .fail {
+        color:red;
+    }
+    .warn {
+        color:orange;
+    }
+    .undef {
+        color:gray;
+    }
+</style>
+        <?php
+        $sStyles = ob_get_clean();
+
+        if($bReturn)
+			return $sStyles;
+
+		echo $sStyles;
+    }
+
+    public function generateJs()
+    {
+        $sUrlSelf = bx_js_string($_SERVER['PHP_SELF'], BX_ESCAPE_STR_APOS);
+        ?>
+        <script language="javascript">
+            function bx_sys_adm_audit_test_email()
+            {
+                var sEmail = prompt('<?php echo _t('_Email'); ?>', '<?php echo class_exists('BxDolDb') && BxDolDb::getInstance() ? BxDolDb::getInstance()->getParam('site_email') : ''; ?>');
+                if (null == sEmail || ('string' == (typeof sEmail) && !sEmail.length))
+                    return;
+
+                $('#bx-sys-adm-audit-test-email').html('Sending...');
+                $.post('<?php echo bx_append_url_params($sUrlSelf, array('action' => 'audit_send_test_email')); ?>&email=' + sEmail, function(data) {
+                    $('#bx-sys-adm-audit-test-email').html(data);
+                });
+            }
+
+            function bx_sys_adm_audit_phpinfo()
+            {
+                $(window).dolPopupAjax({url: '<?php echo bx_append_url_params($sUrlSelf, array('action' => 'phpinfo_popup')); ?>'});
+            }
+        </script>
+        <?php
+    }
+
+    /**
+     * Check minimal Dolphin requirements
+     * @param $sType - BX_DOL_AUDIT_FAIL, BX_DOL_AUDIT_WARN, BX_DOL_AUDIT_UNDEF or BX_DOL_AUDIT_OK
+     * @param $sFunc - requirementsPHP, requirementsMySQL or requirementsWebServer
+     * @return array of FAIL, WARN, UNDEF or OK items, if array is empty then no specified items are found
+     */
+    public function checkRequirements($sType = BX_DOL_AUDIT_FAIL, $sFunc = 'requirementsPHP')
+    {
+        $this->setErrorReporting();
+
+        $aRet = array ();        
+        $aMessages = array ();  
+
+        $this->$sFunc(false, $aMessages);
+
+        foreach ($aMessages as $sName => $r)
+            if ($sType == $r['type'])
+                $aRet[] = "$sName = " . $this->format_output($r['params']['real_val'], isset($this->aPhpSettings[$sName]) ? $this->aPhpSettings[$sName] : '') . " - " . $this->getMsgHTML($sName, $r);        
+
+        $this->restoreErrorReporting();
+
+        return $aRet;
+    }
+
+    public function typeToTitle ($sType) 
+    {
+        return $this->aType2Title[$sType];
+    }
+
+    protected function requirements()
+    {
+        echo '<h1>' . _t('_sys_audit_header_requirements') . '</h1>';
+        $this->requirementsPHP();
+        if (!defined('BX_DOL_INSTALL'))
+            $this->requirementsMySQL();
+        $this->requirementsWebServer();
+        $this->requirementsOS();
+        $this->requirementsHardware();
+    }
+
+    protected function requirementsPHP($bEcho = true, &$aOutputMessages = null)
+    {
+        $a = unserialize(file_get_contents("http://www.php.net/releases/index.php?serialize=1"));
+        $sLatestPhpVersion = $a[5]['version'];
+
+        if (version_compare(phpversion(), "5.4", ">=") == 1)
+            unset($this->aPhpSettings['short_open_tag']);
+
+        $aMessages = array ();
+
+        $sPhpVer = PHP_VERSION;
+        if (empty($sLatestPhpVersion))
+            $aVer = array('type' => BX_DOL_AUDIT_UNDEF, 'msg' => _t('_sys_audit_msg_value_checking_failed'), 'params' => array ('real_val' => $sPhpVer));
+        elseif (version_compare($sPhpVer, $this->sMinPhpVer, '<'))
+            $aVer = array('type' => BX_DOL_AUDIT_FAIL, 'msg' => _t('_sys_audit_msg_version_is_incompatible', $this->sMinPhpVer), 'params' => array ('real_val' => $sPhpVer));
+        elseif (version_compare($sPhpVer, '5.6.0', '>=') && version_compare($sPhpVer, '6.0.0', '<') && !version_compare($sPhpVer, $sLatestPhpVersion, '>='))
+            $aVer = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_version_is_outdated', $sLatestPhpVersion), 'params' => array ('real_val' => $sPhpVer));
+        elseif (version_compare($sPhpVer, '5.5.0', '>=') && version_compare($sPhpVer, '5.6.0', '<') && !version_compare($sPhpVer, $this->aLatestPhp['5.5'], '>='))
+            $aVer = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_version_is_outdated', $this->aLatestPhp['5.5']), 'params' => array ('real_val' => $sPhpVer));
+        elseif (version_compare($sPhpVer, '5.4.0', '>=') && version_compare($sPhpVer, '5.5.0', '<') && !version_compare($sPhpVer, $this->aLatestPhp['5.4'], '>='))
+            $aVer = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_version_is_outdated', $this->aLatestPhp['5.4']), 'params' => array ('real_val' => $sPhpVer));
+        elseif (version_compare($sPhpVer, '5.3.0', '>=') && version_compare($sPhpVer, '5.4.0', '<') && !version_compare($sPhpVer, $this->aLatestPhp['5.3'], '>='))
+            $aVer = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_version_is_outdated', $this->aLatestPhp['5.3']), 'params' => array ('real_val' => $sPhpVer));
+        else
+            $aVer = array('type' => BX_DOL_AUDIT_OK, 'params' => array ('real_val' => $sPhpVer));
+        $aMessages[_t('_sys_audit_version')] = $aVer;
+
+        foreach ($this->aPhpSettings as $sName => $r) {
+            $a = $this->checkPhpSetting($sName, $r);
+            if ($a['res'])
+                $aMessages[$sName] = array('type' => BX_DOL_AUDIT_OK, 'params' => $a);
+            elseif (isset($r['warn']) && $r['warn'])
+                $aMessages[$sName] = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_should_be', $r['op'], $this->format_output($r['val'], $r)), 'params' => $a);
+            else
+                $aMessages[$sName] = array('type' => BX_DOL_AUDIT_FAIL, 'msg' => _t('_sys_audit_msg_must_be', $r['op'], $this->format_output($r['val'], $r)), 'params' => $a);
+        }
+
+        if (null !== $aOutputMessages)
+            $aOutputMessages = $aMessages;
+
+        if ($bEcho) {
+            $s = '';
+            foreach ($aMessages as $sName => $r) {
+                $s .= $this->getBlock($sName, $this->format_output($r['params']['real_val'], isset($this->aPhpSettings[$sName]) ? $this->aPhpSettings[$sName] : ''), $this->getMsgHTML($sName, $r));
+            }
+            echo $this->getSection('PHP', '', $s);
+        }
+    }
+
+    protected function requirementsMySQL($bEcho = true, &$aOutputMessages = null)
+    {
+        $sMysqlVer = BxDolDb::getInstance()->getServerInfo();
+        if (preg_match ('/^(\d+)\.(\d+)\.(\d+)/', $sMysqlVer, $m)) {
+            $sMysqlVer = "{$m[1]}.{$m[2]}.{$m[3]}";
+            if (version_compare($sMysqlVer, $this->sMinMysqlVer, '<'))
+                $aMessage = array('type' => BX_DOL_AUDIT_FAIL, 'msg' => _t('_sys_audit_msg_version_is_incompatible', $this->sMinMysqlVer));
+            else
+                $aMessage = array('type' => BX_DOL_AUDIT_OK);
+        } else {
+            $aMessage = array('type' => BX_DOL_AUDIT_UNDEF, 'msg' => _t('_sys_audit_msg_value_checking_failed'));
+        }
+
+        if (null !== $aOutputMessages)
+            $aOutputMessages['mysql'] = array($aMessage);
+
+        if ($bEcho) {
+            $s = $this->getBlock(_t('_sys_audit_version'), $sMysqlVer, $this->getMsgHTML(_t('_sys_audit_version'), $aMessage));
+            echo $this->getSection('MySQL', '', $s);
+        }
+    }
+
+    protected function requirementsWebServer($bEcho = true, &$aOutputMessages = null)
+    {
+        $aMessages = array();
+        foreach ($this->aRequiredApacheModules as $sName => $sNameCompiledName)
+            $aMessages[$sName] = $this->checkApacheModule($sName, $sNameCompiledName);
+
+        if (null !== $aOutputMessages)
+            $aOutputMessages = $aMessages;
+
+        if ($bEcho) {
+            $s = '';
+            foreach ($aMessages as $sName => $r) {
+                $s .= $this->getBlock($sName, '', $this->getBlock('', '', $this->getMsgHTML($sName, $r), false));
+            }
+            echo $this->getSection(_t('_sys_audit_section_webserver'), $_SERVER['SERVER_SOFTWARE'], $s);
+        }
+    }
+
+    protected function requirementsOS()
+    {
+        $s = $this->getBlock(php_uname());
+        echo $this->getSection(_t('_sys_audit_section_os'), '', $s);
+    }
+
+    protected function requirementsHardware()
+    {
+        $s = $this->getBlock(_t('_sys_audit_msg_hardware_requirements'));
+        echo $this->getSection(_t('_sys_audit_section_hardware'), '', $s);
+    }
+
+    protected function siteSetup()
+    {
+        $sDolphinPath = defined('BX_DIRECTORY_PATH_ROOT') ? BX_DIRECTORY_PATH_ROOT : BX_INSTALL_DIR_ROOT;
+
+        $sEmailToCkeckMailSending = class_exists('BxDolDb') && BxDolDb::getInstance() ? BxDolDb::getInstance()->getParam('site_email') : '';
+
+        $sLatestDolphinVer = file_get_contents("http://rss.boonex.com/");
+        if (preg_match ('#<dolphin>([\.0-9]+)</dolphin>#', $sLatestDolphinVer, $m))
+            $sLatestDolphinVer = $m[1];
+        else
+            $sLatestDolphinVer = 'undefined';
+
+        $sDolphinVer = bx_get_ver();
+        $aMessage = array('type' => BX_DOL_AUDIT_OK);
+        if (!version_compare($sDolphinVer, $sLatestDolphinVer, '>='))
+            $aMessage = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_version_is_outdated', $sLatestDolphinVer));
+
+        $s = '';
+        $s .= $this->getBlock(_t('_sys_audit_version_dolphin'), $sDolphinVer, $this->getMsgHTML(_t('_sys_audit_version_dolphin'), $aMessage));
+        //$s .= $this->getBlock(_t('_sys_audit_permissions'), '', _t('_sys_audit_msg_permissions'));
+        //$s .= $this->getBlock('ffmpeg', '', _t('_sys_audit_msg_ffmpeg', `{$sDolphinPath}flash/modules/global/app/ffmpeg.exe 2>&1`));
+        $s .= $this->getBlock(_t('_sys_audit_mail_sending'), '', _t('_sys_audit_msg_mail_sending'));
+        $s .= $this->getBlock(_t('_sys_audit_cron_jobs'), '', _t('_sys_audit_msg_cron_jobs', `crontab -l 2>&1`));
+
+        echo '<h1>' . _t('_sys_audit_header_site_setup') . '</h1>';
+        echo "<ul>$s</ul>";
+    }
+
+    protected function optimization()
+    {
+        echo '<h1>' . _t('_sys_audit_header_site_optimization') . '</h1>';
+
+        $this->optimizationPhp();
+
+        if (!defined('BX_DOL_INSTALL'))
+            $this->optimizationMySQL();
+
+        $this->optimizationWebServer();
+
+        if (!defined('BX_DOL_INSTALL'))
+            $this->optimizationDolphin();
+    }
+
+    protected function optimizationPhp()
+    {
+        $s = '';
+        $aMessage = array();
+
+        $sAccel = $this->getPhpAccelerator();
+        if (!$sAccel)
+            $aMessage = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_php_accelerator_missing'));
+        else
+            $aMessage = array('type' => BX_DOL_AUDIT_OK);
+        $s .= $this->getBlock(_t('_sys_audit_php_accelerator'), $sAccel, $this->getMsgHTML(_t('_sys_audit_php_accelerator'), $aMessage));
+
+        $sSapi = php_sapi_name();
+        if (0 == strcasecmp('cgi', $sSapi))
+            $aMessage = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_php_setup_inefficient'));
+        else
+            $aMessage = array('type' => BX_DOL_AUDIT_OK);
+        $s .= $this->getBlock(_t('_sys_audit_php_setup'), $sSapi, $this->getMsgHTML(_t('_sys_audit_php_setup'), $aMessage));
+
+        echo $this->getSection('PHP', '', $s);
+    }
+
+    protected function optimizationMySQL()
+    {
+        $s = '';
+        $oDb = BxDolDb::getInstance();
+
+        foreach ($this->aMysqlOptimizationSettings as $sName => $r) {
+            $a = $this->checkMysqlSetting($sName, $r, $oDb);
+            $aMessage = array('type' => BX_DOL_AUDIT_OK);
+            if (!$a['res'])
+                $aMessage = array('type' => BX_DOL_AUDIT_FAIL, 'msg' => _t('_sys_audit_msg_must_be', $r['op'], $this->format_output($r['val'], $r)));
+            $s .= $this->getBlock($sName, $this->format_output($a['real_val'], $r), $this->getMsgHTML($sName, $aMessage));
+        }
+
+        echo $this->getSection('MySQL', '', $s);
+    }
+
+    protected function optimizationWebServer()
+    {
+        $s = '';
+
+        $sName = 'expires_module';
+        $sApacheModuleChack = _t('_sys_audit_msg_optimization_apache_module', $sName, $this->checkApacheModule($sName));
+        $s .= $this->getBlock(_t('_sys_audit_userside_caching'), '', _t('_sys_audit_msg_userside_caching', $this->getUrlForGooglePageSpeed('LeverageBrowserCaching'), $sApacheModuleChack));
+
+        $sName = 'deflate_module';
+        $sApacheModuleChack = _t('_sys_audit_msg_optimization_apache_module', $sName, $this->checkApacheModule($sName));
+        $s .= $this->getBlock(_t('_sys_audit_serverside_compression'), '', _t('_sys_audit_msg_serverside_compression', $sApacheModuleChack));
+
+        echo $this->getSection(_t('_sys_audit_section_webserver'), '', $s);
+    }
+
+    protected function optimizationDolphin()
+    {
+        $s = '';
+        foreach ($this->aDolphinOptimizationSettings as $sName => $a) {
+
+            $sVal = ('always_on' == $a['enabled'] || getParam($a['enabled'])) ? 'On' : 'Off';
+            if ($a['cache_engine'])
+                $sVal .= _t('_sys_audit_msg_dolphin_x_based_cache_engine', getParam($a['cache_engine']));
+
+            if ('always_on' != $a['enabled'] && !getParam($a['enabled']))
+                $aMessage = array('type' => BX_DOL_AUDIT_FAIL, 'msg' => _t('_sys_audit_msg_dolphin_optimization_fail'));
+            elseif ($a['check_accel'] && !$this->getPhpAccelerator() && 'File' == getParam($a['cache_engine']))
+                $aMessage = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_dolphin_optimization_warn'));
+            else
+                $aMessage = array('type' => BX_DOL_AUDIT_OK);
+
+            $s .= $this->getBlock($sName, $sVal, $this->getMsgHTML($sName, $aMessage));
+        }
+
+        echo $this->getSection('Dolphin', '', $s);
+    }
+
+    protected function manualCheck()
+    {
+        echo '<a name="manual_audit"></a>';
+        echo '<h1>' . _t('_sys_audit_header_manual_audit') . '</h1>';
+        echo _t('_sys_audit_msg_manual_audit');
+    }
+
+    protected function checkPhpSetting($sName, $a)
+    {
+        $mixedVal = ini_get($sName);
+        $mixedVal = $this->format_input ($mixedVal, $a);
+
+        switch ($a['op']) {
+            case 'module':
+                $bResult = extension_loaded($a['val']);
+                $mixedVal = $bResult ? $a['val'] : '';
+                break;
+            case '>':
+                $bResult = (isset($a['unlimited']) && $mixedVal == $a['unlimited']) ? true : ($mixedVal > $a['val']);
+                break;
+            case '>=':
+                $bResult = (isset($a['unlimited']) && $mixedVal == $a['unlimited']) ? true : ($mixedVal >= $a['val']);
+                break;
+            case '=':
+            default:
+                $bResult = ($mixedVal == $a['val']);
+        }
+        return array ('res' => $bResult, 'real_val' => $mixedVal);
+    }
+
+    protected function checkMysqlSetting($sName, $a, $oDb)
+    {
+        $mixedVal = $oDb->getOption($sName);
+        $mixedVal = $this->format_input ($mixedVal, $a);
+
+        switch ($a['op']) {
+            case '>':
+                $bResult = ($mixedVal > $a['val']);
+                break;
+            case '>=':
+                $bResult = ($mixedVal >= $a['val']);
+                break;
+            case 'strcasecmp':
+                $bResult = 0 == strcasecmp($mixedVal, $a['val']);
+                break;
+            case '=':
+            default:
+                $bResult = ($mixedVal == $a['val']);
+        }
+        return array ('res' => $bResult, 'real_val' => $mixedVal);
+    }
+
+    protected function format_output ($mixedVal, $a)
+    {
+        if (isset($a['type']) && 'bool' == $a['type'])
+            return $mixedVal ? 'On' : 'Off';
+        else
+            return $mixedVal;
+    }
+
+    protected function format_input ($mixedVal, $a)
+    {
+        if (isset($a['type']) && 'bytes' == $a['type'])
+            return $this->format_bytes ($mixedVal);
+        else
+            return $mixedVal;
+    }
+
+    protected function format_bytes($val)
+    {
+        return return_bytes($val);
+    }
+
+    protected function checkApacheModule ($sModule, $sNameCompiledName = '')
+    {
+        $a = array (
+            'deflate_module' => 'mod_deflate',
+            'expires_module' => 'mod_expires',
+        );
+        if (!$sNameCompiledName && isset($a[$sModule]))
+            $sNameCompiledName = $a[$sModule];
+
+        if (function_exists('apache_get_modules')) {
+
+            $aModules = apache_get_modules();
+            $ret = in_array($sNameCompiledName, $aModules);
+
+        } else {
+
+            $sApachectlPath = trim(`which apachectl`);
+            if (!$sApachectlPath)
+                $sApachectlPath = trim(`which apache2ctl`);
+            if (!$sApachectlPath)
+                $sApachectlPath = trim(`which /usr/local/apache/bin/apachectl`);
+            if (!$sApachectlPath)
+                $sApachectlPath = trim(`which /usr/local/apache/bin/apache2ctl`);
+            if (!$sApachectlPath) {
+                return array('type' => BX_DOL_AUDIT_UNDEF, 'msg' => _t('_sys_audit_msg_apache_module_undef', $sModule));
+            }
+            $ret = (boolean)`$sApachectlPath -M 2>&1 | grep $sModule`;
+            if (!$ret)
+                $ret = (boolean)`$sApachectlPath -l 2>&1 | grep $sNameCompiledName`;
+        }
+
+        $aMessage = array('type' => BX_DOL_AUDIT_OK);
+        if (!$ret)
+            $aMessage = array('type' => BX_DOL_AUDIT_FAIL, 'msg' => _t('_sys_audit_msg_apache_module_fail', $sModule));
+        
+        return $aMessage;
+    }
+
+    protected function getPhpAccelerator ()
+    {
+        $aAccelerators = array (
+            'eAccelerator' => array('op' => 'module', 'val' => 'eaccelerator'),
+            'APC' => array('op' => 'module', 'val' => 'apc'),
+            'XCache' => array('op' => 'module', 'val' => 'xcache'),
+        );
+        foreach ($aAccelerators as $sName => $r) {
+            $a = $this->checkPhpSetting($sName, $r);
+            if ($a['res'])
+                return $sName;
+        }
+        return false;
+    }
+
+    protected function getUrlForGooglePageSpeed ($sRule)
+    {
+        $sUrl = urlencode(BX_DOL_URL_ROOT);
+        return 'http://pagespeed.googlelabs.com/#url=' . $sUrl . '&mobile=false&rule=' . $sRule;
+    }
+
+    protected function sendTestEmail ()
+    {
+        $sEmailToCkeckMailSending = isset($_GET['email']) ? $_GET['email'] : getParam('site_email');
+        $mixedRet = sendMail($sEmailToCkeckMailSending, 'Audit Test Email', 'Sample text for testing<br /><u><b>Sample text for testing</b></u>', '', array(), BX_EMAIL_SYSTEM);
+        if (!$mixedRet) {
+            $aMessage = array('type' => BX_DOL_AUDIT_FAIL, 'msg' => _t('_sys_audit_msg_mail_send_failed'));
+            return $this->getBlock('', '', $this->getMsgHTML(_t('_sys_audit_mail_sending'), $aMessage), false);
+        } else {
+            return _t('_sys_audit_msg_mail_sent', $sEmailToCkeckMailSending);
+        }
+    }
+
+    protected function setErrorReporting ()
+    {
+        if (version_compare(phpversion(), "5.3.0", ">=") == 1)
+            $this->iPhpErrorReporting = error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED & ~E_STRICT);
+        else
+            $this->iPhpErrorReporting = error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);
+    }
+
+    protected function restoreErrorReporting ()
+    {
+        error_reporting($this->iPhpErrorReporting);
+    }
+
+    protected function getSection($sTitle, $sTitleAddon, $sContent)
+    {
+        $s = '<b>' . $sTitle . '</b>: ' . $sTitleAddon;
+        $s .= '<ul>';
+        $s .= $sContent;
+        $s .= '</ul>';
+        return $s;
+    }
+
+    protected function getBlock($sName, $sValue = '', $sMsg = '', $bWrapAsListItem = true)
+    {
+        $s = $bWrapAsListItem ? '<li>'  : '';
+        if ($sName !== '')
+            $s .= "$sName ";
+        if ($sValue !== '')
+            $s .= " = $sValue ";
+        if ($sMsg)
+                $s .= ($s ? " - " : '') . $sMsg;
+        return $s . ($bWrapAsListItem ? '</li>' : '') . "\n";
+    }
+
+    protected function getMsgHTML($sName, $a)
+    {
+        $s = '';
+        $s .= '<b class="' . $this->aType2ClassCSS[$a['type']]. '">' . $this->aType2Title[$a['type']]. '</b> ';
+        if (isset($a['msg']))
+            $s .= '(' . $a['msg'] . ')';
+        return $s;
+    }
+}
+
+/** @} */

+ 229 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/classes/BxDolStudioUpdater.php

@@ -0,0 +1,229 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxDolStudioInstaller');
+
+class BxDolStudioUpdater extends BxDolStudioInstaller
+{
+    public function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+        $this->_sModulePath = $this->_sBasePath . $aConfig['module_dir'];
+
+        $this->_aActions = array_merge($this->_aActions, array(
+            'check_module_exists' => array(
+                'title' => _t('_adm_txt_modules_check_module_exists'),
+            ),
+            'check_module_matches' => array(
+                'title' => _t('_adm_txt_modules_check_module_matches'),
+            ),
+            'check_module_version' => array(
+                'title' => _t('_adm_txt_modules_check_module_version'),
+            ),
+            'check_module_hash' => array(
+                'title' => _t('_adm_txt_modules_check_module_hash'),
+            ),
+            'update_files' => array(
+                'title' => _t('_adm_txt_modules_update_files'),
+            ),
+            'restore_languages' => array(
+                'title' => _t('_adm_txt_modules_restore_languages'),
+            ),
+        ));
+    }
+
+    public function update($aParams)
+    {
+    	$bHtmlResponce = isset($aParams['html_response']) && (bool)$aParams['html_response'];
+
+        $aResult = array(
+            'operation_title' => _t('_adm_txt_modules_operation_update', $this->_aConfig['title'], $this->_aConfig['version_from'], $this->_aConfig['version_to'])
+        );
+
+        //--- Check for module to update ---//
+        $aModuleInfo = $this->oDb->getModulesBy(array('type' => 'path_and_uri', 'path' => $this->_aConfig['module_dir'], 'uri' => $this->_aConfig['module_uri']));
+        if(!$aModuleInfo)
+            return array_merge($aResult, array(
+                'message' => $this->_displayResult('check_module_exists', false, '_adm_err_modules_module_not_found', $bHtmlResponce),
+                'result' => false
+            ));
+
+		if(isset($aParams['module_name']) && strcmp($aParams['module_name'], $aModuleInfo['name']) != 0)
+			return array_merge($aResult, array(
+                'message' => $this->_displayResult('check_module_matches', false, '_adm_err_modules_module_not_match', $bHtmlResponce),
+                'result' => false
+            ));
+
+		//--- Check Dolphin version compatibility ---//
+        if(!$this->_isCompatibleWith())
+            return array(
+                'message' => $this->_displayResult('check_script_version', false, _t('_adm_err_modules_wrong_version_script_update', $aModuleInfo['title']), $bHtmlResponce),
+                'result' => false
+            );
+
+        //--- Check version ---//
+        if(version_compare($aModuleInfo['version'], $this->_aConfig['version_from']) != 0)
+            return array_merge($aResult, array(
+                'message' => $this->_displayResult('check_module_version', false, '_adm_err_modules_wrong_version', $bHtmlResponce),
+                'result' => false
+            ));
+
+        //--- Check hash ---//
+        $aFiles = array();
+        $this->hashFiles(BX_DIRECTORY_PATH_ROOT . 'modules/' . $this->_aConfig['module_dir'], $aFiles);
+
+        list($aFilesChanged, $fChangedPercent) = $this->hashCheck($aFiles, $aModuleInfo['id']);
+        $bAutoupdateForceModifiedFiles = getParam('sys_autoupdate_force_modified_files') == 'on';
+
+    	if(!empty($aFilesChanged) && !$bAutoupdateForceModifiedFiles) 
+    		return array_merge($aResult, array(
+                'message' => $this->_displayResult('check_module_hash', false, '_adm_err_modules_module_was_modified', $bHtmlResponce),
+                'result' => false
+            ));
+		else if($fChangedPercent > BX_FORCE_AUTOUPDATE_MAX_CHANGED_FILES_PERCENT && $bAutoupdateForceModifiedFiles) 
+			return array_merge($aResult, array(
+                'message' => $this->_displayResult('check_module_hash', false, _t('_sys_upgrade_files_checksum_failed_too_many', round($fChangedPercent * 100)), $bHtmlResponce),
+                'result' => false
+            ));
+
+        //--- Perform action and check results ---//
+        $aResult = array_merge($aResult, $this->_perform('install', $aParams));
+        if($aResult['result']) {
+            $this->oDb->updateModule(array('version' => $this->_aConfig['version_to']), array('id' => $aModuleInfo['id']));
+
+            $this->oDb->deleteModuleTrackFiles($aModuleInfo['id']);
+
+            $aFiles = array();
+            $this->hashFiles(BX_DIRECTORY_PATH_ROOT . 'modules/' . $this->_aConfig['module_dir'], $aFiles);
+            foreach($aFiles as $aFile)
+                $this->oDb->insertModuleTrack($aModuleInfo['id'], $aFile);
+
+            //--- Remove update pckage ---//
+            $this->delete($aParams);
+        }
+
+        return $aResult;
+    }
+
+    //--- Action Methods ---//
+    public function actionUpdateFiles($bInstall = true)
+    {
+        $sPath = $this->_sHomePath . 'source/';
+        if(!file_exists($sPath))
+            return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+		$oFile = $this->_getFileManager();
+		if(empty($oFile))
+			return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+		if(!$oFile->copy($sPath . '*', 'modules/' . $this->_aConfig['module_dir']))
+			return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+		if(!empty($this->_aConfig['delete_files']) && is_array($this->_aConfig['delete_files']))
+			foreach($this->_aConfig['delete_files'] as $sFile)
+				if(!$oFile->delete('modules/' . $this->_aConfig['module_dir'] . $sFile))
+					return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+        return BX_DOL_STUDIO_INSTALLER_SUCCESS;
+    }
+
+    public function actionExecuteSql($sOperation)
+    {
+    	$aModule = $this->oDb->getModuleByUri($this->_aConfig['module_uri']);
+    	if(empty($aModule))
+    		return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+    	$sPathInstall = $this->_sHomePath . 'install/sql/install.sql';
+    	$sPathEnable = $this->_sHomePath . 'install/sql/enable.sql';
+
+    	$mixedResult = true;
+    	if(file_exists($sPathInstall))
+        	$mixedResult = $this->oDb->executeSQL($sPathInstall);
+
+		if($mixedResult === true && (int)$aModule['enabled'] == 1 && file_exists($sPathEnable))
+			$mixedResult = $this->oDb->executeSQL($sPathEnable);
+
+        return $mixedResult === true ? BX_DOL_STUDIO_INSTALLER_SUCCESS : array('code' => BX_DOL_STUDIO_INSTALLER_FAILED, 'content' => $mixedResult);
+    }
+
+    public function actionUpdateLanguages($bInstall = true)
+    {
+        $aConfig = self::getModuleConfig($this->_sHomePath . 'install/config.php');
+        if(empty($aConfig) || !is_array($aConfig))
+            return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+    	bx_import('BxDolStudioLanguagesUtils');
+        $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+        $aLanguages = $oLanguages->getLanguages();
+
+        $iCategoryId = !empty($aConfig['language_category']) ? $oLanguages->getLanguageCategory($aConfig['language_category']) : 0;
+
+        foreach($aLanguages as $sName => $sTitle)
+            $this->_updateLanguage($bInstall, $sName, $iCategoryId);
+
+        return $oLanguages->compileLanguage(0, true) ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+
+    /*
+     * Restore module's language files.
+     * 
+     * Note. Mainly the action is needed for Updates in 'language' type modules. 
+     * It should be used after 'update_files' action if some changes were done in module's language files. 
+     */
+    public function actionRestoreLanguages($bInstall = true)
+    {
+    	$aConfig = self::getModuleConfig($this->_sHomePath . 'install/config.php');
+        if(empty($aConfig) || !is_array($aConfig) || empty($aConfig['module_uri']))
+            return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+		bx_import('BxDolStudioLanguagesUtils');
+        $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+        $aLanguages = $oLanguages->getLanguages();
+
+        $bResult = true;
+        foreach($aLanguages as $sName => $sTitle)
+        	$bResult &= $oLanguages->restoreLanguage($sName, $aConfig['module_uri']);
+
+        return $bResult ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+
+    protected function _updateLanguage($bInstall, $sLanguage, $iCategory = 0)
+    {
+        $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+
+        $sPath = $this->_sHomePath . 'install/langs/' . $sLanguage . '.xml';
+        $aLanguageInfo = $oLanguages->readLanguage($sPath, 'update');
+        if(empty($aLanguageInfo))
+            return false;
+
+        $iLanguage = $oLanguages->getLangId($sLanguage);
+
+        if(!empty($aLanguageInfo['category']))
+        	$iCategory = $oLanguages->getLanguageCategory($aLanguageInfo['category']);
+
+        //--- Process delete. Note. Deletion is performed for all languages. ---//
+        if(isset($aLanguageInfo['strings_del']))
+        	foreach($aLanguageInfo['strings_del'] as $sKey => $sValue)
+        		$oLanguages->deleteLanguageString($sKey, 0, false);
+
+        //--- Process add. Note. Key's category will be updated if it doesn't match. ---//
+        if(isset($aLanguageInfo['strings_add']))
+        	foreach($aLanguageInfo['strings_add'] as $sKey => $sValue)
+        		$oLanguages->addLanguageString($sKey, $sValue, $iLanguage, $iCategory, false);
+
+        //--- Process update. Note. Key's category will be updated if it doesn't match. ---//
+        if(isset($aLanguageInfo['strings_upd']))
+        	foreach($aLanguageInfo['strings_upd'] as $sKey => $sValue)
+        		$oLanguages->updateLanguageString($sKey, $sValue, $iLanguage, $iCategory, false);
+
+        return true;
+    }
+}
+
+/** @} */

+ 412 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/js/store.js

@@ -0,0 +1,412 @@
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+function BxDolStudioStore(oOptions) {
+	this.sActionsUrl = oOptions.sActionUrl;
+    this.sObjName = oOptions.sObjName == undefined ? 'oBxDolStudioStore' : oOptions.sObjName;
+    this.sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this.iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+
+    this.sIdPageContent = 'bx-std-pc-content';
+    this.sIdPopupProduct = 'bx-std-str-popup-product';
+    this.sIdPopupFiles = 'bx-std-str-popup-files';
+    this.sIdPopupFile = 'bx-std-str-popup-file';
+}
+
+BxDolStudioStore.prototype.addToCart = function(sVendor, iProduct, oButton) {
+	var oDate = new Date();
+	var $this = this;
+	bx_loading(this.sIdPageContent, true);
+
+	$.get(
+		this.sActionsUrl,
+		{
+			str_action: 'add-to-cart',
+			str_vendor: sVendor,
+			str_item: iProduct,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			bx_loading($this.sIdPageContent, false);
+
+			$this.showPopup($this.sIdPopupFile, oData.message, oButton);
+
+			if(parseInt(oData.code) == 0) {
+				var oCounter = $('#bx-std-pmi-checkout .bx-std-pmen-item-counter span');
+				oCounter.html(parseInt(oCounter.html()) + 1);
+
+				if(parseInt(oCounter.html()) > 0)
+					oCounter.parent('.bx-std-pmen-item-counter').show();
+			}
+		},
+		'json'
+	);
+};
+
+BxDolStudioStore.prototype.deleteFromCart = function(sVendor, iProduct, oButton) {
+	var oDate = new Date();
+	var $this = this;
+	bx_loading(this.sIdPageContent, true);
+
+	$.get(
+		this.sActionsUrl,
+		{
+			str_action: 'delete-from-cart',
+			str_vendor: sVendor,
+			str_item: iProduct,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			bx_loading($this.sIdPageContent, false);
+
+			if(oData.message.length > 0)
+				$this.showPopup($this.sIdPopupFile, oData.message, iProduct != 0 ? oButton : null);
+
+			var iCode = parseInt(oData.code);
+			var oCounter = $('#bx-std-pmi-checkout .bx-std-pmen-item-counter span');
+			if(iCode == 0 && iProduct != 0) {
+				var oProduct = $('#bx-std-product-' + iProduct);
+				if(oProduct.siblings('.bx-std-product').length == 0)
+					oProduct.parents('.bx-std-block:first').bx_anim('hide', 'fade', 'fast', function() {
+						$(this).remove();
+					});
+				else
+					oProduct.bx_anim('hide', 'fade', 'fast', function() {
+						$(this).remove();
+					});
+
+				oCounter.html(parseInt(oCounter.html()) - 1);
+			}
+			else if(iCode == 0 && iProduct == 0) {
+				var oBlock = $(oButton).parents('.bx-std-block:first');
+				oCounter.html(parseInt(oCounter.html()) - oBlock.find('.bx-std-product').length);
+
+				oBlock.bx_anim('hide', 'fade', 'fast', function() {
+					$(this).remove();
+				});
+			}
+
+			if(parseInt(oCounter.html()) <= 0)
+				oCounter.parent('.bx-std-pmen-item-counter').hide();
+		},
+		'json'
+	);
+};
+
+BxDolStudioStore.prototype.deleteAllFromCart = function(sVendor, oButton) {
+	this.deleteFromCart(sVendor, 0, oButton);
+};
+
+BxDolStudioStore.prototype.checkoutCart = function(sVendor, oButton) {
+	var oDate = new Date();
+	var $this = this;
+	bx_loading(this.sIdPageContent, true);
+
+	$.get(
+		this.sActionsUrl,
+		{
+			str_action: 'checkout-cart',
+			str_vendor: sVendor,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			bx_loading($this.sIdPageContent, false);
+
+			if(oData.message.length > 0)
+				$this.showPopup($this.sIdPopupFile, oData.message, oButton);
+
+			if(parseInt(oData.code) == 0 && oData.redirect.length > 0)
+				document.location=oData.redirect;
+		},
+		'json'
+	);
+};
+
+BxDolStudioStore.prototype.getFile = function(iFileId, oButton) {
+	this._getFile('get-file', iFileId, '', oButton);
+};
+
+BxDolStudioStore.prototype.getUpdate = function(sModuleName, oButton) {
+	this._getFile('get-update', sModuleName, '', oButton);
+};
+
+BxDolStudioStore.prototype.getUpdateAndInstall = function(sModuleName, oButton) {
+	var $this = this;
+
+	var onResult = function(oData, oButton) {
+		if(oData.code != 0) {
+			$this._onGetFile(oData, oButton);
+			return;
+		}
+
+		bx_loading($this.sIdPageContent, false);
+
+		$(oButton).parents('.bx-std-product:first').hide();
+	};
+
+	this._getFile('get-update-and-install', sModuleName, onResult, oButton);
+};
+
+BxDolStudioStore.prototype._getFile = function(sAction, mixedId, onResult, oButton) {
+	var oDate = new Date();
+	var $this = this;
+
+	bx_loading(this.sIdPageContent, true);
+	$(oButton).addClass('bx-btn-disabled');
+
+	$.get(
+		this.sActionsUrl,
+		{
+			str_action: sAction,
+			str_id: mixedId,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			if(typeof onResult == 'function')
+				onResult(oData, oButton);
+			else
+				$this._onGetFile(oData, oButton);
+		},
+		'json'
+	);
+};
+
+BxDolStudioStore.prototype._onGetFile = function(oData, oButton) {
+	bx_loading(this.sIdPageContent, false);
+
+	switch(parseInt(oData.code)) {
+		case 1:
+			$(oButton).removeClass('bx-btn-disabled');
+			break;
+		case 2:
+			$(oButton).val(_t('_adm_btn_queued_submit'));
+			break;
+	}
+
+	if(oData.message)
+		this.showPopup(this.sIdPopupFile, oData.message, oButton);
+};
+
+BxDolStudioStore.prototype.info = function(sModuleName, oLink) {
+	var oDate = new Date();
+	var $this = this;
+	bx_loading(this.sIdPageContent, true);
+
+	$.get(
+		this.sActionsUrl,
+		{
+			str_action: 'get-product',
+			str_id: sModuleName,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			bx_loading($this.sIdPageContent, false);
+
+			var sId = $this.sIdPopupProduct;
+			if(oData.code == 0 && oData.popup.length > 0) {
+		        $('#' + sId).remove();
+				$(oData.popup).appendTo('body');
+				$('#' + sId).dolPopup({
+					onShow: function() {
+						$this.initScreenshots(oData.screenshots);
+					}
+				});
+			}
+			else
+				$this.showPopup(sId, oData.message, oLink);
+		},
+		'json'
+	);
+};
+
+BxDolStudioStore.prototype.initScreenshots = function(iCount) {
+	var iWidth = 202;
+	var iPadding = 20;
+	var iWidthOuter = iWidth + iPadding;
+	var bBusy = false;
+
+	$(".bx-std-pv-screenshots a[rel=group]").fancybox({
+		transitionIn: 'elastic',
+		transitionOut: 'elastic',
+		speedIn: 600,
+		speedOut: 200
+	});
+	if(iCount <= 4)
+		return;
+
+	$(".bx-std-pvs-left").bind('click', function() {
+		if(bBusy || parseInt($(".bx-std-pvs-cnt").css('left')) >= 0)
+			return;
+
+		bBusy = true;
+		$(".bx-std-pvs-cnt").animate({left: '+=' + iWidthOuter}, 500, function() {
+			bBusy = false;
+		});
+	});
+	$(".bx-std-pvs-right").bind('click', function() {
+		if(bBusy || parseInt($(".bx-std-pvs-cnt").css('left')) <= 460 - $(".bx-std-pvs-cnt").width())
+			return;
+
+		bBusy = true;
+		$(".bx-std-pvs-cnt").animate({left: '-=' + iWidthOuter}, 500, function() {
+			bBusy = false;
+		});
+	});
+
+	$(".bx-std-pv-screenshots").hover(function() {
+		$(".bx-std-pvs-left, .bx-std-pvs-right").bx_anim('show', 'fade', 'fast');
+	}, function() {
+		$(".bx-std-pvs-left, .bx-std-pvs-right").bx_anim('hide', 'fade', 'fast');;
+	});
+};
+
+BxDolStudioStore.prototype.install = function(sValue, oInput) {
+	var $this = this;
+	var onSuccess = function(oData) {
+		$(oInput).parent('.bx-std-pc-buttons:first').hide(0, function() {
+			$(this).siblings('.bx-std-pcb-installed:hidden').show(0);
+		});
+	};
+
+	return this.perform('install', sValue, onSuccess);
+};
+
+BxDolStudioStore.prototype.update = function(sValue, oInput) {
+	var $this = this;
+	var onSuccess = function(oData) {
+		$(oInput).parents('.bx-std-product:first').hide();
+	};
+
+	return this.perform('update', sValue, onSuccess);
+};
+
+BxDolStudioStore.prototype.remove = function(sValue, oInput) {
+	var onSuccess = function(oData) {
+		switch(parseInt(oData.code)) {
+			case 0:
+				$(oInput).parents('.bx-std-product:first').hide();
+				break;
+			case 2:
+				$(oInput).parent('.bx-std-pc-buttons:first').hide(0, function() {
+					$(this).siblings('.bx-std-pcb-queued:hidden').show(0);
+				});
+				break;
+		}
+	};
+
+    return this.perform('delete', sValue, onSuccess);
+};
+
+BxDolStudioStore.prototype.perform = function(sType, sValue, onSuccess) {
+	var oDate = new Date();
+	var $this = this;
+
+	if(!sValue)
+        return false;
+
+	bx_loading(this.sIdPageContent, true);
+
+    $.post(
+    	this.sActionsUrl,
+    	{
+    		str_action: sType,
+    		str_value: sValue,
+    		_t:oDate.getTime()
+    	},
+    	function (oData) {
+    		bx_loading($this.sIdPageContent, false);
+
+    		if(oData.message.length > 0)
+    			$this.showPopup('bx-std-str-popup-' + sType, oData.message);
+
+    		switch(parseInt(oData.code)) {
+	    		case 0:
+	    		case 2:
+	    			if(typeof onSuccess == 'function')
+	    				onSuccess(oData);
+	    			break;
+    		}
+    	},
+    	'json'
+    );
+};
+
+/**
+ * Is needed if AJAX is used to change (reload) pages. 
+ */
+BxDolStudioStore.prototype.changePage = function(sType) {
+	var oDate = new Date();
+	var $this = this;
+
+	$.get(
+		this.sActionsUrl,
+		{
+			str_action: 'get-products-by-type',
+			str_value: sType,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			if(oData.code != 0) {
+				$this.showPopup('bx-std-str-popup-browsing', oData.message);
+				return;
+			}
+
+			$('#bx-std-pc-menu > .bx-std-pmi-active').removeClass('bx-std-pmi-active');
+			$('#bx-std-pmi-' + sType).addClass('bx-std-pmi-active');
+
+			$('#' + $this.sIdPageContent).bx_anim('hide', $this.sAnimationEffect, $this.iAnimationSpeed, function() {
+				$(this).html(oData.content).bx_anim('show', $this.sAnimationEffect, $this.iAnimationSpeed);
+			});
+		},
+		'json'
+	);
+
+	return true;
+};
+
+BxDolStudioStore.prototype.changePagePaginate = function(oButton, sType, iStart, iPerPage) {
+	var oDate = new Date();
+	var $this = this;
+
+	$.get(
+		this.sActionsUrl,
+		{
+			str_action: 'get-products-by-page',
+			str_type: sType,
+			str_start: iStart,
+			str_per_page: iPerPage,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			if(oData.code != 0) {
+				$this.showPopup('bx-std-str-popup-browsing', oData.message);
+				return;
+			}
+
+			$(oButton).parents('.bx-std-block-content:first').bx_anim('hide', $this.sAnimationEffect, $this.iAnimationSpeed, function() {
+				$(this).html(oData.content).bx_anim('show', $this.sAnimationEffect, $this.iAnimationSpeed);
+			});
+		},
+		'json'
+	);
+
+	return true;
+};
+
+BxDolStudioStore.prototype.showPopup = function(sId, sContent, mixedPointer) {
+    $('#' + sId).remove();
+    $('<div id="' + sId + '" style="display: none;"></div>').appendTo('body').html(sContent);
+
+    var oParams = {};
+    if(mixedPointer)
+    	oParams.pointer = {
+    		el:$(mixedPointer)
+    	};
+
+    $('#' + sId).dolPopup(oParams);
+};
+/** @} */

+ 58 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/_header.html

@@ -0,0 +1,58 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
+    <head>
+        <meta http-equiv="Content-Type" content="text/html; charset=__page_charset__" />
+        <title>__page_header__</title>
+
+        __meta_info__
+	    __page_robots__
+
+        <bx_include_css />
+
+        __dol_images__
+        __dol_lang__
+        __dol_options__
+
+        <bx_include_js />
+        <script defer type="text/javascript">
+            var sUrlRoot = '<bx_url_root />';
+            var sUrlStudio = '<bx_url_studio />';
+            var aSpinnerOpts = {
+                lines: 7, // The number of lines to draw
+                length: 0, // The length of each line
+                width: 8, // The line thickness
+                radius: 7, // The radius of the inner circle
+                corners: 1, // Corner roundness (0..1)
+                rotate: 0, // The rotation offset
+                direction: 1, // 1: clockwise, -1: counterclockwise
+                color: '#165ea1', // #rgb or #rrggbb or array of colors
+                speed: 1, // Rounds per second
+                trail: 60, // Afterglow percentage
+                shadow: false, // Whether to render a shadow
+                hwaccel: false, // Whether to use hardware acceleration
+                className: 'bx-sys-spinner', // The CSS class to assign to the spinner
+                zIndex: 2e9, // The z-index (defaults to 2000000000)
+                top: '10px', // Top position relative to parent in px
+                left: '10px' // Left position relative to parent in px
+            };
+            var aSpinnerSmallOpts = $.extend({}, aSpinnerOpts, {
+                lines: 6,
+                width: 6,
+                radius: 4,
+                color: '#333',
+                top: 'auto',
+                left: 'auto'
+            }); 
+
+			$(document).ready(function () {
+				bx_time('__lang__');
+			});
+        </script>
+        __extra_js__
+
+        <bx_injection:injection_head />
+
+    </head>
+
+    <body class="bx-def-font bx-def-color-bg-page bx-std-bg<bx_injection:injection_body_style />" <bx_injection:injection_body /> >
+        <bx_injection:injection_header />

+ 190 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/css/general.css

@@ -0,0 +1,190 @@
+html,
+body {
+	height: 100%;
+}
+iframe.bx-std-hidden {
+	display: none;
+}
+div.bx-std-cb {
+    clear: both;
+    font-size: 1px;
+}
+
+/**
+ * Page
+ */
+
+/**
+ * Page Content
+ */
+div.bx-std-page-real {
+	width: 100%;
+	min-height: 100%;
+	overflow: hidden;
+}
+div.bx-std-page-wide {
+	width: 200%;
+}
+div.bx-std-page-wrp {
+	position: relative;
+	float: right;
+	width: 50%;
+}
+div.bx-std-page-bg {
+    position:fixed; 
+    width:100%; 
+    height:100%; 
+
+	background:url(../images/bx-std-spacer.gif) center center no-repeat;
+    /* background:url(../images/bx-std-bg-main-image.jpg) center center no-repeat; */
+}
+div.bx-std-page {
+	position: relative;
+	width: 100%;
+	right: 100%;
+}
+div.bx-std-content {
+	position: relative;
+}
+
+/**
+ * Dual Column page
+ */
+div.bx-std-content-wrp {
+	border-top: 1px solid #666666;
+}
+div.bx-std-page-caption {
+	position: relative;
+	height: 70px;
+	background-image: url("../images/bx-std-bg-caption.png");
+    background-position: 0 0;
+    background-repeat: repeat-x;
+}
+div.bx-std-pcap-icon {}
+div.bx-std-pcap-text {
+	position: relative;
+	line-height: 70px;
+	text-align: center;
+	font-size: 2rem;
+}
+div.bx-std-pcap-menu-popup {
+	position: absolute;
+	z-index: 1;
+}
+div.bx-std-pcap-mp-right {
+	right: 10px;
+}
+
+div.bx-std-pcap-help .sys-rss-feed-wrapper {
+	padding-top: 0px;
+}
+div.bx-std-pcap-help .bx-loading-ajax {
+	margin-top: 0px;
+	margin-bottom: 0px;
+}
+
+div.bx-std-pcap-action {
+	position: relative;
+	float: left;
+	width: 36px;
+	height: 36px;
+}
+div.bx-std-pcap-action:first-child {
+	margin-left: 0px;
+}
+div#bx-std-pca-activate {
+	width: 82px;
+}
+div.bx-std-pcap-mp-right div.bx-std-pcap-action {
+    float: right;
+}
+
+/**
+ * Page Menu (Left Side)
+ */
+div.bx-std-pmen-item {
+	position: relative;
+	height: 46px;
+	border-top: 1px solid transparent;
+	border-bottom: 1px solid transparent;
+	line-height: 46px;
+	text-shadow: #ffffff 0px 1px 0px;
+	overflow: hidden;
+}
+div.bx-std-pmen-item-cnt {
+	padding-left: 66px;    
+}
+
+span.bx-std-pmen-item-counter {}
+span.bx-std-pmen-ic-num {}
+div.bx-std-pmen-item img {
+	position: absolute;
+	top: 7px;
+	left: 20px;
+	width: 32px;
+	height: 32px;
+}
+div.bx-std-pmen-item i.sys-icon {
+	position: absolute;
+    left: 20px;
+    width:32px;
+
+    color:white;
+    font-size:20px;
+    text-align:center;
+    text-shadow:none;
+}
+div.bx-menu-tab-active {
+	background-color: #f2f2f2;
+}
+div.bx-menu-tab-active a,
+div.bx-menu-tab-active:hover a {
+	color: #333333;
+    text-decoration:none;
+}
+
+/**
+ * Page Blocks
+ */
+div.bx-std-block {}
+div.bx-std-block-caption {
+    position: relative;
+    margin: 0px 0px 10px 0px;
+    border-style: solid;
+    border-width: 0px 0px 1px 0px;
+}
+div.bx-std-bcap-text {
+    line-height: 25px;
+    color: #666666;
+}
+div.bx-std-bcap-actions {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+}
+div.bx-std-bcap-action,
+div.bx-std-bcap-action-div {
+	position: relative;
+	float: left;
+    line-height: 25px;
+}
+div.bx-std-bcap-action-div:first-child {
+	display: none;
+}
+div.bx-std-block-panel-top {}
+div.bx-std-block-content {}
+div.bx-std-block-panel-bottom {
+	margin: 10px 0px 0px 0px;
+}
+
+/**
+ * Footer
+ */
+div.bx-std-bx-footer {
+	margin-top: 60px;
+	border: 0px;
+	background-color: transparent;
+    -moz-shadow: none;
+    -webkit-shadow: none;
+	box-shadow: none;
+}

+ 249 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/css/launcher.css

@@ -0,0 +1,249 @@
+body.bx-std-bg {
+	background-color: #3b8686;
+}
+
+/**
+ * Launcher
+ */
+div.bx-std-launcher {
+	position: relative;
+	float: left;
+	clear: both;
+	width: 100%;
+	left: 0px;
+}
+
+
+/**
+ * Widget
+ */
+div.bx-std-widgets-wrp {
+    position: relative;
+	float: left;
+    top: 0px;
+	width: 100%;
+}
+div.bx-std-widgets-center {
+    position: relative;
+    float: left;
+    left: 50%;
+}
+div.bx-std-widgets {
+    position: relative;
+    float: left;
+    left: -50%;
+	min-height: 238px;
+    margin: 0px 30px;
+}
+div.bx-std-widgets-highlight {
+    background-color: red;
+}
+div.bx-std-widget {
+    position: relative;
+    float: left;
+	padding: 30px;
+}
+div.bx-std-widget-empty {
+    width: 128px;
+    height: 178px;
+}
+div.bx-std-widget-nl {
+	clear: both;
+}
+
+.bx-std-widget-dragging {
+    -webkit-transform: scale(1.05, 1.05);
+    -moz-transform: scale(1.05, 1.05);
+    -ms-transform: scale(1.05, 1.05);
+    transform: scale(1.05, 1.05);
+}
+
+.bx-std-widget-icon {
+    position: relative;
+    top:0px;
+    width: 128px;
+    height: 128px;
+
+    background-color:transparent;
+
+    line-height: 128px;
+}
+
+.bx-std-widget-icon i.sys-icon {
+	position: absolute;
+	z-index: 2;
+	left: 0px;
+	width: 128px;
+
+    font-size: 96px;
+    text-align: center;
+    text-shadow: none;
+    color: white;
+}
+
+.bx-std-widget-shadow {
+    position:absolute;
+    top:2px;
+    left:2px;
+    width: 124px;
+    height: 124px;
+    z-index:1;
+
+    -moz-border-radius: 32px;
+    -webkit-border-radius: 32px;
+    border-radius: 32px;
+
+    /* big shadow */
+    -moz-box-shadow: 0px 15px 20px rgba(51, 51, 51, 0.55);
+    -webkit-box-shadow: 0px 15px 20px rgba(51, 51, 51, 0.55);
+    box-shadow: 0px 15px 20px rgba(51, 51, 51, 0.55);
+}
+
+.bx-std-widget-icon-trans {
+    -webkit-transition: all 0.15s ease-out;
+    -moz-transition: all 0.15s ease-out;
+    -ms-transition: all 0.15s ease-out;
+    -o-transition: all 0.15s ease-out;
+}
+
+.bx-std-widget-icon:hover {
+    top:3px;
+}
+
+.bx-std-widget-icon:hover .bx-std-widget-shadow {
+    -moz-box-shadow: 0px 12px 15px rgba(51, 51, 51, 0.55);
+    -webkit-box-shadow: 0px 12px 15px rgba(51, 51, 51, 0.55);
+    box-shadow: 0px 12px 15px rgba(51, 51, 51, 0.55);
+}
+
+.bx-std-widget-dragging .bx-std-widget-shadow {
+    -moz-box-shadow: 0px 25px 40px rgba(51, 51, 51, 0.55);
+    -webkit-box-shadow: 0px 25px 40px rgba(51, 51, 51, 0.55);
+    box-shadow: 0px 25px 40px rgba(51, 51, 51, 0.55);
+}
+
+.bx-std-widget-icon-disabled {
+    -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=40)";
+    filter: alpha(opacity=40);
+    -moz-opacity: 0.4;
+    -khtml-opacity: 0.4;
+    opacity: 0.4;    
+}
+
+.bx-std-widget-link > img {
+    position:relative;
+    z-index:2;
+
+    width: 128px;
+    height: 128px;
+
+    background-color: transparent;
+    background-position:0px 0px;
+    background-repeat:no-repeat;
+}
+
+div.bx-std-widget-icon-jitter {
+    position: absolute;
+    top: 30px;
+    left: 30px;
+    width: 128px;
+    height: 128px;
+    z-index: 5;
+
+    background-color: transparent;
+}
+
+div.bx-std-widget-icon-jitter img {
+    position:absolute;
+
+    bottom:-5px;
+    left:24px;
+    width:80px;
+    height:40px;
+
+    background-color: transparent;
+    background-image:url(../images/bx-std-widget-drag.png);
+
+    cursor: move;
+}
+
+div.bx-std-widget-notices {
+    position: absolute;
+    top: 0px;
+    left: 45px;
+    width: 38px;
+    height: 27px;
+    z-index: 3;
+    overflow:visible;
+    
+    color:#fff;
+    font-weight:bold;
+    text-shadow: #633 0px -1px 0px;        
+    line-height:25px;
+    text-align:center;
+
+    background-color: transparent;
+    background-image: url(../images/bx-std-bg-widget-notice.png);
+}
+div.bx-std-widget-actions {
+    position: absolute;
+    display: none;
+    top: 10px;
+    right: 10px;
+    height: 40px;
+    z-index: 10;
+}
+div.bx-std-widget-action {
+    position: relative;
+    float: left;
+    width: 40px;
+    height: 40px;
+	line-height: 40px;
+}
+div.bx-std-widget-action i.sys-icon {
+	position: absolute;
+	left: 0px;
+	width: 40px;
+
+    font-size: 32px;
+    text-align: center;
+    text-shadow: none;
+    color: white;
+}
+.bx-std-widget-loading {
+    display:none;
+    position:absolute;
+    top: 176px;
+    left: 30px;
+    width: 128px;
+    height: 15px;
+
+    background-image: url(../images/loading.gif);
+    background-repeat: no-repeat;
+    background-position: center center;
+
+    -moz-opacity: 0.5;
+    -khtml-opacity: 0.5;
+    opacity: 0.5;
+}
+div.bx-std-widget-caption {
+    position: relative;
+    width: 128px;
+    height: 50px;
+    line-height: 50px;
+    text-align: center; 
+    overflow: hidden;
+
+    text-shadow: #000 0px 5px 12px;
+}
+div.bx-std-widget-caption-jitter {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    width: 128px;
+    height: 50px;
+}
+div.bx-std-widget-caption a {
+    color: #ffffff;
+    font-weight:normal;
+}

+ 0 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/css/page.css


+ 45 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/scripts/BxBaseStudioConfig.php

@@ -0,0 +1,45 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinView Dolphin Studio Representation classes
+ * @ingroup     DolphinStudio
+ * @{
+ */
+
+bx_import('BxTemplConfig');
+
+class BxBaseStudioConfig extends BxTemplConfig
+{
+    function __construct()
+    {
+        parent::__construct();
+
+        $this->_aConfig['aLessConfig'] = array_merge($this->_aConfig['aLessConfig'], array(
+            'bx-margin' => '30px',
+            'bx-margin-sec' => '20px',
+            'bx-margin-thd' => '10px',
+
+            'bx-padding' => '30px',
+            'bx-padding-sec' => '20px',
+            'bx-padding-thd' => '10px',
+
+        	'bx-color-page' => '#ffffff',
+        	'bx-border-color-layout' => '#cccccc',
+
+            'bx-size-widget' => '128px',
+            'bx-round-corners-radius-widget' => '32px',
+        ));
+    }
+
+    public static function getInstance()
+    {
+        if(!isset($GLOBALS['bxDolClasses'][__CLASS__]))
+            $GLOBALS['bxDolClasses'][__CLASS__] = new BxTemplStudioConfig();
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+}
+
+/** @} */

+ 804 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/scripts/BxBaseStudioNavigationItems.php

@@ -0,0 +1,804 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinView Dolphin Studio Representation classes
+ * @ingroup     DolphinStudio
+ * @{
+ */
+
+bx_import('BxDolStudioNavigationItems');
+bx_import('BxTemplStudioFormView');
+
+class BxBaseStudioNavigationItems extends BxDolStudioNavigationItems
+{
+    protected $sUrlPage;
+    protected $sUrlViewItems;
+
+    function __construct($aOptions, $oTemplate = false)
+    {
+        parent::__construct($aOptions, $oTemplate);
+
+        $this->_aOptions['actions_single']['edit']['attr']['title'] = _t('_adm_nav_btn_items_edit');
+        $this->_aOptions['actions_single']['delete']['attr']['title'] = _t('_adm_nav_btn_items_delete');
+
+        $this->sUrlPage = BX_DOL_URL_STUDIO . 'builder_menu.php?page=items';
+        $this->sUrlViewItems = $this->sUrlPage . '&module=%s&set=%s';
+    }
+
+    public function performActionImport()
+    {
+        bx_import('BxDolGrid');
+        $oGrid = BxDolGrid::getObjectInstance('sys_studio_nav_import');
+        if(!$oGrid)
+            return '';
+
+        bx_import('BxTemplFunctions');
+        $sContent = BxTemplFunctions::getInstance()->popupBox('adm-nav-item-import-popup', _t('_adm_nav_txt_items_import_popup'), $this->_oTemplate->parseHtmlByName('nav_import_item.html', array(
+            'grid' => $oGrid->getCode()
+        )));
+
+        $this->_echoResultJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))), true);
+    }
+
+    public function performActionAdd()
+    {
+        $sAction = 'add';
+
+        $oForm = $this->_getFormObject($sAction);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            if(!empty($_FILES['icon_image']['tmp_name'])) {
+                bx_import('BxDolStorage');
+                $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+
+                $mixedIcon = $oStorage->storeFileFromForm($_FILES['icon_image'], false, 0);
+                if($mixedIcon === false) {
+                    $this->_echoResultJson(array('msg' => _t('_adm_nav_err_items_icon_image') . $oStorage->getErrorString()), true);
+                    return;
+                }
+
+                $oStorage->afterUploadCleanup($mixedIcon, 0);
+                BxDolForm::setSubmittedValue('icon', $mixedIcon, $oForm->aFormAttrs['method']);
+            }
+
+            bx_import('BxDolPermalinks');
+            $oPermalinks = BxDolPermalinks::getInstance();
+
+            $sLink = $oForm->getCleanValue('link');
+            $sLink = $oPermalinks->unpermalink($sLink);
+            BxDolForm::setSubmittedValue('link', $sLink, $oForm->aFormAttrs['method']);
+
+            $sName = $oPermalinks->getPageNameFromLink($sLink);
+            if($sName == '') {
+                bx_import('BxDolStudioLanguagesUtils');
+                $sLanguage = BxDolStudioLanguagesUtils::getInstance()->getCurrentLangName(false);
+
+                $sName = BxDolForm::getSubmittedValue('title_system-' . $sLanguage, $oForm->aFormAttrs['method']);
+                $sName = uriGenerate($sName, 'sys_menu_items', 'name', 'item');
+            }
+
+            $sSubmenu = $oForm->getCleanValue('submenu_object');
+            if(!empty($sSubmenu)) {
+                BxDolForm::setSubmittedValue('link', 'javascript:void(0)', $oForm->aFormAttrs['method']);
+                BxDolForm::setSubmittedValue('target', '', $oForm->aFormAttrs['method']);
+                BxDolForm::setSubmittedValue('onclick', 'bx_menu_popup(\'' . $sSubmenu . '\', this);', $oForm->aFormAttrs['method']);
+            }
+
+            $iId = (int)$oForm->insert(array('module' => BX_DOL_STUDIO_MODULE_CUSTOM, 'name' => $sName, 'active' => 1, 'order' => $this->oDb->getItemOrderMax($this->sSet) + 1));
+            if($iId != 0)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $iId);
+            else
+                $aRes = array('msg' => _t('_adm_nav_err_items_create'));
+
+            $this->_echoResultJson($aRes, true);
+        } else {
+            bx_import('BxTemplStudioFunctions');
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-nav-item-create-popup', _t('_adm_nav_txt_items_create_popup'), $this->_oTemplate->parseHtmlByName('nav_add_item.html', array(
+                'form_id' => $oForm->aFormAttrs['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            )));
+
+            $this->_echoResultJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))), true);
+        }
+    }
+
+    public function performActionEdit($bUpdateGrid = false)
+    {
+        $sAction = 'edit';
+
+        $aItem = $this->_getItem('getItems');
+        if($aItem === false) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $oForm = $this->_getFormObject($sAction, $aItem);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $bIconImageCur = is_numeric($aItem['icon']) && (int)$aItem['icon'] != 0;
+            $bIconImageNew = !empty($_FILES['icon_image']['tmp_name']);
+
+            $sIconFont = $oForm->getCleanValue('icon');
+            $bIconFont = !empty($sIconFont);
+
+            if($bIconImageCur && ($bIconImageNew || $bIconFont)) {
+                bx_import('BxDolStorage');
+                $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+                if(!$oStorage->deleteFile((int)$aItem['icon'], 0)) {
+                    $this->_echoResultJson(array('msg' => _t('_adm_nav_err_items_icon_image_remove')), true);
+                    return;
+                }
+            }
+
+            $sIcon = $sIconFont;
+            if($bIconImageNew) {
+                bx_import('BxDolStorage');
+                $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+                $sIcon = $oStorage->storeFileFromForm($_FILES['icon_image'], false, 0);
+                if($sIcon === false) {
+                    $this->_echoResultJson(array('msg' => _t('_adm_nav_err_items_icon_image') . $oStorage->getErrorString()), true);
+                    return;
+                }
+
+                $oStorage->afterUploadCleanup($sIcon, 0);
+            } else if($bIconImageCur && !$bIconFont)
+                $sIcon = $aItem['icon'];
+
+            BxDolForm::setSubmittedValue('icon', $sIcon, $oForm->aFormAttrs['method']);
+
+            bx_import('BxDolPermalinks');
+            $sLink = $oForm->getCleanValue('link');
+            $sLink = BxDolPermalinks::getInstance()->unpermalink($sLink);
+            BxDolForm::setSubmittedValue('link', $sLink, $oForm->aFormAttrs['method']);
+
+            $sSubmenu = $oForm->getCleanValue('submenu_object');
+            if(!empty($sSubmenu)) {
+                BxDolForm::setSubmittedValue('link', 'javascript:void(0)', $oForm->aFormAttrs['method']);
+                BxDolForm::setSubmittedValue('target', '', $oForm->aFormAttrs['method']);
+                BxDolForm::setSubmittedValue('onclick', 'bx_menu_popup(\'' . $sSubmenu . '\', this);', $oForm->aFormAttrs['method']);
+            } else {
+                $sOnClick = $oForm->getCleanValue('onclick');
+                if(mb_substr($sOnClick, 0, 13) == 'bx_menu_popup')
+                    BxDolForm::setSubmittedValue('onclick', '', $oForm->aFormAttrs['method']);
+            }
+
+            $sTarget = $oForm->getCleanValue('target');
+            if($sTarget === false && !in_array($aItem['target'], array('', '_blank')))
+                unset($oForm->aInputs['target']);
+
+            if($oForm->update($aItem['id']) !== false)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $aItem['id']);
+            else
+                $aRes = array('msg' => _t('_adm_nav_err_items_edit'));
+
+            $this->_echoResultJson($aRes, true);
+        } else {
+            $sTitle = _t($aItem['title']);
+            bx_import('BxTemplStudioFunctions');
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-nav-item-edit-popup', _t('_adm_nav_txt_items_edit_popup', ($sTitle != "" ? '"' . $sTitle . '"' : '')), $this->_oTemplate->parseHtmlByName('nav_add_item.html', array(
+                'form_id' => $oForm->aFormAttrs['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            )));
+
+            $aRes = array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false)));
+            if($bUpdateGrid)
+                $aRes = array_merge($aRes, array('grid' => $this->getCode(false), 'blink' => $aItem['id']));
+
+            $this->_echoResultJson($aRes, true);
+        }
+    }
+
+    public function performActionDelete()
+    {
+        $iAffected = 0;
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $aIdsAffected = array ();
+        foreach($aIds as $iId) {
+            if(!$this->deleteById($iId))
+                continue;
+
+            $aIdsAffected[] = $iId;
+            $iAffected++;
+        }
+
+        $this->_echoResultJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('msg' => _t('_adm_nav_err_items_delete')));
+    }
+
+    public function performActionShowTo()
+    {
+        $sAction = 'show_to';
+
+        $aItem = $this->_getItem('getItems');
+        if($aItem === false) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-nav-item-create',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction . '&set=' . $this->sSet,
+                'method' => 'post'
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_menu_items',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'id' => array(
+                    'type' => 'hidden',
+                    'name' => 'id',
+                    'value' => $aItem['id'],
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'visible_for' => array(
+                    'type' => 'select',
+                    'name' => 'visible_for',
+                    'caption' => _t('_adm_nav_txt_items_visible_for'),
+                    'info' => '',
+                    'value' => $aItem['visible_for_levels'] == BX_DOL_INT_MAX ? BX_DOL_STUDIO_VISIBLE_ALL : BX_DOL_STUDIO_VISIBLE_SELECTED,
+                    'values' => array(
+                        array('key' => BX_DOL_STUDIO_VISIBLE_ALL, 'value' => _t('_adm_nav_txt_items_visible_for_all')),
+                        array('key' => BX_DOL_STUDIO_VISIBLE_SELECTED, 'value' => _t('_adm_nav_txt_items_visible_for_selected')),
+                    ),
+                    'required' => '0',
+                    'attrs' => array(
+                        'onchange' => $this->getJsObject() . '.onChangeVisibleFor(this)'
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    )
+                ),
+                'visible_for_levels' => array(
+                    'type' => 'checkbox_set',
+                    'name' => 'visible_for_levels',
+                    'caption' => _t('_adm_nav_txt_items_visible_for_levels'),
+                    'info' => _t('_adm_nav_dsc_items_visible_for_levels'),
+                    'value' => '',
+                    'values' => array(),
+                    'tr_attrs' => array(
+                        'style' => $aItem['visible_for_levels'] == BX_DOL_INT_MAX ? 'display:none' : ''
+                    ),
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'controls' => array(
+                    'name' => 'controls',
+                    'type' => 'input_set',
+                    array(
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_adm_nav_btn_items_save'),
+                    ),
+                    array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_adm_nav_btn_items_cancel'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    )
+                )
+            )
+        );
+
+        BxDolStudioUtils::getVisibilityValues($aItem['visible_for_levels'], $aForm['inputs']['visible_for_levels']['values'], $aForm['inputs']['visible_for_levels']['value']);
+
+        bx_import('BxTemplStudioFormView');
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            if($oForm->updateWithVisibility($aItem['id']) !== false)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $aItem['id']);
+            else
+                $aRes = array('msg' => _t('_adm_nav_err_items_show_to'));
+
+            $this->_echoResultJson($aRes, true);
+        } else {
+            bx_import('BxTemplStudioFunctions');
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-nav-item-hide-from-popup', _t('_adm_nav_txt_items_show_to_popup', _t($aItem['title'])), $this->_oTemplate->parseHtmlByName('nav_add_item.html', array(
+                'form_id' => $aForm['form_attrs']['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            )));
+
+            $this->_echoResultJson(array('popup' => $sContent), true);
+        }
+    }
+
+    public function performActionDeleteIcon()
+    {
+        $sAction = 'delete_icon';
+
+        $aIds = bx_get('ids');
+        if(empty($aIds[0])) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $iId = (int)$aIds[0];
+
+        $aItem = array();
+        $iItem = $this->oDb->getItems(array('type' => 'by_id', 'value' => $iId), $aItem);
+        if($iItem != 1 || empty($aItem)){
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        if(is_numeric($aItem['icon']) && (int)$aItem['icon'] != 0) {
+            bx_import('BxDolStorage');
+            if(!BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES)->deleteFile((int)$aItem['icon'], 0)) {
+                $this->_echoResultJson(array());
+                exit;
+            }
+        }
+
+        if($this->oDb->updateItem($aItem['id'], array('icon' => '')) !== false)
+            $this->_echoResultJson(array('grid' => $this->getCode(false), 'blink' => $iId, 'preview' => $this->_getIconPreview($aItem['id']), 'eval' => $this->getJsObject() . ".onDeleteIcon(oData)"), true);
+    }
+
+    public function getJsObject()
+    {
+        return 'oBxDolStudioNavigationItems';
+    }
+
+    public function getSetsSelector($sModule = '')
+    {
+        bx_import('BxTemplStudioFormView');
+        $oForm = new BxTemplStudioFormView(array());
+
+        $aInputSets = array(
+            'type' => 'select',
+            'name' => 'set',
+            'attrs' => array(
+                'id' => 'bx-grid-set-' . $this->_sObject,
+                'onChange' => 'javascript:' . $this->getJsObject() . '.onChangeSet()'
+            ),
+            'value' => $this->sSet,
+            'values' => array()
+        );
+
+        $aSets = array();
+        if(!empty($sModule))
+            $this->oDb->getSets(array('type' => 'by_module', 'value' => $sModule), $aSets, false);
+        else
+            $aInputSets['attrs']['disabled'] = 'disabled';
+
+        if(!empty($aSets)) {
+            $aCounter = array();
+            $this->oDb->getItems(array('type' => 'counter_by_sets'), $aCounter, false);
+            foreach($aSets as $aSet)
+                $aInputSets['values'][$aSet['name']] = _t($aSet['title']) . " (" . (isset($aCounter[$aSet['name']]) ? $aCounter[$aSet['name']] : "0") . ")";
+
+            asort($aInputSets['values']);
+        }
+        $aInputSets['values'] = array_merge(array('' => _t('_adm_nav_txt_select_set')), $aInputSets['values']);
+
+        return $oForm->genRow($aInputSets);
+    }
+
+    public function getCode($isDisplayHeader = true)
+    {
+        return $this->_oTemplate->parseHtmlByName('nav_items.html', array(
+            'content' => parent::getCode($isDisplayHeader),
+            'js_object' => $this->getJsObject(),
+            'page_url' => $this->sUrlPage,
+            'grid_object' => $this->_sObject,
+            'params_divider' => $this->sParamsDivider
+        ));
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oTemplate->addJs(array('jquery.form.min.js', 'navigation_items.js', 'navigation_import.js'));
+
+        bx_import('BxTemplStudioFormView');
+        $oForm = new BxTemplStudioFormView(array());
+        $oForm->addCssJs();
+    }
+
+    protected function _getCellIcon ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $mixedValue = $this->_oTemplate->getIcon($mixedValue, array('class' => 'bx-nav-item-icon bx-def-border'));
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellLink ($mixedValue, $sKey, $aField, $aRow)
+    {
+        if($aRow['submenu_object'] != "") {
+            $aMenu = array();
+            $this->oDb->getMenus(array('type' => 'by_object', 'value' => $aRow['submenu_object']), $aMenu, false);
+
+            $sPrefix = _t('_adm_nav_txt_items_gl_link_menu');
+            $aField['chars_limit'] -= strlen($sPrefix);
+
+            $aValue = $this->_limitMaxLength(_t($aMenu['title']), $sKey, $aField, $aRow, $this->_isDisplayPopupOnTextOverflow, false);
+
+            $mixedValue = $sPrefix . ' ' . $this->_oTemplate->parseHtmlByName('bx_a.html', array(
+                'href' => sprintf($this->sUrlViewItems, $aMenu['module'], $aMenu['set_name']),
+                'title' => _t('_adm_nav_txt_manage_items'),
+                'bx_repeat:attrs' => array(),
+                'content' => $aValue[0]
+            )) . (isset($aValue[1]) ? $aValue[1] : '');
+        } else if($aRow['submenu_object'] == "" && $aRow['onclick'] != "")
+            $mixedValue = $this->_limitMaxLength(_t('_adm_nav_txt_items_gl_link_custom'), $sKey, $aField, $aRow, $this->_isDisplayPopupOnTextOverflow);
+        else {
+            bx_import('BxDolPermalinks');
+            $mixedValue = BxDolPermalinks::getInstance()->permalink($mixedValue);
+        }
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellModule($mixedValue, $sKey, $aField, $aRow)
+    {
+        $mixedValue = $this->_limitMaxLength($this->getModuleTitle($aRow['module']), $sKey, $aField, $aRow, $this->_isDisplayPopupOnTextOverflow);
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellVisibleForLevels ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $mixedValue = $this->_oTemplate->parseHtmlByName('bx_a.html', array(
+            'href' => 'javascript:void(0)',
+            'title' => _t('_adm_nav_txt_manage_visibility'),
+            'bx_repeat:attrs' => array(
+                array('key' => 'bx_grid_action_single', 'value' => 'show_to'),
+                array('key' => 'bx_grid_action_data', 'value' => $aRow['id'])
+            ),
+            'content' => BxDolStudioUtils::getVisibilityTitle($aRow['visible_for_levels'])
+        ));
+
+        return parent::_getCellDefault ($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getActionImport ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if($this->sSet == '')
+            $isDisabled = true;
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getActionAdd ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if($this->sSet == '')
+            $isDisabled = true;
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+	protected function _getActionEdit ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if($sType == 'single' && (int)$aRow['editable'] == 0)
+            return '';
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getActionDelete ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if ($sType == 'single' && $aRow['module'] == BX_DOL_STUDIO_MODULE_SYSTEM)
+            return '';
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getActionShowTo ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        return '';
+    }
+
+    protected function _getActionDeleteIcon ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        return '';
+    }
+
+    protected function _getActionsDisabledBehavior($aRow)
+    {
+        return false;
+    }
+
+    protected function _getFilterControls ()
+    {
+        parent::_getFilterControls();
+
+        $sContent = $this->getModulesSelectOne('getItems') . $this->getSetsSelector($this->sModule);
+
+        bx_import('BxTemplStudioFormView');
+        $oForm = new BxTemplStudioFormView(array());
+
+        $aInputSearch = array(
+            'type' => 'text',
+            'name' => 'keyword',
+            'attrs' => array(
+                'id' => 'bx-grid-search-' . $this->_sObject,
+            ),
+            'tr_attrs' => array(
+                'style' => 'display:none;'
+            )
+        );
+        $sContent .= $oForm->genRow($aInputSearch);
+
+        return $sContent;
+    }
+
+    protected function _getFormObject($sAction, $aItem = array())
+    {
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-nav-item-',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction . '&set=' . $this->sSet,
+                'method' => BX_DOL_STUDIO_METHOD_DEFAULT,
+                'enctype' => 'multipart/form-data',
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_menu_items',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'id' => array(
+                    'type' => 'hidden',
+                    'name' => 'id',
+                    'value' => isset($aItem['id']) ? (int)$aItem['id'] : 0,
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'set_name' => array(
+                    'type' => 'hidden',
+                    'name' => 'set_name',
+                    'value' => $this->sSet,
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'onclick' => array(
+                    'type' => 'hidden',
+                    'name' => 'onclick',
+                    'value' => isset($aItem['onclick']) ? $aItem['onclick'] : '',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'title_system' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title_system',
+                    'caption' => _t('_adm_nav_txt_items_title_system'),
+                    'info' => _t('_adm_nav_dsc_items_title_system'),
+                    'value' => isset($aItem['title_system']) ? $aItem['title_system'] : '_adm_nav_txt_item',
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'LengthTranslatable',
+                        'params' => array(3, 100, 'title_system'),
+                        'error' => _t('_adm_nav_err_items_title_system'),
+                    ),
+                ),
+                'title' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title',
+                    'caption' => _t('_adm_nav_txt_items_title'),
+                    'info' => _t('_adm_nav_dsc_items_title'),
+                    'value' => isset($aItem['title']) ? $aItem['title'] : '_adm_nav_txt_item',
+                    'required' => '0',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'submenu_object' => array(
+                    'type' => 'select',
+                    'name' => 'submenu_object',
+                    'caption' => _t('_adm_nav_txt_items_submenu'),
+                    'info' => _t('_adm_nav_dsc_items_submenu'),
+                    'value' => isset($aItem['submenu_object']) ? $aItem['submenu_object'] : '',
+                    'values' => array(),
+                    'required' => '0',
+                    'attrs' => array(
+                        'onChange' => 'javascript:' . $this->getJsObject() . '.onChangeSubmenu(this)'
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'link' => array(
+                    'type' => 'text',
+                    'name' => 'link',
+                    'caption' => _t('_adm_nav_txt_items_link'),
+                    'info' => _t('_adm_nav_dsc_items_link'),
+                    'value' => isset($aItem['link']) ? $aItem['link'] : '',
+                    'required' => '0',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => '',
+                        'params' => array(),
+                        'error' => _t('_adm_nav_err_items_link'),
+                    ),
+                ),
+                'target' => array(
+                    'type' => 'select',
+                    'name' => 'target',
+                    'caption' => _t('_adm_nav_txt_items_target'),
+                    'info' => _t('_adm_nav_dsc_items_target'),
+                    'value' => isset($aItem['target']) ? $aItem['target'] : '_self',
+                    'values' => array(
+                        array('key' => '', 'value' => _t('_adm_nav_txt_items_target_self')),
+                        array('key' => '_blank', 'value' => _t('_adm_nav_txt_items_target_blank'))
+                    ),
+                    'required' => '0',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    )
+                ),
+                'icon' => array(
+                    'type' => 'text',
+                    'name' => 'icon',
+                    'caption' => _t('_adm_nav_txt_items_icon'),
+                    'info' => _t('_adm_nav_dsc_items_icon'),
+                    'value' => '',
+                    'required' => '0',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => '',
+                        'params' => array(),
+                        'error' => _t('_adm_nav_err_items_icon'),
+                    ),
+                ),
+                'icon_image' => array(
+                    'type' => 'file',
+                    'name' => 'icon_image',
+                    'caption' => _t('_adm_nav_txt_items_icon_image'),
+                    'info' => _t('_adm_nav_dsc_items_icon_image'),
+                    'value' => '',
+                    'checker' => array (
+                        'func' => '',
+                        'params' => '',
+                        'error' => _t('_adm_nav_err_items_icon_image'),
+                    ),
+                ),
+                'icon_preview' => array(
+                    'type' => 'custom',
+                    'name' => 'icon_preview',
+                    'caption' => _t('_adm_nav_txt_items_icon_image_old'),
+                    'content' => ''
+                ),
+                'controls' => array(
+                    'name' => 'controls',
+                    'type' => 'input_set',
+                    array(
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_adm_nav_btn_items_add'),
+                    ),
+                    array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_adm_nav_btn_items_cancel'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    )
+                )
+            )
+        );
+
+        $aMenus = array();
+        $this->oDb->getMenus(array('type' => 'all'), $aMenus, false);
+        foreach($aMenus as $aMenu)
+            $aForm['inputs']['submenu_object']['values'][$aMenu['object']] = _t($aMenu['title']);
+
+        asort($aForm['inputs']['submenu_object']['values']);
+        $aForm['inputs']['submenu_object']['values'] = array_merge(array('' => _t('_adm_nav_txt_items_submenu_empty')), $aForm['inputs']['submenu_object']['values']);
+
+        switch($sAction) {
+            case 'add':
+                unset($aForm['inputs']['id']);
+                unset($aForm['inputs']['icon_preview']);
+
+                $aForm['form_attrs']['id'] .= 'create';
+                break;
+
+            case 'edit':
+                unset($aForm['inputs']['set_name']);
+
+                $aForm['form_attrs']['id'] .= 'edit';
+                $aForm['inputs']['icon_image']['caption'] = _t('_adm_nav_txt_items_icon_image_new');
+                $aForm['inputs']['controls'][0]['value'] = _t('_adm_nav_btn_items_save');
+
+                if(($bSubmenu = !empty($aItem['submenu_object'])) === true) {
+                    $aForm['inputs']['link']['tr_attrs']['style'] = 'display:none;';
+                    $aForm['inputs']['target']['tr_attrs']['style'] = 'display:none;';
+                }
+
+                if(!$bSubmenu && ($aItem['onclick'] != "" || !in_array($aItem['target'], array('', '_blank')))) {
+                    $aForm['inputs']['submenu_object']['tr_attrs']['style'] = 'display:none;';
+                    $aForm['inputs']['link']['tr_attrs']['style'] = 'display:none;';
+                    $aForm['inputs']['target']['tr_attrs']['style'] = 'display:none;';
+                }
+
+                $sIconImage = $sIconFont = "";
+                if(!empty($aItem['icon'])) {
+                    if(is_numeric($aItem['icon']) && (int)$aItem['icon'] != 0) {
+                        bx_import('BxDolStorage');
+                        $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+
+                        $sIconImage = $oStorage->getFileUrlById((int)$aItem['icon']);
+                    } else {
+                        $sIconFont = $aItem['icon'];
+                        $aForm['inputs']['icon']['value'] = $sIconFont;
+                    }
+                }
+
+                $aForm['inputs']['icon_preview']['content'] = $this->_getIconPreview($aItem['id'], $sIconImage, $sIconFont);
+                break;
+        }
+
+        return  new BxTemplStudioFormView($aForm);
+    }
+
+    protected function _getIconPreview($iId, $sIconImage = '', $sIconFont = '')
+    {
+        $bIconImage = !empty($sIconImage);
+        $bIconFont = !empty($sIconFont);
+
+        return $this->_oTemplate->parseHtmlByName('nav_item_icon_preview.html', array(
+            'id' => $iId,
+            'bx_if:show_icon_empty' => array(
+                'condition' => !$bIconImage && !$bIconFont,
+                'content' => array()
+            ),
+            'bx_if:show_icon_image' => array(
+                'condition' => $bIconImage,
+                'content' => array(
+                    'js_object' => $this->getJsObject(),
+                    'url' => $sIconImage,
+                    'id' => $iId
+                )
+            ),
+            'bx_if:show_icon_font' => array(
+                'condition' => $bIconFont,
+                'content' => array(
+                    'icon' => $sIconFont
+                )
+            )
+        ));
+    }
+}
+
+/** @} */

+ 712 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/scripts/BxBaseStudioStore.php

@@ -0,0 +1,712 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinView Dolphin Studio Representation classes
+ * @ingroup     DolphinStudio
+ * @{
+ */
+
+bx_import('BxDolStudioStore');
+bx_import('BxTemplStudioFunctions');
+
+class BxBaseStudioStore extends BxDolStudioStore
+{
+    protected $iPerPageDefault = 24;
+
+    function __construct($sPage = "")
+    {
+        parent::__construct($sPage);
+    }
+
+    function getPageCss()
+    {
+        return array_merge(parent::getPageCss(), array('modules.css', 'store.css', 'store-media-tablet.css', 'store-media-desktop.css'));
+    }
+
+    function getPageJs()
+    {
+    	BxDolStudioTemplate::getInstance()->addJsTranslation(array('_adm_btn_queued_submit'));
+        return array_merge(parent::getPageJs(), array('jquery.fancybox.pack.js', 'store.js'));
+    }
+
+    function getPageJsObject()
+    {
+        return 'oBxDolStudioStore';
+    }
+
+    function getPageMenu($aMenu = array(), $aMarkers = array())
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aMenu = array();
+        $aMenuItems = array(
+	        'goodies' => array('icon' => 'home'), 
+	        'featured' => array('icon' => 'thumbs-up'), 
+	        'purchases' => array('icon' => 'shopping-cart'), 
+	        'updates' => array('icon' => 'refresh'), 
+	        'checkout' => array('icon' => 'credit-card'), 
+	        'downloaded' => array('icon' => 'list')
+        );
+        foreach($aMenuItems as $sMenuItem => $aItem)
+            $aMenu[] = array(
+                'name' => $sMenuItem,
+                'icon' => $aItem['icon'],
+                'link' => BX_DOL_URL_STUDIO . 'store.php?page=' . $sMenuItem,
+                'title' => _t('_adm_lmi_cpt_' . $sMenuItem),
+                'selected' => $sMenuItem == $this->sPage
+            );
+
+        bx_import('BxDolStudioCart');
+        $iCounter = BxDolStudioCart::getInstance()->getCount();
+
+        $aMarkers = array(
+            'checkout_counter' => $oTemplate->parseHtmlByName('menu_side_counter.html', array(
+                'bx_if:hide_counter' => array(
+                    'condition' => $iCounter <= 0,
+                    'content' => array()
+                ),
+                'counter' => $iCounter
+            ))
+        );
+
+        return parent::getPageMenu($aMenu, $aMarkers);
+    }
+
+    function getPageCode($bHidden = false)
+    {
+        $sMethod = 'get' . ucfirst($this->sPage) . 'List';
+        if(!method_exists($this, $sMethod))
+            return '';
+
+        return $this->$sMethod();
+    }
+
+    function getPageContent()
+    {
+        $sMethod = 'get' . ucfirst($this->sPage) . 'List';
+        if(!method_exists($this, $sMethod))
+            return '';
+
+        return $this->$sMethod(false);
+    }
+
+    protected function getGoodiesList()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+		$aTmplVars = array(
+            'js_object' => $sJsObject,
+            'bx_repeat:blocks' => array()
+        );
+
+        $mixedResult = BxDolStudioInstallerUtils::getInstance()->getAccessObject(true)->doAuthorize();
+        if($mixedResult === true) {
+	        $aProducts = $this->loadGoodies();
+
+	        $sActions = "";
+	        foreach($aProducts as $aBlock) {
+	            $sItems = $oTemplate->parseHtmlByName('str_products.html', array(
+	                'list' => $this->displayProducts($aBlock['items']),
+	                'paginate' => ''
+	            ));
+
+	            $aTmplVars['bx_repeat:blocks'][] = array(
+	                'caption' => $this->getBlockCaption($aBlock),
+	                'panel_top' => '',
+	                'items' => $sItems,
+	                'panel_bottom' => ''
+	            );
+	        }
+        }
+        else 
+        	$aTmplVars['bx_repeat:blocks'][] = array(
+                'caption' => $this->getBlockCaption(array('caption' => _t('_adm_block_cpt_goodies'), 'actions' => array())),
+                'panel_top' => '',
+                'items' => $mixedResult,
+                'panel_bottom' => ''
+            );
+
+        return $oTemplate->parseHtmlByName('store.html', $aTmplVars);
+    }
+
+    protected function getFeaturedList($bWrapInBlock = true)
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $mixedResult = BxDolStudioInstallerUtils::getInstance()->getAccessObject(true)->doAuthorize();
+        if($mixedResult === true) {
+	        $iStart = (int)bx_get('str_start');
+	        $iPerPage = (int)bx_get('str_per_page');
+	        if(empty($iPerPage))
+	            $iPerPage = $this->iPerPageDefault;
+
+	        $aProducts = $this->loadFeatured($iStart, $iPerPage + 1);
+
+	        bx_import('BxTemplPaginate');
+	        $oPaginate = new BxTemplPaginate(array(
+	            'start' => $iStart,
+	            'per_page' => $iPerPage,
+	            'on_change_page' => $sJsObject . ".changePagePaginate(this, 'featured', {start}, {per_page})"
+	        ));
+	        $oPaginate->setNumFromDataArray($aProducts);
+
+	        $sContent = $oTemplate->parseHtmlByName('str_products.html', array(
+	            'list' => $this->displayProducts($aProducts),
+	            'paginate' => $oPaginate->getSimplePaginate()
+	        ));
+        }
+        else 
+        	$sContent = $mixedResult;
+
+		if(!$bWrapInBlock)
+			return $sContent;
+
+        return $oTemplate->parseHtmlByName('store.html', array(
+            'js_object' => $sJsObject,
+            'bx_repeat:blocks' => array(
+                array(
+                    'caption' => $this->getBlockCaption(array('caption' => _t('_adm_block_cpt_featured'), 'actions' => array())),
+                    'panel_top' => '',
+                    'items' => $sContent,
+                    'panel_bottom' => ''
+                )
+            )
+        ));
+    }
+
+    protected function getModulesList($bWrapInBlock = true)
+    {
+        return $this->getTag('modules', $bWrapInBlock);
+    }
+
+    protected function getTemplatesList($bWrapInBlock = true)
+    {
+        return $this->getTag('templates', $bWrapInBlock);
+    }
+
+    protected function getLanguagesList($bWrapInBlock = true)
+    {
+        return $this->getTag('languages', $bWrapInBlock);
+    }
+
+    protected function getTag($sTag, $bWrapInBlock = true)
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $iStart = (int)bx_get('str_start');
+        $iPerPage = (int)bx_get('str_per_page');
+        if(empty($iPerPage))
+            $iPerPage = $this->iPerPageDefault;
+
+        $aProducts = $this->loadTag($sTag, $iStart, $iPerPage + 1);
+
+        bx_import('BxTemplPaginate');
+        $oPaginate = new BxTemplPaginate(array(
+            'start' => $iStart,
+            'per_page' => $iPerPage,
+            'on_change_page' => $sJsObject . ".changePagePaginate(this, '" . $sTag . "', {start}, {per_page})"
+        ));
+        $oPaginate->setNumFromDataArray($aProducts);
+
+        $sContent = $oTemplate->parseHtmlByName('str_products.html', array(
+            'list' => $this->displayProducts($aProducts),
+            'paginate' => $oPaginate->getSimplePaginate()
+        ));
+
+        if(!$bWrapInBlock)
+            return $sContent;
+
+        return $oTemplate->parseHtmlByName('store.html', array(
+            'js_object' => $sJsObject,
+            'bx_repeat:blocks' => array(
+                array(
+                    'caption' => $this->getBlockCaption(array('caption' => _t('_adm_block_cpt_' . $sTag), 'actions' => array())),
+                    'panel_top' => '',
+                    'items' => $sContent,
+                    'panel_bottom' => ''
+                )
+            )
+        ));
+    }
+
+    protected function getPurchasesList()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aProducts = $this->loadPurchases();
+
+        $aTmplVars = array(
+            'js_object' => $sJsObject,
+            'bx_repeat:blocks' => array(
+                array(
+                    'caption' => $this->getBlockCaption(array('caption' => _t('_adm_block_cpt_purchases'), 'actions' => array())),
+                    'panel_top' => '',
+                    'items' => $this->displayProducts($aProducts),
+                    'panel_bottom' => ''
+                )
+            )
+        );
+
+        return $oTemplate->parseHtmlByName('store.html', $aTmplVars);
+    }
+
+    protected function getUpdatesList()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aUpdates = $this->loadUpdates();
+
+        $aTmplVars = array(
+            'js_object' => $sJsObject,
+            'bx_repeat:blocks' => array(
+                array(
+                    'caption' => $this->getBlockCaption(array('caption' => _t('_adm_block_cpt_updates'), 'actions' => array())),
+                    'panel_top' => '',
+                    'items' => $this->displayUpdates($aUpdates),
+                    'panel_bottom' => '',
+                )
+            )
+        );
+
+        return $oTemplate->parseHtmlByName('store.html', $aTmplVars);
+    }
+
+    protected function getCheckoutList()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        //--- Empty cart by vendor if the payment was accepted.
+        $mixedVendor = bx_get('vendor');
+        $mixedProducts = bx_get('products');
+        if($mixedVendor !== false && $mixedProducts !== false) {
+            bx_import('BxDolStudioCart');
+            $oCart = BxDolStudioCart::getInstance();
+
+            $aProducts = explode(',', base64_decode($mixedProducts));
+            foreach($aProducts as $iId)
+                $oCart->delete($mixedVendor, $iId);
+
+            return $this->getMessage('_adm_block_cpt_checkout', '_adm_msg_modules_success_checkouted');
+        }
+
+        $aVendors = $this->loadCheckout();
+
+        $aTmplVarsBlocks = array();
+        foreach($aVendors as $sName => $aInfo) {
+            $fTotal = 0;
+            $sCurrency = '';
+            foreach($aInfo['products'] as $aProduct) {
+                $iCount = isset($aInfo['counts'][$aProduct['id']]) ? (int)$aInfo['counts'][$aProduct['id']] : 1;
+                $fTotal += $iCount * $aProduct['price'];
+
+                if($sCurrency == '' && isset($aProduct['currency_sign']))
+                    $sCurrency = $aProduct['currency_sign'];
+            }
+
+            $aActions = array(
+                array('name' => 'checkout-' . $sName, 'caption' => '_adm_action_cpt_checkout', 'url' => 'javascript:void(0)', 'onclick' => $sJsObject . ".checkoutCart('" . $sName . "', this);"),
+                array('name' => 'delete-all-' . $sName, 'caption' => '_adm_action_cpt_delete_all', 'url' => 'javascript:void(0)', 'onclick' => $sJsObject . ".deleteAllFromCart('" . $sName . "', this)")
+            );
+
+            $aTmplVarsBlocks[] = array(
+                'caption' => $this->getBlockCaption(array('caption' => _t('_adm_block_cpt_checkout_by_vendor_csign', $sName, $sCurrency, $fTotal), 'actions' => $aActions)),
+                'panel_top' => '',
+                'items' => $this->displayProducts($aInfo['products'], array('is_shopping_cart' => true, 'counts' => $aInfo['counts'])),
+                'panel_bottom' => ''
+            );
+        }
+
+        if(empty($aTmplVarsBlocks))
+            return $this->getMessage('_adm_block_cpt_checkout', '_Empty');
+
+        return $oTemplate->parseHtmlByName('store.html', array(
+            'js_object' => $sJsObject,
+            'bx_repeat:blocks' => $aTmplVarsBlocks
+        ));
+    }
+
+    protected function getDownloadedList()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $sModules = $sUpdates = "";
+        $aProducts = $this->loadDownloaded();
+
+        //--- Prepare modules.
+        foreach($aProducts['modules'] as $aModule) {
+        	$sIcon = BxDolStudioUtils::getModuleIcon($aModule, 'store');
+        	$bIcon = strpos($sIcon, '.') === false;
+
+        	$bInstalled = $aModule['installed'];
+        	$bQueued = !$bInstalled && $this->oDb->isQueued('action', $aModule['dir']);
+        	
+
+            $sModules .= $oTemplate->parseHtmlByName('str_product_v1.html', array(
+                'js_object' => $sJsObject,
+             	'name' => $aModule['name'],
+                'bx_if:icon' => array (
+	                'condition' => $bIcon,
+	                'content' => array('icon' => $sIcon),
+	            ),
+                'bx_if:image' => array (
+	                'condition' => !$bIcon,
+	                'content' => array('icon_url' => $sIcon),
+	            ),
+                'title' => $aModule['title'],
+                'vendor' => $aModule['vendor'],
+                'version' => $aModule['version'],
+                'dir' => $aModule['dir'],
+                'bx_if:hide_install' => array(
+                    'condition' => $bInstalled || $bQueued,
+                    'content' => array()
+                ),
+                'bx_if:hide_queued' => array(
+                    'condition' => !$bQueued,
+                    'content' => array()
+                ),
+                'bx_if:hide_installed' => array(
+                    'condition' => !$bInstalled || $bQueued,
+                    'content' => array()
+                )
+            ));
+        }
+
+        //--- Prepare updates.
+        foreach($aProducts['updates'] as $aUpdate) {
+        	$sIcon = BxDolStudioUtils::getModuleIcon(array('type' => $aUpdate['module_type'], 'name' => $aUpdate['module_name'], 'dir' => $aUpdate['module_dir']), 'store');
+        	$bIcon = strpos($sIcon, '.') === false;
+
+            $sUpdates .= $oTemplate->parseHtmlByName('str_update_v1.html', array(
+                'js_object' => $sJsObject,
+            	'name' => $aUpdate['module_name'],
+                'bx_if:icon' => array (
+	                'condition' => $bIcon,
+	                'content' => array('icon' => $sIcon),
+	            ),
+                'bx_if:image' => array (
+	                'condition' => !$bIcon,
+	                'content' => array('icon_url' => $sIcon),
+	            ),
+                'title' => $aUpdate['title'],
+                'vendor' => $aUpdate['vendor'],
+                'versions' => _t('_adm_str_txt_update_from_to', $aUpdate['version_from'], $aUpdate['version_to']),
+                'dir' => $aUpdate['dir']
+            ));
+        }
+
+        $aTmplVars = array(
+            'js_object' => $sJsObject,
+            'bx_repeat:blocks' => array(
+                array(
+                    'caption' => $this->getBlockCaption(array('caption' => _t('_adm_block_cpt_downloaded_modules'), 'actions' => array())),
+                    'panel_top' => '',
+                    'items' => $sModules,
+                    'panel_bottom' => ''
+                )
+            )
+        );
+
+        if(!empty($sUpdates))
+            $aTmplVars['bx_repeat:blocks'][] = array(
+                'caption' => $this->getBlockCaption(array('caption' => _t('_adm_block_cpt_downloaded_updates'), 'actions' => array())),
+                'panel_top' => '',
+                'items' => $sUpdates,
+                'panel_bottom' => ''
+            );
+
+        return $oTemplate->parseHtmlByName('store.html', $aTmplVars);
+    }
+
+    protected function getMessage($sCaption, $sContent, $aActions = array())
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        return $oTemplate->parseHtmlByName('store.html', array(
+            'js_object' => $sJsObject,
+            'bx_repeat:blocks' => array(
+                array(
+                    'caption' => $this->getBlockCaption(array('caption' => _t($sCaption), 'actions' => $aActions)),
+                	'panel_top' => '',
+                    'items' => MsgBox(_t($sContent)),
+                	'panel_bottom' => ''
+                )
+            )
+        ));
+    }
+
+    protected function getProduct($sModuleName)
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aProduct = $this->loadProduct($sModuleName);
+        if(empty($aProduct) || !is_array($aProduct))
+            return array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => (!empty($aProduct) ? $aProduct : _t('_adm_str_err_no_product_info')));
+
+		$aDownloaded = $this->getDownloadedModules(false);
+
+        $bFree = (int)$aProduct['is_free'] == 1;
+        $bPurchased = (int)$aProduct['is_purchased'] == 1;
+
+        $bDownloadable = (int)$aProduct['is_file'] == 1;
+        $bDownloaded = array_key_exists($sModuleName, $aDownloaded);
+        $bDownload = ($bFree || $bPurchased) && $bDownloadable;
+
+        $bDiscount = !empty($aProduct['discount']);
+        $sVersion = $bDownloaded && version_compare($aDownloaded[$sModuleName]['version'], $aProduct['version']) != 0 ? _t('_adm_str_txt_pv_version_mask', $aDownloaded[$sModuleName]['version'], $aProduct['version']) : $aProduct['version'];
+
+        $iScreenshots = 0;
+        $aScreenshots = array();
+        $bScreenshots = is_array($aProduct['screenshots']) && !empty($aProduct['screenshots']);
+        if($bScreenshots) {
+            $iScreenshots = count($aProduct['screenshots']);
+            foreach($aProduct['screenshots'] as $aScreenshot)
+                $aScreenshots[] = array(
+                    'view_url' => $aProduct['url'],
+                    'image_url' => $aScreenshot['big']
+                );
+        }
+
+        $sContent = $oTemplate->parseHtmlByname('str_product_view.html', array(
+            'id' => $aProduct['id'],
+            'title' => $aProduct['title'],
+            'url' => $aProduct['url'],
+            'author_name' => $aProduct['author_name'],
+            'author_url' => $aProduct['author_url'],
+            'version' => $sVersion,
+            'price' => !$bFree ? _t('_adm_str_txt_price_csign', $aProduct['author_currency_sign'], $aProduct['price']) : _t('_adm_str_txt_price_free'),
+            'bx_if:show_discount' => array(
+                'condition' => !$bFree && $bDiscount,
+                'content' => array(
+                    'discount' => $bDiscount ? _t('_adm_str_txt_pv_discount_off_csign', $aProduct['discount']['percent'], $aProduct['author_currency_sign'], $aProduct['discount']['save']) : ''
+                )
+            ),
+            'category' => $aProduct['category'],
+            'category_url' => $aProduct['category_url'],
+            'tags' => implode(', ', explode(',', $aProduct['tags'])),
+            'reviews' => _t('_adm_str_txt_pv_stats_reviews', $aProduct['reviews_cnt']),
+            'reviews_url' => $aProduct['reviews_url'],
+            'likes' => _t('_adm_str_txt_pv_stats_likes', $aProduct['likes_cnt']),
+            'views' => _t('_adm_str_txt_pv_stats_views', $aProduct['views_cnt']),
+            'created' => $aProduct['created'],
+            'updated' => $aProduct['updated'],
+            'description' => $aProduct['description'],
+            'bx_if:show_screenshots' => array(
+                'condition' => $bScreenshots,
+                'content' => array(
+                    'width' => 222 * $iScreenshots - 20,
+                    'bx_repeat:screenshots' => $aScreenshots
+                )
+            ),
+            'bx_if:show_purchase' => array(
+                'condition' => !$bFree && !$bPurchased,
+                'content' => array(
+                    'js_object' => $sJsObject,
+                    'id' => $aProduct['id'],
+                    'vendor' => $aProduct['author_name'],
+                )
+            ),
+            'bx_if:show_download' => array(
+                'condition' => $bDownload && !$bDownloaded,
+                'content' => array(
+                    'js_object' => $sJsObject,
+                    'file_id' => $aProduct['file_id'],
+                )
+            ),
+            'bx_if:show_download_disabled' => array(
+				'condition' => $bDownload && $bDownloaded,
+				'content' => array()
+			),
+        ));
+
+        return array('code' => BX_DOL_STUDIO_IU_RC_SUCCESS, 'message' => '', 'popup' => PopupBox('bx-std-str-popup-product', $aProduct['title'], $sContent, true), 'screenshots' => $iScreenshots);
+    }
+
+    protected function getFile($iFileId)
+    {
+        $mixedResult = $this->loadFile($iFileId);
+        if($mixedResult === true)
+        	return array('code' => BX_DOL_STUDIO_IU_RC_SUCCESS, 'message' => _t('_adm_str_msg_download_successfully'));
+
+        if(is_string($mixedResult))
+			return array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => (!empty($mixedResult) ? $mixedResult : _t('_adm_str_err_download_failed')));
+
+		if(is_array($mixedResult))
+			return array_merge(array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => _t('_adm_str_err_download_failed')), $mixedResult);
+    }
+
+	protected function getUpdate($sModuleName, $bAutoUpdate = false)
+    {
+        $mixedResult = $this->loadUpdate($sModuleName, $bAutoUpdate);
+        if($mixedResult === true)
+        	return array('code' => BX_DOL_STUDIO_IU_RC_SUCCESS, 'message' => _t('_adm_str_msg_download' . ($bAutoUpdate ? '_and_install' : '') . '_successfully'));
+
+		if(is_string($mixedResult))
+			return array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => (!empty($mixedResult) ? $mixedResult : _t('_adm_str_err_download_failed')));
+
+		if(is_array($mixedResult))
+        	return array_merge(array('code' => BX_DOL_STUDIO_IU_RC_FAILED, 'message' => _t('_adm_str_err_download_failed')), $mixedResult);
+    }
+
+    protected function displayProducts($aItems, $aParams = array())
+    {
+        if(!is_array($aItems))
+            return $aItems;
+
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aDownloaded = $this->getDownloadedModules();
+        $bShoppingCart = isset($aParams['is_shopping_cart']) && $aParams['is_shopping_cart'];
+
+        $sResult = '';
+        foreach($aItems as $aItem) {
+            $bFree = (int)$aItem['is_free'] == 1;
+            $bPurchased = (int)$aItem['is_purchased'] == 1;
+
+            $bDownloadable = (int)$aItem['is_file'] == 1;
+            $bDownloaded = in_array($aItem['name'], $aDownloaded);
+            $bDownload = !$bShoppingCart && ($bFree || $bPurchased) && $bDownloadable;
+            $bQueued = $this->oDb->isQueued('download', $aItem['name']);
+
+            $sPrice = !$bFree ? _t('_adm_str_txt_price_csign', $aItem['currency_sign'], $aItem['price']) : _t('_adm_str_txt_price_free');
+            $sDiscount = !$bFree && !empty($aItem['discount']) ? _t('_adm_str_txt_discount_csign', $aItem['currency_sign'], $aItem['discount']['price']) : '';
+
+            $sIcon = !empty($aItem['thumbnail']['big']) ? $aItem['thumbnail']['big'] : BxDolStudioUtils::getIconDefault(BX_DOL_MODULE_TYPE_MODULE);
+			$bIcon = strpos($sIcon, '.') === false;
+
+            $sResult .= $oTemplate->parseHtmlByName('str_product_v2.html', array(
+                'js_object' => $sJsObject,
+                'id' => $aItem['id'],
+            	'name' => $aItem['name'],
+                'url' => $aItem['url'],
+                'bx_if:icon' => array (
+	                'condition' => $bIcon,
+	                'content' => array('icon' => $sIcon),
+	            ),
+                'bx_if:image' => array (
+	                'condition' => !$bIcon,
+	                'content' => array('icon_url' => $sIcon),
+	            ),
+                'title' => $aItem['title'],
+                'bx_if:show_vendor_price' => array(
+                    'condition' => !$bShoppingCart,
+                    'content' => array(
+                        'vendor' => $aItem['author'],
+                        'price' => $sPrice,
+                        'discount' => $sDiscount,
+                    )
+                ),
+                'bx_if:show_count_price' => array(
+                    'condition' => $bShoppingCart,
+                    'content' => array(
+                        'count' => isset($aParams['counts'][$aItem['id']]) ? (int)$aParams['counts'][$aItem['id']] : 1,
+                        'price' => $sPrice,
+                        'discount' => $sDiscount,
+                    )
+                ),
+                'bx_if:show_purchase' => array(
+                    'condition' => !$bShoppingCart && !$bFree && !$bPurchased,
+                    'content' => array(
+                        'js_object' => $sJsObject,
+                        'id' => $aItem['id'],
+                        'vendor' => $aItem['author']
+                    )
+                ),
+                'bx_if:show_download' => array(
+                    'condition' => $bDownload && !$bQueued && !$bDownloaded,
+                    'content' => array(
+                        'js_object' => $sJsObject,
+                        'file_id' => $aItem['file_id']
+                    )
+                ),
+                'bx_if:show_download_disabled' => array(
+					'condition' => $bDownload && !$bQueued && $bDownloaded,
+					'content' => array()
+				),
+				'bx_if:show_queued_disabled' => array(
+					'condition' => $bDownload && $bQueued && !$bDownloaded,
+					'content' => array()
+				),
+                'bx_if:show_delete' => array(
+                    'condition' => $bShoppingCart,
+                    'content' => array(
+                        'js_object' => $sJsObject,
+                        'id' => $aItem['id'],
+                        'vendor' => $aItem['author']
+                    )
+                )
+            ));
+        }
+
+        return $sResult;
+    }
+
+    protected function displayUpdates($aItems, $aParams = array())
+    {
+        if(!is_array($aItems))
+            return $aItems;
+
+		if(empty($aItems))
+			return MsgBox(_t('_Empty'));
+
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $sResult = '';
+        foreach($aItems as $aItem) {
+            $bDownloadable = (int)$aItem['is_file'] == 1;
+
+            $sIcon = !empty($aItem['thumbnail']['big']) ? $aItem['thumbnail']['big'] : BxDolStudioUtils::getIconDefault(BX_DOL_MODULE_TYPE_MODULE);
+			$bIcon = strpos($sIcon, '.') === false;
+
+            $sResult .= $oTemplate->parseHtmlByName('str_update_v2.html', array(
+                'js_object' => $sJsObject,
+                'id' => $aItem['id'],
+            	'name' => $aItem['name'],
+                'url' => $aItem['url'],
+            	'bx_if:icon' => array (
+	                'condition' => $bIcon,
+	                'content' => array('icon' => $sIcon),
+	            ),
+                'bx_if:image' => array (
+	                'condition' => !$bIcon,
+	                'content' => array('icon_url' => $sIcon),
+	            ),
+                'title' => $aItem['title'],
+                'vendor' => $aItem['author'],
+                'versions' => _t('_adm_str_txt_update_from_to', $aItem['file_version'], $aItem['file_version_to']),
+                'bx_if:show_download' => array(
+                    'condition' => $bDownloadable,
+                    'content' => array(
+	            		'caption' => _t($this->bAuthAccessUpdates ? '_adm_btn_download_submit' : '_adm_btn_install_submit'),
+	            		'on_click' => $sJsObject . "." . ($this->bAuthAccessUpdates ? "getFile(" . $aItem['file_id'] . ", this)" : "getUpdateAndInstall('" . $aItem['name'] . "', this)")
+                    )
+                )
+            ));
+        }
+
+        return $sResult;
+    }
+
+    private function getDownloadedModules($bNamesOnly = true)
+    {
+		bx_import('BxDolStudioInstallerUtils');
+		$aModules = BxDolStudioInstallerUtils::getInstance()->getModules(false);
+
+        return $bNamesOnly ? array_keys($aModules) : $aModules;
+    }
+}
+
+/** @} */

+ 29 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/str_product_v1.html

@@ -0,0 +1,29 @@
+<div class="bx-std-product bx-def-widget-size-max-height bx-def-margin-bottom">
+    <div class="bx-stdp-icon bx-def-widget-size bx-def-round-corners-widget">
+        <a href="javascript:void(0)" onclick="javascript:__js_object__.info('__name__', this)">
+			<bx_if:icon>
+				<img class="bx-def-widget-size" src="<bx_icon_url:wi-empty.png />" />
+				<i class="sys-icon __icon__"></i>
+			</bx_if:icon>
+			<bx_if:image><img class="bx-def-widget-size" src="__icon_url__" /></bx_if:image>
+        </a>
+    </div>
+    <div class="bx-stdp-content bx-def-widget-size-min-height bx-def-widget-size-left-indent bx-def-margin-right">
+        <div class="bx-std-pc-title bx-def-margin-sec-top bx-def-font-large">
+            <a href="javascript:void(0)" onclick="javascript:__js_object__.info('__name__', this)">__title__</a>
+        </div>
+        <div class="bx-std-pc-vendor bx-def-margin-thd-top bx-def-font-small">__vendor__ &bull; <span class="bx-std-pc-version">__version__</span></div>
+        <div class="bx-std-pc-action bx-def-margin-thd-top">
+        	<div class="bx-std-pc-buttons bx-std-pcb-install bx-clearfix" <bx_if:hide_install>style="display:none"</bx_if:hide_install>>
+        		<input class="bx-btn bx-btn-small bx-def-margin-sec-left-auto" type="button" value="<bx_text:_adm_btn_install_submit />" onclick="javascript:__js_object__.install('__dir__', this)" />
+        		<input class="bx-btn bx-btn-small bx-def-margin-sec-left-auto" type="button" value="<bx_text:_adm_btn_delete_submit />" onclick="javascript:__js_object__.remove('__dir__', this)" />
+        	</div>
+        	<div class="bx-std-pc-buttons bx-std-pcb-queued bx-clearfix" <bx_if:hide_queued>style="display:none"</bx_if:hide_queued>>
+            	<input class="bx-btn bx-btn-small bx-btn-disabled" type="button" value="<bx_text:_adm_btn_queued_submit />" />
+            </div>
+            <div class="bx-std-pc-buttons bx-std-pcb-installed bx-clearfix" <bx_if:hide_installed>style="display:none"</bx_if:hide_installed>>
+            	<input class="bx-btn bx-btn-small bx-btn-disabled" type="button" value="<bx_text:_adm_btn_installed_submit />" />
+            </div>
+        </div>
+    </div>
+</div>

+ 39 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/studio/template/str_product_v2.html

@@ -0,0 +1,39 @@
+<div class="bx-std-product bx-def-widget-size-max-height bx-def-margin-bottom" id="bx-std-product-__id__">
+    <div class="bx-stdp-icon bx-def-widget-size bx-def-round-corners-widget">
+        <a href="javascript:void(0)" onclick="javascript:__js_object__.info('__name__', this)">
+            <bx_if:icon>
+				<img class="bx-def-widget-size" src="<bx_icon_url:wi-empty.png />" />
+				<i class="sys-icon __icon__"></i>
+			</bx_if:icon>
+			<bx_if:image><img class="bx-def-widget-size" src="__icon_url__" /></bx_if:image>
+        </a>
+    </div>
+    <div class="bx-stdp-content bx-def-widget-size-min-height bx-def-widget-size-left-indent bx-def-margin-right">
+        <div class="bx-std-pc-title bx-def-margin-sec-top bx-def-font-large">
+            <a href="javascript:void(0)" onclick="javascript:__js_object__.info('__name__', this)">__title__</a>
+        </div>
+        <bx_if:show_vendor_price>
+        	<div class="bx-std-pc-vendor bx-def-margin-thd-top bx-def-font-small">__vendor__ &bull; <span class="bx-std-pc-price">__price__ __discount__</span></div>
+        </bx_if:show_vendor_price>
+        <bx_if:show_count_price>
+        	<div class="bx-std-pc-count bx-def-margin-thd-top bx-def-font-small">__count__ <bx_text:_adm_str_txt_items_by /> <span class="bx-std-pc-price">__price__ __discount__</span></div>
+        </bx_if:show_count_price>
+        <div class="bx-std-pc-action bx-def-margin-thd-top bx-clearfix">
+        	<bx_if:show_purchase>
+            	<input class="bx-btn bx-btn-small bx-std-pc-purchase" type="button" value="<bx_text:_adm_btn_purchase_submit />" onclick="javascript:__js_object__.addToCart('__vendor__', __id__, this)" />
+            </bx_if:show_purchase>
+        	<bx_if:show_download>
+            	<input class="bx-btn bx-btn-small bx-std-pc-download" type="button" value="<bx_text:_adm_btn_download_submit />" onclick="javascript:__js_object__.getFile(__file_id__, this)" />
+            </bx_if:show_download>
+            <bx_if:show_download_disabled>
+            	<input class="bx-btn bx-btn-small bx-btn-disabled bx-std-pc-download" type="button" value="<bx_text:_adm_btn_download_submit />" disabled="disabled" />
+            </bx_if:show_download_disabled>
+            <bx_if:show_queued_disabled>
+            	<input class="bx-btn bx-btn-small bx-btn-disabled bx-std-pc-queued" type="button" value="<bx_text:_adm_btn_queued_submit />" disabled="disabled" />
+            </bx_if:show_queued_disabled>
+            <bx_if:show_delete>
+            	<input class="bx-btn bx-btn-small bx-std-pc-delete" type="button" value="<bx_text:_adm_btn_delete_submit />" onclick="javascript:__js_object__.deleteFromCart('__vendor__', __id__, this)" />
+            </bx_if:show_delete>
+        </div>
+    </div>
+</div>

+ 77 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/_header.html

@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html lang="__lang__">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=__page_charset__" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
+    <title>__page_header__</title>
+    <base href="<bx_url_root />" />
+
+    __meta_info__
+    __page_robots__
+
+    <meta http-equiv="Content-Style-Type" content="text/css" />
+
+    <bx_include_css />
+
+    __dol_images__
+    __dol_lang__
+    __dol_options__
+
+    <bx_include_js />
+    <script type="text/javascript" language="javascript">
+
+        var sUrlRoot = '<bx_url_root />';
+
+        var aSpinnerOpts = {
+            lines: 7, // The number of lines to draw
+            length: 0, // The length of each line
+            width: 8, // The line thickness
+            radius: 7, // The radius of the inner circle
+            corners: 1, // Corner roundness (0..1)
+            rotate: 0, // The rotation offset
+            direction: 1, // 1: clockwise, -1: counterclockwise
+            color: '#3a8686', // #rgb or #rrggbb or array of colors
+            speed: 1, // Rounds per second
+            trail: 60, // Afterglow percentage
+            shadow: false, // Whether to render a shadow
+            hwaccel: false, // Whether to use hardware acceleration
+            className: 'bx-sys-spinner', // The CSS class to assign to the spinner
+            zIndex: 2e9, // The z-index (defaults to 2000000000)
+            top: '10px', // Top position relative to parent in px
+            left: '10px' // Left position relative to parent in px
+        };
+
+        var aSpinnerSmallOpts = $.extend({}, aSpinnerOpts, {
+            lines: 6,
+            width: 6,
+            radius: 4,
+            color: '#333',
+            top: 'auto',
+            left: 'auto'
+        });
+
+        $(document).ready(function () {
+            bx_time('__lang__');
+
+            $('#bx-toolbar').css({opacity: 0.9});
+
+            bx_activate_anim_icons();
+        });
+
+        (function(w) {
+            var dpr = (w.devicePixelRatio === undefined ? 1 : w.devicePixelRatio);
+            if ($.cookie('devicePixelRatio') == dpr || dpr < 2)
+                return;
+            $.cookie('devicePixelRatio', dpr, {expires: 365, path: '/'});
+            window.location.reload();
+        })(window);
+
+    </script>
+    __extra_js__
+
+    <bx_injection:injection_head />
+
+</head>
+
+<body class="bx-def-font bx-def-color-bg-page" <bx_injection:injection_body /> >
+    <bx_injection:injection_header />    

+ 71 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/_sub_header.html

@@ -0,0 +1,71 @@
+
+<bx_include_auto:_header.html />
+<div class="bx-main">
+	<bx_injection:top />
+
+	<div id="bx-toolbar" class="bx-def-z-index-nav">
+	
+	    <div class="bx-def-centered bx-def-padding-sec-left bx-def-padding-sec-right bx-def-page-width">
+	
+	        <div id="bx-toolbar-content" class="bx-clearfix">
+	
+	            <div id="bx-menu-toolbar-1-container">
+	                <bx_menu:sys_toolbar_site />
+	            </div>
+	
+	
+	            <div id="bx-logo-container">
+	
+	                <bx_injection:injection_logo_before />
+	                    __main_logo__
+	                <bx_injection:injection_logo_after />
+	
+	            </div>
+	
+	            <div id="bx-menu-toolbar-2-container">
+	                <bx_menu:sys_toolbar_member />
+	            </div>
+	
+	        </div>
+	
+	    </div>
+	
+	</div>
+	
+	<bx_injection:injection_between_logo_top_menu />
+	
+	<div id="bx-menu-main-bar-wrapper">
+	
+	    <div id="bx-menu-main-bar" class="bx-def-color-bg-sec">
+	
+	        <div class="bx-def-centered bx-def-padding-sec-left bx-def-padding-sec-right bx-def-page-width">
+	
+	            <bx_injection:injection_top_menu_before />
+	
+	            <div id="bx-menu-main-container">
+	                <div id="bx-menu-main-container-scroller"> 
+	                    <bx_menu:sys_site_submenu />
+	                </div>
+	            </div>
+	
+	            <bx_injection:injection_top_menu_after />
+	
+	        </div>
+	
+	    </div>
+	
+	</div>
+	
+	<bx_injection:injection_between_content_menu />
+
+	<div id="bx-content-wrapper">
+
+		__informer__
+		
+		<div id="bx-content-wrapper" class="bx-page-wrapper bx-def-centered bx-def-padding-sec-left bx-def-padding-sec-right bx-def-page-width">
+		
+		    <div id="bx-content-container" class="bx-def-padding-sec-left bx-def-padding-sec-right">
+		
+		        <bx_injection:injection_content_before />
+		
+		        <div id="bx-content-main" class="bx-def-margin-top">

+ 399 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/common.css

@@ -0,0 +1,399 @@
+
+* { -webkit-font-smoothing: antialiased; } 
+
+a {
+    text-decoration:none;
+    outline:none;
+    color:#3e8685; /* 26.13.13: prev val #387877; */
+}
+
+a:hover {
+    text-decoration:underline;
+}
+
+a > img {
+    border:none;
+}
+
+html,
+body {
+	height: 100%; 
+    padding: 0;
+    margin: 0;
+}
+
+blockquote {
+    /* NOTE: this style was copied to editor.css */
+    border-left: 2px solid #ccc;
+    padding-left: 10px;
+}
+
+.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-anchor-invisible,
+.bx-anchor {
+    padding-top:60px;
+    margin-top:-60px;
+    -webkit-background-clip:content-box;
+    background-clip:content-box;
+}
+.bx-anchor-invisible {
+    display:block;
+    display:inline-block;
+    height:0px;
+    width:0px;
+    overflow:hidden;
+}
+
+.sys-icon-a {
+    width:1.5rem;
+    height:1.5rem;
+}
+
+/* --------------- design boxes */
+
+.bx-db-container {
+    position:relative;
+}
+
+.bx-db-title {
+    display:inline-block;
+    float:left;
+    line-height:40px;
+}
+
+.bx-db-menu {
+    line-height:40px;
+    float: right;
+    vertical-align:baseline;
+}
+
+.bx-db-menu-tab {
+    float: left;
+    margin-left:5px;
+}
+
+.bx-db-menu-tab-active {
+    text-decoration:underline;
+}
+
+.bx-db-menu-tab-btn a {
+    text-decoration:none;
+}
+
+/* --------------- popup boxes */
+
+.bx-popup-width {
+	width: 500px;
+}
+
+.bx-popup-border {
+	border:1px solid #383d66;
+}
+
+.bx-popup-color-bg {
+    background-color:#fff;
+}
+
+.bx-popup {    
+    overflow:hidden;
+
+	-moz-box-shadow:0px 0px 0px 1px rgba(255, 255, 255, 0.15), 0px 10px 25px 0px rgba(33, 33, 33, 0.5);
+	-webkit-box-shadow:0px 0px 0px 1px rgba(255, 255, 255, 0.15), 0px 10px 25px 0px rgba(33, 33, 33, 0.5);
+    box-shadow:0px 0px 0px 1px rgba(255, 255, 255, 0.15), 0px 10px 25px 0px rgba(33, 33, 33, 0.5);
+}
+
+.bx-popup-header {
+    position:relative;
+    background-color: #3b8686;
+}
+
+.bx-popup-header .bx-db-menu-tab-btn {
+    padding-top:16px;
+}
+
+.bx-popup-header h2 {
+    margin:0;
+    text-align:center;
+    color:#FFFFFF;
+}
+
+.bx-popup-box-pointer {
+    display:none;
+    position:relative;
+    top:10px;
+    width:13px;
+    height:7px;
+    overflow:hidden;
+}
+
+.bx-popup-box-pointer > i {
+    display:block;
+    position:absolute;
+    top:-5px;
+    width:0px;
+    height:0px;
+    border-width:6px;
+    border-style:solid;
+    border-top-color:transparent;
+    border-right-color:transparent;
+    border-left-color:transparent;
+}
+
+.bx-popup-image-container {
+    position: relative;
+}
+
+.bx-popup-image-btn {
+    position: absolute;
+    right: 0;
+    top: 0;
+}
+
+.bx-popup-image-wrapper img {
+    display: block;
+}
+
+.bx-popup-element-close:hover,
+.bx-popup-element-close {
+    position:absolute;
+    top:0;
+    right:0;
+    text-decoration:none;
+}
+
+.bx-popup-slide-wrapper {
+    opacity:0.9;
+}
+
+.bx-popup-slide-wrapper li {
+    text-align:right;
+}
+
+/* --------------- message box */
+
+.bx-msg-box-container {
+    padding:0px 5px;
+    text-align:center;
+}
+
+.bx-msg-box {
+    display:inline-block; 
+    margin-right:auto;
+    margin-left:auto;
+
+    background-color:rgba(200, 200, 200, 0.3);
+
+    text-align:center;
+    font-weight:bold;
+    font-size:1rem;
+    padding:10px;
+    text-transform:uppercase;
+}
+
+/* --------------- loading indicator */
+
+.bx-loading-ajax {
+    position:absolute;
+    width:50px;
+    height:50px;
+    margin-left:auto;
+    margin-right:auto;
+
+    background-color:#b9ddcc;
+    background-color:rgba(121, 189, 154, 0.5);
+
+	border-radius:5px;
+	-moz-border-radius:5px;
+	-webkit-border-radius:5px;
+
+    text-align:center;
+    color:#CCCCCC;
+}
+
+.bx-loading-ajax-btn {
+    display:inline-block;
+    width:20px;
+    height:20px;
+    margin-left:5px;
+    vertical-align:middle;
+}
+.bx-btn .bx-loading-ajax-btn {
+    margin-top:8px;
+    vertical-align:top;
+}
+
+/* --------------- button */
+
+.bx-btn {
+    position:relative;
+	display:block;	
+    float:left;
+
+    -moz-box-sizing: border-box;
+    -ms-box-sizing: border-box;
+    box-sizing: border-box;
+
+    height:36px;
+    height:2.2rem;
+    margin:0;
+    padding:0px 12px 0px 12px;
+
+    line-height:36px;
+    line-height:2.2rem;
+    font-size:1rem;
+	color:#fff;
+	text-decoration:none;
+    cursor: pointer;
+
+    background-color: #6caa8a;
+
+    border:none;
+	border-radius:3px;
+	-moz-border-radius:3px;
+	-webkit-border-radius:3px;	
+
+    -webkit-transition: background-color 0.2s ease-in-out;
+    -moz-transition: background-color 0.2s ease-in-out;
+    -o-transition: background-color 0.2s ease-in-out;
+    transition: background-color 0.2s ease-in-out;
+}
+.bx-btn:active,
+.bx-btn:hover {	
+    background-color: #79bd9a;
+    text-decoration:none;
+}
+.bx-btn:first-child {
+    margin-left:0;
+}
+button.bx-btn,
+input.bx-btn {    
+	padding-top:0px;
+	padding-bottom:0px;
+    font-size:1rem;
+    color:#fff;
+}
+
+.bx-btn img {
+    width:16px;
+    height:16px;
+}
+.bx-btn u {
+    margin-left:2px;
+    text-decoration:none;
+}
+.bx-btn .sys-icon {
+    font-size:1.2em;
+}
+.bx-btn .sys-icon-bigger {
+    font-size:1.4em;
+}
+
+.bx-btn.bx-btn-small {
+    height:24px; 
+    height:1.6rem; 
+    padding-left:0px 8px 0px 8px;
+
+    line-height:24px;
+    line-height:1.5rem;
+    font-size:0.9rem;
+}
+
+.bx-btn.bx-btn-primary {
+    background-color: #357877;
+}
+.bx-btn.bx-btn-primary:hover,
+.bx-btn.bx-btn-primary:active { 
+    background-color: #3a8686;
+}
+
+.bx-btn.bx-btn-disabled:active,
+.bx-btn.bx-btn-disabled:hover,
+.bx-btn.bx-btn-disabled {
+    background-color: #ddd;
+}
+
+.bx-btn.bx-btn-symbol {
+    font-family: "Times New Roman", Georgia, Serif;
+    font-size:1.3rem;
+    line-height:32px;
+}
+.bx-btn.bx-btn-symbol:hover {
+    text-decoration:none;
+}
+.bx-btn.bx-btn-symbol-small {
+    font-size:1rem;
+}
+
+.bx-btn-group .bx-btn {
+    margin-left:0px;
+    margin-right:0px;
+}
+.bx-btn-group .bx-btn:not(:first-child):after {
+    content:"";
+    position:absolute;
+    display:block;
+    top:0px;
+    left:-0.05em;
+    width:0.1em;
+    height:100%;
+    background-color:rgba(255,255,255,0.1);
+}
+.bx-btn-group .bx-btn + .bx-btn:not(:last-child) {
+	border-radius:0px;
+	-moz-border-radius:0px;
+	-webkit-border-radius:0px;    
+}
+.bx-btn-group .bx-btn:first-child {
+    -webkit-border-top-right-radius: 0px;
+    -webkit-border-bottom-right-radius: 0px;
+    -moz-border-radius-topright: 0px;
+    -moz-border-radius-bottomright: 0px;
+    border-top-right-radius: 0px;
+    border-bottom-right-radius: 0px;
+}
+.bx-btn-group .bx-btn:last-child {
+    -webkit-border-top-left-radius: 0px;
+    -webkit-border-bottom-left-radius: 0px;
+    -moz-border-radius-topleft: 0px;
+    -moz-border-radius-bottomleft: 0px;
+    border-top-left-radius: 0px;
+    border-bottom-left-radius: 0px;
+}
+
+/* --------------- button at the end of cut string */
+
+.bx-str-limit {
+    height:10px;
+    width:14px;    
+    margin-left:3px;
+
+	border-radius:3px;
+	-moz-border-radius:3px;
+	-webkit-border-radius:3px;
+    border-collapse:separate;
+
+	-moz-box-shadow:0px 0px 1px rgba(92, 92, 92, 0.8);
+	-webkit-box-shadow:0px 0px 1px rgba(92, 92, 92, 0.8);
+    box-shadow:0px 0px 1px rgba(92, 92, 92, 0.8);
+
+    cursor:pointer;
+}
+
+/* --------------- */

+ 521 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/default.less

@@ -0,0 +1,521 @@
+@import url(_mixins.less);
+
+.bx-def-page-width {
+    width:@bx-page-width;
+}
+
+.bx-def-box-sizing {
+    .box-sizing(border-box);
+}
+
+/* margins */
+.bx-def-centered {
+	margin: 0px auto;
+}
+
+.bx-def-margin { 
+    .margin(@bx-margin);
+}
+.bx-def-margin-neg { 
+    .margin(-@bx-margin);
+}
+.bx-def-margin-rightbottomleft {
+    .margin-right(@bx-margin);
+    .margin-bottom(@bx-margin);
+    .margin-left(@bx-margin);
+}
+.bx-def-margin-leftright {
+    .margin-left(@bx-margin);
+    .margin-right(@bx-margin);
+}
+.bx-def-margin-leftright-neg {
+    .margin-left(-@bx-margin);
+    .margin-right(-@bx-margin);
+}
+.bx-def-margin-topbottom {
+    .margin-top(@bx-margin);
+    .margin-bottom(@bx-margin);
+}
+.bx-def-margin-top {
+    .margin-top(@bx-margin);
+}
+.bx-def-margin-top-auto {
+    .margin-top(@bx-margin);
+    &:first-child {
+        .margin-top(0px);
+    }
+}
+.bx-def-margin-right {
+    .margin-right(@bx-margin);
+}
+.bx-def-margin-bottom {
+    .margin-bottom(@bx-margin);
+}
+.bx-def-margin-left {
+    .margin-left(@bx-margin);
+}
+.bx-def-margin-left-auto {
+    .margin-left(@bx-margin);
+    &:first-child {
+        .margin-left(0);
+    }
+}
+.bx-def-margin-right-auto {
+    .margin-right(@bx-margin);
+    &:last-child {
+        .margin-right(0);
+    }
+}
+
+.bx-def-margin-sec {
+    .margin(@bx-margin-sec);
+}
+.bx-def-margin-sec-neg {
+    .margin(-@bx-margin-sec);
+}
+.bx-def-margin-sec-rightbottomleft {
+    .margin-right(@bx-margin-sec);
+    .margin-bottom(@bx-margin-sec);
+    .margin-left(@bx-margin-sec);
+}
+.bx-def-margin-sec-leftright {
+    .margin-left(@bx-margin-sec);
+    .margin-right(@bx-margin-sec);
+}
+.bx-def-margin-sec-leftright-neg {
+    .margin-left(-@bx-margin-sec);
+    .margin-right(-@bx-margin-sec);
+}
+.bx-def-margin-sec-topbottom {
+    .margin-top(@bx-margin-sec);
+    .margin-bottom(@bx-margin-sec);
+}
+.bx-def-margin-sec-top {
+    .margin-top(@bx-margin-sec);
+}
+.bx-def-margin-sec-top-auto {
+    .margin-top(@bx-margin-sec);
+    &:first-child {
+        .margin-top(0);
+    }
+}
+.bx-def-margin-sec-right {
+    .margin-right(@bx-margin-sec);
+}
+.bx-def-margin-sec-bottom {
+    .margin-bottom(@bx-margin-sec);
+}
+.bx-def-margin-sec-left {
+    .margin-left(@bx-margin-sec);
+}
+.bx-def-margin-sec-left-auto {
+    .margin-left(@bx-margin-sec);
+    &:first-child {
+        .margin-left(0);
+    }
+}
+.bx-def-margin-sec-right-auto {
+    .margin-left(@bx-margin-sec);
+    &:last-child {
+        .margin-left(0);
+    }
+}
+
+.bx-def-margin-thd {
+    .margin(@bx-margin-thd);
+}
+.bx-def-margin-thd-rightbottomleft {
+    .margin-right(@bx-margin-thd);
+    .margin-bottom(@bx-margin-thd);
+    .margin-left(@bx-margin-thd);
+}
+.bx-def-margin-thd-leftright {
+    .margin-left(@bx-margin-thd);
+    .margin-right(@bx-margin-thd);
+}
+.bx-def-margin-thd-leftright-neg {
+	.margin-left(-@bx-margin-thd);
+    .margin-right(-@bx-margin-thd);
+}
+.bx-def-margin-thd-topbottom {
+    .margin-top(@bx-margin-thd);
+    .margin-bottom(@bx-margin-thd);
+}
+.bx-def-margin-thd-top {
+    .margin-top(@bx-margin-thd);
+}
+.bx-def-margin-thd-top-auto {
+    .margin-top(@bx-margin-thd);
+    &:first-child {
+        .margin-top(0);
+    }
+}
+.bx-def-margin-thd-right {
+    .margin-right(@bx-margin-thd);
+}
+.bx-def-margin-thd-bottom {
+    .margin-bottom(@bx-margin-thd);
+}
+.bx-def-margin-thd-left {
+    .margin-left(@bx-margin-thd);
+}
+
+/* paddings */
+.bx-def-padding {
+    .padding(@bx-padding);
+}
+.bx-def-padding-rightbottomleft {
+    .padding-right(@bx-padding);
+    .padding-bottom(@bx-padding);
+    .padding-left(@bx-padding);
+}
+.bx-def-padding-leftright {
+    .padding-left(@bx-padding);
+    .padding-right(@bx-padding);
+}
+.bx-def-padding-topbottom {
+    .padding-top(@bx-padding);
+    .padding-bottom(@bx-padding);
+}
+.bx-def-padding-top {
+    .padding-top(@bx-padding);
+}
+.bx-def-padding-right {
+    .padding-right(@bx-padding);
+}
+.bx-def-padding-bottom {
+    .padding-bottom(@bx-padding);
+}
+.bx-def-padding-left {
+    .padding-left(@bx-padding);
+}
+
+.bx-def-padding-sec {
+    .padding(@bx-padding-sec);
+}
+.bx-def-padding-sec-rightbottomleft {
+    .padding-right(@bx-padding-sec);
+    .padding-bottom(@bx-padding-sec);
+    .padding-left(@bx-padding-sec);
+}
+.bx-def-padding-sec-topbottom {
+    .padding-top(@bx-padding-sec);
+    .padding-bottom(@bx-padding-sec);
+}
+.bx-def-padding-sec-leftright {
+    .padding-left(@bx-padding-sec);
+    .padding-right(@bx-padding-sec);
+}
+.bx-def-padding-sec-top {
+    .padding-top(@bx-padding-sec);
+}
+.bx-def-padding-sec-right {
+    .padding-right(@bx-padding-sec);
+}
+.bx-def-padding-sec-bottom {
+    .padding-bottom(@bx-padding-sec);
+}
+.bx-def-padding-sec-left {
+    .padding-left(@bx-padding-sec);
+}
+
+.bx-def-padding-thd {
+    .padding(@bx-padding-thd);
+}
+.bx-def-padding-thd-rightbottomleft {
+	.padding-right(@bx-padding-thd);
+	.padding-bottom(@bx-padding-thd);
+    .padding-left(@bx-padding-thd);
+}
+.bx-def-padding-thd-topbottom {
+    .padding-top(@bx-padding-thd);
+    .padding-bottom(@bx-padding-thd);
+}
+.bx-def-padding-thd-leftright {
+    .padding-left(@bx-padding-thd);
+    .padding-right(@bx-padding-thd);
+}
+.bx-def-padding-thd-top {
+    .padding-top(@bx-padding-thd);
+}
+.bx-def-padding-thd-right {
+    .padding-right(@bx-padding-thd);
+}
+.bx-def-padding-thd-bottom {
+    .padding-bottom(@bx-padding-thd);
+}
+.bx-def-padding-thd-left {
+    .padding-left(@bx-padding-thd);
+}
+
+/* colors */
+.bx-def-color-bg-page {
+    .background-color (@bx-color-page);
+}
+.bx-def-color-bg-block {
+    .background-color (@bx-color-block);
+}
+.bx-def-color-bg-box {
+    .background-color (@bx-color-box);
+}
+.bx-def-color-bg-sec {
+    .background-color (@bx-color-sec);
+}
+
+.bx-def-color-bg-hl,
+.bx-def-color-bg-hl-even:nth-child(even),
+.bx-def-color-bg-hl-hover:hover {
+    .background-color (@bx-color-hl);
+}
+
+.bx-def-color-bg-active {
+    .background-color (@bx-color-active);
+}
+
+/* hr */
+.bx-def-hr,
+.bx-def-hr-auto {
+    margin-left:0;
+    margin-right:0;
+    padding:0;
+    border:0px;
+    border-top:@bx-border-width @bx-border-type @bx-border-color;
+}
+.bx-def-hr-auto:first-child {
+	display: none;
+}
+
+/* border for common layout elements likedesign boxes, pads, menu items, etc */
+.bx-def-border {
+    border:@bx-border-width @bx-border-type @bx-border-color;
+}
+.bx-def-border-rightbottomleft {
+    border-right:@bx-border-width @bx-border-type @bx-border-color;
+    border-bottom:@bx-border-width @bx-border-type @bx-border-color;
+    border-left:@bx-border-width @bx-border-type @bx-border-color;
+}
+.bx-def-border-top {
+    border-top:@bx-border-width @bx-border-type @bx-border-color;
+}
+.bx-def-border-top-auto {
+    border-top:@bx-border-width @bx-border-type @bx-border-color;
+    &:first-child {
+        border-top:none;
+    }
+}
+.bx-def-border-right {
+    border-right:@bx-border-width @bx-border-type @bx-border-color;
+}
+.bx-def-border-bottom {
+    border-bottom:@bx-border-width @bx-border-type @bx-border-color;
+}
+.bx-def-border-left {
+    border-left:@bx-border-width @bx-border-type @bx-border-color;
+}
+.bx-def-border-right {
+    border-right:@bx-border-width @bx-border-type @bx-border-color;
+}
+
+/* shadow */
+.bx-def-shadow {
+    drop-shadow (0px, 1px, 3px, 0px, rgba(51, 51, 51, 0.1));
+}
+
+/* round corders */
+.bx-def-round-corners {
+    .radius(@bx-round-corners-radius);
+}
+
+/* fonts */
+
+/* NOTE: bx-def-font, h1, h2, h3 styles were copied to editor.css */
+
+.bx-def-font {
+    font-family:@bx-font-family;
+    font-weight:400; /* 26.13.13: prev val 300 */
+    .font-size(@bx-font-size-default);
+    color:@bx-font-color-default;
+}
+
+.bx-def-font-inputs {
+    font-family:@bx-font-family;
+    font-weight:400; /* 27.13.13: prev val 300 */
+    .font-size(@bx-font-size-default);
+    color:@bx-font-color-default;
+}
+
+.bx-def-font-grayed {
+    color:@bx-font-color-grayed;
+}
+
+.bx-def-font-contrasted {
+    color:@bx-font-color-contrasted;
+}
+
+.bx-def-font-small {
+    .font-size(@bx-font-size-small);
+}
+
+.bx-def-font-middle {
+    .font-size(@bx-font-size-middle);
+}
+
+.bx-def-font-large {
+    .font-size(@bx-font-size-large);
+}
+
+h1,
+.bx-def-font-h1 {
+    .font-size(@bx-font-size-h1);
+    font-weight:500;
+}
+
+h2,
+.bx-def-font-h2 {
+    .font-size(@bx-font-size-h2);
+    font-weight:500;
+}
+
+h3,
+.bx-def-font-h3 {
+    .font-size(@bx-font-size-h3);
+    font-weight:500;
+}
+
+/* image/thumbnail/gallery default styles */
+
+.bx-def-ava,
+.bx-def-thumb,
+.bx-def-icon {
+    .radius(80px);
+    overflow:hidden;
+}
+
+
+.bx-def-ava-size {
+    .width(@bx-size-avatar);
+    .height(@bx-size-avatar);
+}
+.bx-def-ava-size-max-height {
+    .max-height(@bx-size-avatar);
+}
+.bx-def-ava-size-min-height {
+    .min-height(@bx-size-avatar);
+}
+
+
+.bx-def-thumb-size {
+    .width(@bx-size-thumb);
+    .height(@bx-size-thumb);
+    .line-height(@bx-size-thumb);
+}
+.bx-def-thumb-size-max-height {
+    .max-height(@bx-size-thumb);
+}
+.bx-def-thumb-size-min-height {
+    .min-height(@bx-size-thumb);
+}
+.bx-def-thumb-size-left-indent {
+	.margin-left(@bx-size-thumb + @bx-margin);
+}
+
+.bx-def-icon-size {
+    .width(@bx-size-icon);
+    .height(@bx-size-icon);
+}
+.bx-def-icon-size-max-height {
+    .max-height(@bx-size-icon);
+}
+.bx-def-icon-size-min-height {
+    .min-height(@bx-size-icon);
+}
+
+
+.bx-def-unit {
+    position:relative;
+    white-space:nowrap;
+}
+.bx-def-unit .bx-def-unit-info,
+.bx-def-unit .bx-def-unit-img,
+.bx-def-unit img {
+    display:inline;
+    display:inline-block;
+    vertical-align:middle;
+    .line-height(19px);
+}
+.bx-def-unit-info {
+    .box-sizing(border-box);
+
+    text-overflow:ellipsis;
+    overflow:hidden;
+}
+.bx-def-unit-alert-middle,
+.bx-def-unit-alert {
+    display:block;
+    position:absolute;
+    right:0;
+    bottom:0;
+    .min-width(@bx-font-size-small + 5);
+    .height(@bx-font-size-small + 5);
+    .line-height(@bx-font-size-small + 6);
+    .radius(@bx-font-size-small);
+    .border (1px, solid, #fff);
+    .font-size(@bx-font-size-small);
+    text-align:center;
+    color:white;
+}
+.bx-def-unit-alert-middle {
+    .min-width(@bx-font-size-middle + 2);
+    .height(@bx-font-size-middle + 2);
+    .line-height(@bx-font-size-middle + 1);
+    .radius(@bx-font-size-middle);
+    .font-size(@bx-font-size-middle);
+    .padding(3px);
+}
+
+.bx-def-gal-img-column {
+    .column-width-raw(100%);
+}
+.bx-def-gal-img-size {
+    max-width:100%;
+    height:auto;
+}
+.bx-def-gal-div-size {
+    max-width:100%;
+    .min-height(@bx-size-gallery-img-height);
+}
+.bx-def-gal-unit-column {
+    .column-width-raw(100%);
+}
+.bx-def-gal-unit-size {
+    display:inline-block;
+    width:100%;
+    height:auto;
+    vertical-align:top;
+    overflow:hidden;
+    .box-sizing();
+}
+div[min-width~="480px"][max-width~="720px"] .bx-def-gal-unit-size {
+    width:50%;
+}
+div[min-width~="720px"] .bx-def-gal-unit-size {
+    width:33.3%;
+}
+
+/* z-index */
+
+.bx-def-z-index-front {
+    z-index:100;
+}
+
+.bx-def-z-index-nav {
+    z-index:400;
+}
+
+.bx-def-z-index-overlay {
+    z-index:700;
+}
+
+.bx-def-z-index-modal {
+    z-index:1000;
+}

+ 125 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/general.css

@@ -0,0 +1,125 @@
+.bx-main {
+	position: relative;
+	min-height: 100%;
+}
+
+.bx-page-image-container img,
+.bx-page-html-container img {
+    max-width:100%;
+    height:auto;
+}
+.bx-page-html-container {
+    overflow:hidden;
+}
+
+/* --------------- toolbar */
+
+#bx-toolbar {
+    position:fixed;
+    width:100%;
+    height:48px;
+    height:3rem;
+
+    background-color:#3B8686;
+
+    opacity:0;
+
+    -webkit-transition: opacity .2s;
+    transition: opacity .2s;
+}
+
+#bx-toolbar-content {
+    position: relative;
+}
+
+#bx-menu-toolbar-1-container,
+#bx-menu-toolbar-2-container {
+    position:absolute;
+    top:0px;
+}
+
+#bx-menu-toolbar-1-container {
+    left:0px;
+    text-align:left;
+}
+
+#bx-menu-toolbar-2-container {
+    right:0px;
+    text-align:right;
+}
+
+#bx-logo-container {
+    -moz-box-sizing: border-box;
+    -ms-box-sizing: border-box;
+    box-sizing: border-box;
+
+    height: 48px;
+    height: 3rem;
+           
+    margin:0 48px;
+    margin:0 3rem;
+
+    line-height: 48px;
+    line-height: 3rem;
+    
+    vertical-align: middle;
+    text-align: center;
+
+    overflow:hidden;
+    text-overflow:ellipsis;
+}
+
+#bx-logo-container a {
+    text-decoration:none;
+    font-size:32px;
+    font-size:2rem;
+}  
+
+/* --------------- menu bar */
+
+#bx-menu-main-bar-wrapper {
+    padding-top:48px;
+    padding-top:3rem;
+}
+
+/* --------------- content */
+#bx-content-wrapper {
+	padding-bottom: 100px;
+	padding-bottom: 6.25rem;
+}
+
+/* --------------- footer */
+#bx-footer-wrapper {
+	position: absolute;
+
+	width: 100%;
+	height: 100px;
+	height: 6.25rem;
+	bottom: 0;
+}
+#bx-footer {
+    padding-bottom:60px;
+}
+#bx-menu-bottom {
+	float: left;
+}
+#bx-footer-copyright {
+    float:right;
+}
+
+/* --------------- social buttons */
+
+.bx-social-sharing-btn {
+    float:left;
+}
+
+.bx-social-sharing-btn a,
+.bx-social-sharing-btn .sys-icon {
+    text-decoration:none;
+    vertical-align:middle;
+}
+.bx-social-sharing-btn span {
+    font-size:1.5rem;
+    vertical-align:middle;
+    margin:0 0.2rem;
+}

+ 90 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/media-phone.css

@@ -0,0 +1,90 @@
+@media (max-width:720px) {
+
+html {
+    font-size:70%;
+}
+
+.bx-def-media-phone-hide {
+    display:none;
+}
+.bx-def-media-phone-show-block {
+    display:block;
+}
+
+
+.bx-def-constraint-all *,
+.bx-def-constraint-list ol, .bx-def-constraint-list ul,
+.bx-def-constraint-pre pre,
+.bx-def-constraint-object object,
+.bx-def-constraint-iframe iframe,
+.bx-def-constraint-img img {
+    -moz-box-sizing: border-box;
+    -ms-box-sizing: border-box;
+    -webkit-box-sizing: border-box;
+    box-sizing: border-box;
+
+    max-width:300px !important;
+    overflow:hidden !important;
+}
+.bx-def-constraint-img img {
+    height:auto !important;
+}
+.bx-def-constraint-pre pre {
+    overflow:scroll !important;
+}
+
+
+/* default.css */ 
+
+.bx-def-page-width {
+    width:auto;
+}
+
+/* common.css */
+
+.bx-popup-box-wrapper,
+.bx-popup-box {
+    position:fixed !important;
+    width:100%;
+	width:100vw;
+    height:100%;
+    height:100vh;
+    overflow:scroll;
+}
+
+.bx-popup-box > div {
+	border-radius:0;
+	-moz-border-radius:0;
+	-webkit-border-radius:0;
+}
+
+.bx-popup-image-wrapper .bx-popup-border,
+.bx-popup-box-wrapper .bx-popup-border {
+    border:none;
+}
+
+.bx-popup-image-wrapper .bx-popup,
+.bx-popup-box-wrapper .bx-popup {
+	-moz-box-shadow:none;
+	-webkit-box-shadow:none;
+    box-shadow:none;
+}
+
+/* page layouts */
+
+.bx-layout-col {
+    float:none !important;
+    width:auto !important;
+}
+
+.bx-layout-cell {
+    padding:0 !important;
+}
+
+/* menu.css */
+
+ul.bx-menu-toolbar li a span {
+    display:none;
+}
+
+} /* @media end */

+ 418 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/menu.css

@@ -0,0 +1,418 @@
+/* --------------- horizontal menu */
+
+ul.bx-menu-hor {
+    padding:0;
+    margin:0;
+    list-style:none;
+}
+
+.bx-menu-hor li {
+    float:left;
+}
+
+.bx-menu-hor li a {
+    display:block;
+    text-decoration:none;
+}
+
+.bx-menu-hor-inline li a {
+	display:inline;
+}
+
+.bx-menu-hor li.bx-menu-tab-active a {
+    color:#000;
+}
+
+.bx-menu-hor li img,
+.bx-menu-hor li .sys-icon {
+	display:inline-block;
+}
+
+/* --------------- vertical, vertical lite, notifications menu */
+
+ul.bx-menu-notif,
+ul.bx-menu-verlit,
+ul.bx-menu-ver {
+    padding:0;
+    margin:0;
+    list-style:none;
+}
+
+.bx-menu-ver li {
+    margin-top:1px;
+}
+
+.bx-menu-notif li:first-child,
+.bx-menu-verlit li:first-child,
+.bx-menu-ver li:first-child {
+    margin-top:0px;
+}
+
+.bx-menu-verlit li a,
+.bx-menu-ver li a {
+    display:block;
+    text-decoration:none;
+}
+
+.bx-menu-notif li.bx-menu-tab-active a,
+.bx-menu-verlit li.bx-menu-tab-active a,
+.bx-menu-ver li.bx-menu-tab-active a {
+    color:#000;
+}
+
+.bx-menu-notif li img,
+.bx-menu-notif li .sys-icon,
+.bx-menu-notif li span,
+.bx-menu-verlit li img,
+.bx-menu-verlit li .sys-icon,
+.bx-menu-verlit li span,
+.bx-menu-ver li img,
+.bx-menu-ver li .sys-icon,
+.bx-menu-ver li span {
+    display:inline-block;
+    vertical-align:middle;
+}
+
+.bx-menu-notif li img,
+.bx-menu-notif li .sys-icon,
+.bx-menu-verlit li img,
+.bx-menu-verlit li .sys-icon,
+.bx-menu-ver li img,
+.bx-menu-ver li .sys-icon {
+    width:20px;
+    width:1.25rem;
+    height:20px;
+    height:1.25rem;
+    margin-right:2px;
+    margin-right:0.125rem;
+    text-align:center;
+}
+
+.bx-menu-ver li img {
+    background-position:center center;
+    background-repeat:no-repeat;
+}
+
+.bx-menu-ver li .sys-icon {
+    font-size:20px;
+    font-size:1.25rem;
+}
+
+.bx-menu-notif li .sys-icon {
+    font-size:24px;
+    font-size:1.5rem;
+    width:24px;
+    width:1.5rem;
+    height:24px;
+    height:1.5rem;
+    margin-right:5px;
+    margin-right:0.3rem;
+}
+
+.bx-menu-ver .bx-def-hr {
+    margin:0;
+}
+
+.bx-menu-verlit li img {
+    width:24px;
+    width:1.5rem;
+    height:24px;
+    height:1.5rem;
+    background-position:center center;
+    background-repeat:no-repeat;
+    vertical-align:middle;
+}
+
+.bx-menu-notif .bx-def-unit-alert-middle {
+    position:relative;
+    float:right;
+}
+
+/* --------------- main menu */
+
+#bx-menu-main-container {
+    height:36px;
+    height:2.2rem;
+    white-space: nowrap;
+    overflow:hidden;    
+}
+
+#bx-menu-main-container-scroller {
+    overflow:auto;
+    height:60px;
+    height:4rem;
+}
+
+#bx-menu-main-submenu,
+#bx-menu-main {
+    display:inline-block;
+    line-height:36px;
+    line-height:2.2rem;
+    vertical-align:top;
+}
+
+#bx-menu-main-container #bx-menu-main-submenu:first-child .bx-menu-main-submenu-div:first-child {
+    display:none;
+}
+
+.bx-menu-main-submenu-div .sys-icon {
+    font-size:32px;
+    font-size:2rem;
+}
+
+ul.bx-menu-main-submenu,
+ul.bx-menu-main {    
+    padding:0;
+    margin:0;
+    list-style:none;
+    white-space:nowrap;
+}
+
+.bx-menu-main-submenu li,
+.bx-menu-main li {
+    display:inline-block;
+    margin:0;
+    vertical-align:top;
+}
+
+.bx-menu-main li a,
+.bx-menu-main li span,
+.bx-menu-main li .sys-icon,
+.bx-menu-main-submenu li a {
+    text-decoration:none;
+    font-size:16px;
+    font-size:1.2rem;
+}
+.bx-menu-main li .sys-icon {
+    font-size:32px;
+    font-size:2rem;
+}
+
+.bx-menu-main-submenu li.bx-menu-tab-active a,
+.bx-menu-main li span {
+    color:#000;
+}
+
+/* --------------- toolbar menu */
+
+#bx-menu-toolbar-1-container .bx-menu-toolbar {
+    float:left;
+}
+
+#bx-menu-toolbar-2-container .bx-menu-toolbar {
+    float:right;
+}
+
+.bx-menu-toolbar {
+    list-style:none;
+    margin:0 0 0 0;
+    padding:0;
+}
+ul.bx-menu-toolbar li {
+    float:left;
+    margin:0px;
+} 
+
+ul.bx-menu-toolbar li a {
+    position:relative;
+    display:block;
+    min-width:48px;
+    min-width:3rem;
+    height:48px;
+    height:3rem;
+
+    line-height:48px;
+    line-height:3rem;
+    font-size:16px;
+    font-size:1rem;
+    text-align: center;
+    vertical-align:middle;
+    text-decoration:none;
+}
+
+ul.bx-menu-toolbar li a .sys-icon, 
+ul.bx-menu-toolbar li a img,
+ul.bx-menu-toolbar li a span {
+    vertical-align:top;
+}
+ul.bx-menu-toolbar li a span {
+    margin-right:0;
+}
+
+ul.bx-menu-toolbar li:first-child a {
+    margin-left:0;
+}
+
+ul.bx-menu-toolbar li img {
+    background-position:center center;
+    background-repeat:no-repeat;
+}
+
+ul.bx-menu-toolbar li .sys-icon {
+    display:inline-block;
+    vertical-align:middle;
+    font-size:32px;
+    font-size:2rem;
+    text-align:center;
+}
+
+
+/* --------------- floating blocks menu */
+
+.bx-popup-content .bx-menu-floating-blocks {
+    width:256px;
+    width:16rem;
+}
+.bx-popup-content .bx-menu-account-popup .bx-menu-floating-blocks {
+    width:auto;
+}
+
+ul.bx-menu-floating-blocks-big,
+ul.bx-menu-floating-blocks {
+    padding:0;
+    margin:0;
+    list-style:none;    
+}
+
+.bx-menu-floating-blocks-big li,
+.bx-menu-floating-blocks li {
+    display: inline-block;
+    /* For IE 7 */
+    zoom: 1;
+    *display: inline;
+    vertical-align:top;    
+
+    width:64px;
+    width:4rem;
+    height:64px;
+    height:4rem;
+}
+.bx-menu-floating-blocks-big li {
+    width:256px;
+    width:16rem;
+    height:256px;
+    height:16rem;
+}
+
+.bx-menu-floating-blocks-big li:hover,
+.bx-menu-floating-blocks li:hover {
+    opacity:0.8;
+}
+
+.bx-menu-floating-blocks-big li:active,
+.bx-menu-floating-blocks-big li.bx-menu-tab-active,
+.bx-menu-floating-blocks li:active,
+.bx-menu-floating-blocks li.bx-menu-tab-active {
+    opacity:1.0;
+}
+
+.bx-menu-floating-blocks-big li a,
+.bx-menu-floating-blocks li a {
+    display:block;
+    text-decoration:none;
+}
+
+.bx-menu-floating-blocks-big li.bx-menu-tab-active a,
+.bx-menu-floating-blocks li.bx-menu-tab-active a {
+    color:#000;
+}
+
+.bx-menu-floating-blocks li img,
+.bx-menu-floating-blocks li .sys-icon {
+    display:block;
+    width:32px;
+    width:2rem;
+    height:32px;
+    height:2rem;
+    margin:8px 16px 3px 16px;
+    margin:0.5rem 1rem 0.2rem 1rem;
+}
+.bx-menu-floating-blocks-big li img,
+.bx-menu-floating-blocks-big li .sys-icon {
+    display:block;
+    width:192px;
+    width:12rem;
+    height:192px;
+    height:12rem;
+    margin:16px 32px 8px 32px;
+    margin:1rem 2rem 0.5rem 2rem;
+}
+
+.bx-menu-floating-blocks-big li img,
+.bx-menu-floating-blocks li img {
+    background-position:center center;
+    background-repeat:no-repeat;
+}
+
+.bx-menu-floating-blocks-big li .sys-icon,
+.bx-menu-floating-blocks li .sys-icon {
+    text-align:center;
+    vertical-align:bottom;
+    line-height:32px;
+    line-height:2rem;
+    font-size:32px;
+    font-size:2rem;
+}
+.bx-menu-floating-blocks-big li .sys-icon {
+    line-height:192px;
+    line-height:12rem;
+    font-size:192px;
+    font-size:12rem;
+}
+
+.bx-menu-floating-blocks-big li .bx-menu-floating-blocks-title,
+.bx-menu-floating-blocks li .bx-menu-floating-blocks-title {
+    text-align:center;
+    overflow:hidden;
+    text-overflow:ellipsis;
+}
+
+/* --------------- interactive block menu */
+ul.bx-menu-inter,
+ul.bx-menu-inter-vert {
+	padding:0;
+    margin:0;
+	list-style:none;
+}
+ul.bx-menu-inter li {
+    float:left;
+}
+li.bx-menu-inter-div {
+	padding: 0px 3px;
+}
+li.bx-menu-inter-div:first-child {
+	display: none;
+}
+li.bx-menu-inter-act {}
+li.bx-menu-inter-pas a:hover {
+	text-decoration: none;
+}
+li.bx-menu-inter-hidden {
+	display: none;
+}
+
+/* --------------- horizontal buttons menu */
+
+ul.bx-menu-btns-hor {
+	padding:0;
+    margin:0;
+	list-style:none;
+    text-align:center;
+}
+ul.bx-menu-btns-hor li {
+    display:inline;
+    display:inline-block;
+	vertical-align: bottom;
+}
+
+/* --------------- account popup menu */
+
+.bx-popup-content .bx-menu-account-popup {
+    min-width:17em;
+}
+
+.bx-menu-account-popup-switch-profile:hover,
+.bx-menu-account-popup-switch-profile {
+    float:right;
+    text-decoration:none;
+}

+ 82 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/css/uploaders.css

@@ -0,0 +1,82 @@
+
+.bx-uploader-msg-info {
+    padding:1px 0px 1px 16px;
+    background-image:url(../images/icons/information-small-white.png);
+    background-repeat:no-repeat;    
+}
+
+.bx-uploader-msg-err {
+    padding:1px 0px 1px 16px;
+    background-image:url(../images/icons/exclamation-small-red.png);
+    background-repeat:no-repeat;    
+    color:#900;
+}
+
+.bx-uploader-form-cont {
+    width:500px;
+}
+
+.bx-uploader-ghost-preview {
+    width:101px; 
+    float:left;
+}
+
+.bx-uploader-ghost-preview img {
+    width:101px;
+    min-height:20px;
+    background-image:url(../images/loading-spinner.gif);
+    background-repeat:no-repeat;
+    background-position:center center;
+    -webkit-box-reflect: below 0px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(.7, transparent), to(rgba(0,0,0,0.4)));
+}
+
+.bx-uploader-ghost-form {
+    width:500px; 
+    float:left;
+}
+
+.bx-uploader-ghost-form-filename {
+
+}
+ 
+.bx-uploader-ghost-form-actions {
+}
+
+/* simple uploader */
+
+.bx-uploader-simple-file {
+    margin-bottom:5px;
+}
+
+/* html5 uploader */
+
+.bx-uploader-html5 { }
+
+.bx-uploader-html5-drop-area { 
+    width:auto;
+    height:200px;
+    
+    line-height:200px; 
+    text-align:center;
+
+    border:5px dashed #ccc;
+}
+
+.bx-uploader-html5-drop-area-active { border-color:#999; }
+
+.bx-uploader-html5-list { padding:0; list-style:none; }
+.bx-uploader-html5-list li { margin:0; padding:0; line-height:15px; }
+.bx-uploader-html5-file, .bx-uploader-html5-spinner, .bx-uploader-html5-size, .bx-uploader-html5-cancel, .bx-uploader-html5-failed-text {
+    margin-right: 7px;
+}
+
+.bx-uploader-html5-file {}
+.bx-uploader-html5-spinner { display:inline-block; background: url("../images/loading-spinner.gif"); width:15px; height:15px; vertical-align:text-bottom; }
+.bx-uploader-html5-size, .bx-uploader-html5-cancel {}
+
+.bx-uploader-html5-failed-text { display:none; }
+.bx-uploader-html5-fail .bx-uploader-html5-failed-text { display:inline; }
+
+.bx-uploader-html5-button input {
+    height:38px;
+}

+ 9 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/menu_account_popup.html

@@ -0,0 +1,9 @@
+<div class="bx-menu-account-popup bx-menu-object-__object__">
+    <div class="bx-def-font-h2">
+    	<span class="bx-menu-account-popup-profile">__profile_display_name__</span>
+        <a title="<bx_text_attribute:_sys_switch_profile />" class="bx-menu-account-popup-switch-profile" href="__url_switch_profile__"><i class="sys-icon exchange"></i></a>
+    </div>
+    <div class="bx-def-margin-top bx-def-margin-bottom">__menu_account__</div>
+    <div class="bx-def-color-bg-sec" style="height:0.5em; margin-left:-3em; margin-right:-3em;">1</div>
+    <div>__menu_notifications__</div>
+</div>

+ 5 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/menu_custom.html

@@ -0,0 +1,5 @@
+<ul id="bx-menu-custom" class="bx-menu-hor bx-menu-hor-inline bx-menu-object-__object__ bx-clearfix">
+    <bx_repeat:menu_items>
+        <li class="bx-def-margin-left-auto __class__">__item__</li>
+    </bx_repeat:menu_items>
+</ul>

+ 8 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/menu_toolbar.html

@@ -0,0 +1,8 @@
+<ul class="bx-menu-toolbar bx-menu-object-__object__">
+    <bx_repeat:menu_items>
+        <li class="bx-menu-toolbar-item bx-def-color-bg-hl-hover __class_add__">
+            <a href="__link__" target="__target__" onclick="__onclick__" class="bx-def-margin-sec-left bx-def-font-contrasted"><bx_if:title><span class="bx-menu-toolbar-item-title bx-def-margin-sec-leftright">__title__</span></bx_if:title><bx_if:icon><i class="sys-icon __icon__ bx-def-margin-sec-leftright"></i></bx_if:icon><bx_if:image><img class="bx-def-thumb bx-def-thumb-size bx-def-margin-sec-leftright" src="__icon_url__" /></bx_if:image></a>
+            <bx_if:addon><span class="bx-menu-toolbar-item-addon bx-def-unit-alert-middle bg-col-blue2-dark">__addon__</span></bx_if:addon>
+        </li>
+    </bx_repeat:menu_items>
+</ul>

+ 12 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/popup_box.html

@@ -0,0 +1,12 @@
+<div id="__id__" class="bx-popup-wrapper bx-popup-box-wrapper" style="__wrapper_style__" role="dialog" aria-describedby="__id___title">
+    <div class="bx-popup-box-pointer"><i class="bx-popup-border"></i></div>
+    <div class="bx-popup bx-popup-box bx-popup-width bx-popup-color-bg bx-popup-border bx-def-round-corners">
+    <div class="bx-def-round-corners" style="overflow:hidden;">
+        <div class="bx-popup-header bx-popup-color-bg bx-def-padding bx-def-font-contrasted">
+            <h2 id="__id___title">__title__</h2>
+            <a href="javascript:void(0);" class="bx-popup-element-close bx-def-font-h2 bx-def-font-contrasted bx-def-margin bx-def-media-tablet-hide bx-def-media-desktop-hide"><i class="sys-icon remove"></i></a>
+        </div>
+        <div class="bx-popup-content bx-def-padding">__content__</div>
+    </div>
+    </div>
+</div>

+ 7 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/popup_image.html

@@ -0,0 +1,7 @@
+<div class="bx-popup-image-container">
+    <div class="bx-popup-image-btn bx-def-padding">
+        <a class="bx-btn bx-def-margin-sec-left" title="<bx_text_attribute:_sys_close />" href="javascript:void(0);" onclick="$(this).parents().filter('.bx-popup-wrapper').dolPopupHide()"><i class="sys-icon times"></i></a>
+    </div>
+
+    <img class="bx-popup-image-image" src="__image_url__" />
+</div>

+ 3 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/popup_slide.html

@@ -0,0 +1,3 @@
+<div  id="__id__" class="bx-popup-wrapper bx-popup-slide-wrapper bx-def-z-index-front bx-def-color-bg-box bx-def-border-bottom bx-def-border-top" style="__wrapper_style__">
+    __content__
+</div>

+ 6 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/popup_trans.html

@@ -0,0 +1,6 @@
+<div  id="__id__" class="bx-popup-wrapper bx-popup-trans-wrapper" style="__wrapper_style__" role="alert">
+    <div class="bx-popup-box-pointer"><i class="bx-popup-border"></i></div>
+    <div class="bx-popup bx-popup-trans bx-popup-border bx-popup-color-bg bx-def-round-corners">
+        <div class="bx-popup-content" style="">__content__</div>
+    </div>
+</div>

+ 3 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/rss_holder.html

@@ -0,0 +1,3 @@
+    <div class="RSSAggrCont" rssobject="__rss_object__" rssid="__rss_id__" rssnum="__rss_num__" member="__member_id__">
+        <div class="bx-loading-ajax bx-def-z-index-front bx-def-margin-top bx-def-margin-bottom" style="position:static;"><div class="bx-loading-ajax-rotating"></div></div>
+    </div>

+ 722 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseCmts.php

@@ -0,0 +1,722 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolCmts');
+bx_import('BxDolProfile');
+bx_import('BxTemplPaginate');
+
+/**
+ * @see BxDolCmts
+ */
+class BxBaseCmts extends BxDolCmts
+{
+    protected $_sJsObjName;
+    protected $_sStylePrefix;
+
+    function __construct( $sSystem, $iId, $iInit = 1 )
+    {
+        parent::__construct( $sSystem, $iId, $iInit );
+        if (empty($sSystem))
+            return;
+
+        $this->_sJsObjName = 'oCmts' . bx_gen_method_name($sSystem, array('_' , '-')) . $iId;
+        $this->_sStylePrefix = isset($this->_aSystem['root_style_prefix']) ? $this->_aSystem['root_style_prefix'] : 'cmt';
+
+        BxDolTemplate::getInstance()->addJsTranslation('_sys_txt_cmt_loading');
+    }
+
+    /**
+     * Add comments CSS/JS
+     */
+    public function addCssJs ()
+    {
+        $oTemplate = BxDolTemplate::getInstance();
+
+        $oTemplate->addCss(array('cmts.css'));
+        $oTemplate->addJs(array('jquery.anim.js', 'jquery.form.min.js', 'BxDolCmts.js'));
+
+        bx_import('BxDolForm');
+        $oForm = BxDolForm::getObjectInstance($this->_sFormObject, $this->_sFormDisplayPost);
+        $oForm->addCssJs();
+    }
+
+    public function getJsObjectName()
+    {
+        return $this->_sJsObjName;
+    }
+
+    /**
+     * Get initialization section of comments box
+     *
+     * @return string
+     */
+    public function getJsScript()
+    {
+        $aParams = array(
+            'sObjName' => $this->_sJsObjName,
+            'sRootUrl' => BX_DOL_URL_ROOT,
+            'sSystem' => $this->getSystemName(),
+            'iAuthorId' => $this->_getAuthorId(),
+            'iObjId' => $this->getId(),
+            'sPostFormPosition' => $this->_aSystem['post_form_position'],
+            'sBrowseType' => $this->_sBrowseType,
+            'sDisplayType' => $this->_sDisplayType
+        );
+
+        $this->addCssJs();
+        return BxDolTemplate::getInstance()->_wrapInTagJsCode("var " . $this->_sJsObjName . " = new BxDolCmts(" . json_encode($aParams) . ");");
+    }
+
+    /**
+     * get full comments block with initializations
+     */
+    function getCommentsBlock($iParentId = 0, $iVParentId = 0, $bInDesignbox = true)
+    {
+        $aBp = array('parent_id' => $iParentId, 'vparent_id' => $iVParentId);
+        $aDp = array('show_empty' => true);
+
+        $sCaption = _t('_cmt_block_comments_title', $this->getCommentsCount());
+        $sContent = BxDolTemplate::getInstance()->parseHtmlByName('comments_block.html', array(
+            'system' => $this->_sSystem,
+            'list_anchor' => $this->getListAnchor(),
+            'id' => $this->getId(),
+            'comments' => $this->getComments($aBp, $aDp),
+            'post_form_top' => $this->getFormBoxPost($aBp, array('type' => $this->_sDisplayType, 'position' => BX_CMT_PFP_TOP)),
+            'post_form_bottom'  => $this->getFormBoxPost($aBp, array('type' => $this->_sDisplayType, 'position' => BX_CMT_PFP_BOTTOM)),
+        	'view_image_popup' => $this->_getViewImagePopup(),
+            'script' => $this->getJsScript()
+        ));
+
+        return $bInDesignbox ? DesignBoxContent($sCaption, $sContent, BX_DB_PADDING_DEF, $this->_getControlsBox()) : array(
+            'title' => $sCaption,
+            'content' => $sContent,
+            'designbox_id' => BX_DB_PADDING_DEF,
+            'menu' => $this->_getControlsBox(),
+        );
+    }
+
+    /**
+     * get comments list for specified parent comment
+     *
+     * @param array $aBp - browse params array
+     * @param array $aDp - display params array
+     *
+     */
+    function getComments($aBp = array(), $aDp = array())
+    {
+        $this->_prepareParams($aBp, $aDp);
+
+        $aCmts = $this->getCommentsArray($aBp['vparent_id'], $aBp['filter'], $aBp['order'], $aBp['start'], $aBp['per_view']);
+        if(empty($aCmts) || !is_array($aCmts))
+            return isset($aDp['show_empty']) && $aDp['show_empty'] === true ? $this->_getEmpty() : '';
+            
+        $sCmts = '';
+        foreach($aCmts as $k => $aCmt)
+            $sCmts .= $this->getComment($aCmt, $aBp, $aDp);
+
+        $sCmts = $this->_getMoreLink($sCmts, $aBp, $aDp);
+        return $sCmts;
+    }
+
+    /**
+     * get comment view block with initializations
+     */
+    function getCommentBlock($iCmtId = 0, $aBp = array(), $aDp = array())
+    {
+        return BxDolTemplate::getInstance()->parseHtmlByName('comment_block.html', array(
+            'system' => $this->_sSystem,
+            'id' => $this->getId(),
+            'comment' => $this->getComment(
+        		$iCmtId, 
+        		array_merge(array('type' => $this->_sBrowseType), $aBp), 
+        		array_merge(array('type' => BX_CMT_DISPLAY_THREADED), $aDp)
+        	),
+        	'view_image_popup' => $this->_getViewImagePopup(), 
+            'script' => $this->getJsScript()
+        ));
+    }
+
+    /**
+     * get one just posted comment
+     *
+     * @param  int    $iCmtId - comment id
+     * @return string
+     */
+    function getComment($mixedCmt, $aBp = array(), $aDp = array())
+    {
+        $oTemplate = BxDolTemplate::getInstance();
+
+        $iUserId = $this->_getAuthorId();
+        $aCmt = !is_array($mixedCmt) ? $this->getCommentRow((int)$mixedCmt) : $mixedCmt;
+
+        list($sAuthorName, $sAuthorLink, $sAuthorIcon) = $this->_getAuthorInfo($aCmt['cmt_author_id']);
+
+        $sClass = '';
+        if(isset($aCmt['vote_rate']) && (float)$aCmt['vote_rate'] < $this->_aSystem['viewing_threshold']) {
+            $oTemplate->pareseHtmlByName('comment_hidden.html', array(
+                'js_object' => $this->_sJsObjName,
+                'id' => $aCmt['cmt_id'],
+                'title' => bx_process_output(_t('_hidden_comment', $sAuthorName)),
+                'bx_if:show_replies' => array(
+                    'condition' => $aCmt['cmt_replies'] > 0,
+                    'content' => array(
+                        'replies' => _t('_Show N replies', $aCmt['cmt_replies'])
+                    )
+                )
+            ));
+
+            $sClass = ' cmt-hidden';
+        }
+
+        if($aCmt['cmt_author_id'] == $iUserId)
+            $sClass .= ' cmt-mine';
+
+        $sActions = $this->_getActionsBox($aCmt, $aDp);
+
+        $sText = $aCmt['cmt_text'];
+        $sTextMore = '';
+
+        $iMaxLength = (int)$this->_aSystem['chars_display_max'];
+        if(strlen($sText) > $iMaxLength) {
+            $iLength = strpos($sText, ' ', $iMaxLength);
+
+            $sTextMore = trim(substr($sText, $iLength));
+            $sText = trim(substr($sText, 0, $iLength));
+        }
+
+        $sText = $this->_prepareTextForOutput($sText);
+        $sTextMore = $this->_prepareTextForOutput($sTextMore);
+
+        $aTmplReplyTo = array();
+        if((int)$aCmt['cmt_parent_id'] != 0) {
+            $aParent = $this->getCommentRow($aCmt['cmt_parent_id']);
+            list($sParAuthorName, $sParAuthorLink, $sParAuthorIcon) = $this->_getAuthorInfo($aParent['cmt_author_id']);
+
+            $aTmplReplyTo = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'par_cmt_link' => $this->getBaseUrl() . '#' . $this->_sSystem . $aCmt['cmt_parent_id'],
+                'par_cmt_author' => $sParAuthorName
+            );
+        }
+
+        $aTmplImages = array();
+        if($this->isAttachImageEnabled()) {
+            $aImages = $this->_oQuery->getImages($this->_aSystem['system_id'], $aCmt['cmt_id']);
+            if(!empty($aImages) && is_array($aImages)) {
+                bx_import('BxDolStorage');
+        		$oStorage = BxDolStorage::getObjectInstance($this->getStorageObjectName());
+
+            	bx_import('BxDolImageTranscoder');
+                $oTranscoder = BxDolImageTranscoder::getObjectInstance($this->getTranscoderPreviewName());
+
+                foreach($aImages as $aImage)
+                    $aTmplImages[] = array(
+                        'style_prefix' => $this->_sStylePrefix,
+                        'js_object' => $this->_sJsObjName,
+                        'image' => $oTranscoder->getImageUrl($aImage['image_id']),
+                    	'image_orig' => $oStorage->getFileUrlById($aImage['image_id'])
+                    );
+            }
+        }
+
+        $sReplies = '';
+        if((int)$aCmt['cmt_replies'] > 0 && !empty($aDp) && $aDp['type'] == BX_CMT_DISPLAY_THREADED)
+            $sReplies = $this->getComments(array('parent_id' => $aCmt['cmt_id'], 'vparent_id' => $aCmt['cmt_id'], 'type' => $aBp['type']), $aDp);
+
+        $bAuthorIcon = !empty($sAuthorIcon);
+        return $oTemplate->parseHtmlByName('comment.html', array(
+            'system' => $this->_sSystem,
+            'style_prefix' => $this->_sStylePrefix,
+            'js_object' => $this->_sJsObjName,
+            'id' => $aCmt['cmt_id'],
+            'class' => $sClass,
+            'bx_if:show_icon' => array(
+                'condition' => $bAuthorIcon,
+                'content' => array(
+                    'author_icon' => $sAuthorIcon
+                )
+            ),
+            'bx_if:show_icon_empty' => array(
+                'condition' => !$bAuthorIcon,
+                'content' => array()
+            ),
+            'bx_if:show_author_link' => array(
+                'condition' => !empty($sAuthorLink),
+                'content' => array(
+                    'author_link' => $sAuthorLink,
+                    'author_name' => $sAuthorName
+                )
+            ),
+            'bx_if:show_author_text' => array(
+                'condition' => empty($sAuthorLink),
+                'content' => array(
+                    'author_name' => $sAuthorName
+                )
+            ),
+            'bx_if:show_reply_to' => array(
+                'condition' => !empty($aTmplReplyTo),
+                'content' => $aTmplReplyTo
+            ),
+            'text' => $sText,
+            'bx_if:show_more' => array(
+                'condition' => !empty($sTextMore),
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+                    'js_object' => $this->_sJsObjName,
+                    'text_more' => $sTextMore
+                )
+            ),
+            'bx_if:show_attached' => array(
+                'condition' => !empty($aTmplImages),
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+                    'bx_repeat:attached' => $aTmplImages
+                )
+            ),
+            'actions' => $sActions,
+            'replies' =>  $sReplies
+        ));
+    }
+
+	/**
+     * get one comment for "Live Search"
+     *
+     * @param  int    $iCmtId - comment id
+     * @return string
+     */
+    function getCommentLiveSearch($mixedCmt, $aParams = array())
+    {
+        $aCmt = !is_array($mixedCmt) ? $this->getCommentRow((int)$mixedCmt) : $mixedCmt;
+
+        list($sAuthorName, $sAuthorLink, $sAuthorIcon) = $this->_getAuthorInfo($aCmt['cmt_author_id']);
+        $bAuthorIcon = !empty($sAuthorIcon);
+
+        $sViewLink = $this->getViewUrl($aCmt['cmt_id']);
+
+        return BxDolTemplate::getInstance()->parseHtmlByName('comment_live_search.html', array(
+            'bx_if:show_icon' => array(
+                'condition' => $bAuthorIcon,
+                'content' => array(
+                    'author_icon' => $sAuthorIcon,
+        			'view_link' => $sViewLink
+                )
+            ),
+            'bx_if:show_icon_empty' => array(
+                'condition' => !$bAuthorIcon,
+                'content' => array()
+            ),
+            'view_link' => $sViewLink,
+            'text' => BxTemplFunctions::getInstance()->getStringWithLimitedLength(strip_tags($aCmt['cmt_text']), $this->_sSnippetLenthLiveSearch),
+            'sample' => isset($aParams['txt_sample_single']) ? _t($aParams['txt_sample_single']) : ''
+        ));
+    }
+
+    function getFormBoxPost($aBp = array(), $aDp = array())
+    {
+        return $this->_getFormBox(BX_CMT_ACTION_POST, $aBp, $aDp);
+    }
+
+    function getFormBoxEdit($aBp = array(), $aDp = array())
+    {
+        return $this->_getFormBox(BX_CMT_ACTION_EDIT, $aBp, $aDp);
+    }
+
+    function getFormPost($iCmtParentId = 0)
+    {
+        return $this->_getFormPost($iCmtParentId);
+    }
+
+    function getFormEdit($aCmt)
+    {
+        return $this->_getFormEdit($aCmt);
+    }
+
+    /**
+     * private functions
+     */
+    protected function _getControlsBox()
+    {
+        $oTemplate = BxDolTemplate::getInstance();
+
+        $sDisplay = '';
+        $bDisplay = (int)$this->_aSystem['is_display_switch'] == 1;
+        if($bDisplay) {
+            $aDisplayLinks = array(
+                array('id' => $this->_sSystem . '-flat', 'name' => $this->_sSystem . '-flat', 'class' => '', 'title' => '_cmt_display_flat', 'target' => '_self', 'onclick' => 'javascript:' . $this->_sJsObjName . '.cmtChangeDisplay(this, \'flat\');'),
+                array('id' => $this->_sSystem . '-threaded', 'name' => $this->_sSystem . '-threaded', 'class' => '', 'title' => '_cmt_display_threaded', 'target' => '_self', 'onclick' => 'javascript:' . $this->_sJsObjName . '.cmtChangeDisplay(this, \'threaded\');')
+            );
+
+            bx_import('BxTemplMenuInteractive');
+            $oMenu = new BxTemplMenuInteractive(array('template' => 'menu_interactive_vertical.html', 'menu_id'=> $this->_sSystem . '-display', 'menu_items' => $aDisplayLinks));
+            $oMenu->setSelected('', $this->_sSystem . '-' . $this->_sDisplayType);
+            $sDisplay = $oMenu->getCode();
+        }
+
+        $sBrowseType = '';
+        $bBrowseType = (int)$this->_aSystem['is_browse_switch'] == 1;
+        if($bBrowseType) {
+            $aBrowseLinks = array(
+                array('id' => $this->_sSystem . '-tail', 'name' => $this->_sSystem . '-tail', 'class' => '', 'title' => '_cmt_browse_tail', 'target' => '_self', 'onclick' => 'javascript:' . $this->_sJsObjName . '.cmtChangeBrowse(this, \'tail\');'),
+                array('id' => $this->_sSystem . '-head', 'name' => $this->_sSystem . '-head', 'class' => '', 'title' => '_cmt_browse_head', 'target' => '_self', 'onclick' => 'javascript:' . $this->_sJsObjName . '.cmtChangeBrowse(this, \'head\');'),
+                array('id' => $this->_sSystem . '-popular', 'name' => $this->_sSystem . '-popular', 'class' => '', 'title' => '_cmt_browse_popular', 'target' => '_self', 'onclick' => 'javascript:' . $this->_sJsObjName . '.cmtChangeBrowse(this, \'popular\');'),
+            );
+
+            bx_import('BxTemplMenuInteractive');
+            $oMenu = new BxTemplMenuInteractive(array('template' => 'menu_interactive_vertical.html', 'menu_id'=> $this->_sSystem . '-browse', 'menu_items' => $aBrowseLinks));
+            $oMenu->setSelected('', $this->_sSystem . '-' . $this->_sBrowseType);
+            $sBrowseType = $oMenu->getCode();
+        }
+
+        $sBrowseFilter = '';
+        $bBrowseFilter = $bBrowseType;
+        if($bBrowseFilter) {
+            $aFilterLinks = array(
+                array('id' => $this->_sSystem . '-all', 'name' => $this->_sSystem . '-all', 'class' => '', 'title' => '_cmt_browse_all', 'target' => '_self', 'onclick' => 'javascript:' . $this->_sJsObjName . '.cmtChangeFilter(this, \'all\');'),
+                array('id' => $this->_sSystem . '-friends', 'name' => $this->_sSystem . '-friends', 'class' => '', 'title' => '_cmt_browse_friends', 'target' => '_self', 'onclick' => 'javascript:' . $this->_sJsObjName . '.cmtChangeFilter(this, \'friends\');'),
+                array('id' => $this->_sSystem . '-subscriptions', 'name' => $this->_sSystem . '-subscriptions', 'class' => '', 'title' => '_cmt_browse_subscriptions', 'target' => '_self', 'onclick' => 'javascript:' . $this->_sJsObjName . '.cmtChangeFilter(this, \'subscriptions\');')
+            );
+
+            $oMenu = new BxTemplMenuInteractive(array('template' => 'menu_interactive_vertical.html', 'menu_id'=> $this->_sSystem . '-filter', 'menu_items' => $aFilterLinks));
+            $oMenu->setSelected('', $this->_sSystem . '-' . $this->_sBrowseFilter);
+            $sBrowseFilter = $oMenu->getCode();
+        }
+
+        return $oTemplate->parseHtmlByName('comments_controls.html', array(
+            'display_switcher' => $bDisplay ? $sDisplay : '',
+            'bx_if:is_divider_1' => array(
+                'condition' => $bDisplay && $bBrowseType,
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+                )
+            ),
+            'browse_switcher' => $bBrowseType ? $sBrowseType : '',
+            'bx_if:is_divider_2' => array(
+                'condition' => $bBrowseType && $bBrowseFilter,
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+                )
+            ),
+            'filter_switcher' => $bBrowseFilter ? $sBrowseFilter : '',
+        ));
+    }
+
+    protected function _getActionsBox(&$aCmt, $aDp = array())
+    {
+    	$oTemplate = BxDolTemplate::getInstance();
+
+    	$bViewOnly = isset($aDp['view_only']) && $aDp['view_only'] === true;
+
+        $bMenuManage = false;
+        $sMenuActions = $sMenuManage = '';
+
+		if(!$bViewOnly) {
+	        //--- Actions Menu
+	        bx_import('BxDolMenu');
+	        $oMenuActions = BxDolMenu::getObjectInstance($this->_sMenuObjActions);
+	        $oMenuActions->setCmtsData($this, $aCmt['cmt_id']);
+	        $sMenuActions = $oMenuActions->getCode();
+
+	        $oVote = $this->getVoteObject($aCmt['cmt_id']);
+	        if($oVote !== false)
+	            $sMenuActions .= $oVote->getJsScript();
+	
+	        //--- Manage Menu
+	        $oMenuManage = BxDolMenu::getObjectInstance($this->_sMenuObjManage);
+	        $oMenuManage->setCmtsData($this, $aCmt['cmt_id']);
+	
+	        $sMenuManage = $oMenuManage->getCode();
+	        $bMenuManage = !empty($sMenuManage);
+	        if($bMenuManage) {
+	            $sMenuManage = $oTemplate->parseHtmlByName('comment_manage.html', array(
+	                'style_prefix' => $this->_sStylePrefix,
+	                'content' => $sMenuManage
+	            ));
+	
+	            bx_import('BxTemplFunctions');
+	            $sMenuManage = BxTemplFunctions::getInstance()->transBox($this->_sSystem . '-manage-' . $aCmt['cmt_id'], $sMenuManage, true);
+	        }
+		}
+
+        $sAgo = bx_time_js($aCmt['cmt_time']);
+        $bObjectTitle = !empty($this->_aSystem['trigger_field_title']);
+        return $oTemplate->parseHtmlByName('comment_actions.html', array(
+            'id' => $aCmt['cmt_id'],
+            'js_object' => $this->_sJsObjName,
+            'style_prefix' => $this->_sStylePrefix,
+            'bx_if:show_ago_link' => array(
+                'condition' => $bObjectTitle,
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+                    'view_link' => $this->getViewUrl($aCmt['cmt_id']),
+                    'ago' => $sAgo
+                )
+            ),
+            'bx_if:show_ago_text' => array(
+                'condition' => !$bObjectTitle,
+                'content' => array(
+                    'ago' => $sAgo
+                )
+            ),
+            'menu_actions' => $sMenuActions,
+/*
+            'bx_if:hide_rate_count' => array(
+                'condition' => (int)$aCmt['cmt_rate'] <= 0,
+                'content' => array()
+            ),
+            'points' => _t(in_array($aCmt['cmt_rate'], array(-1, 0, 1)) ? '_N_point' : '_N_points', $aCmt['cmt_rate']),
+*/
+            'bx_if:show_menu_manage' => array(
+                'condition' => $bMenuManage,
+                'content' => array(
+                    'js_object' => $this->_sJsObjName,
+                    'style_prefix' => $this->_sStylePrefix,
+                    'id' => $aCmt['cmt_id'],
+                    'popup_text' => $sMenuManage
+                )
+            )
+        ));
+    }
+
+    protected function _getFormBox($sType, $aBp, $aDp)
+    {
+        $iCmtParentId = isset($aBp['parent_id']) ? (int)$aBp['parent_id'] : 0;
+        $sPosition = isset($aDp['position']) ? $aDp['position'] : '';
+
+        if(!$this->isPostReplyAllowed())
+            return '';
+
+        $sPositionSystem = $this->_aSystem['post_form_position'];
+        if(!empty($sPosition) && $sPositionSystem != $sPosition)
+            return '';
+
+        $sMethod = '_getForm' . ucfirst($sType);
+        $aForm = $this->$sMethod($iCmtParentId);
+
+        return BxDolTemplate::getInstance()->parseHtmlByName('comment_reply_box.html', array(
+            'js_object' => $this->_sJsObjName,
+            'style_prefix' => $this->_sStylePrefix,
+            'bx_if:show_class' => array(
+                'condition' => !empty($sPosition),
+                'content' => array(
+                    'class' => $this->_sStylePrefix . '-reply-' . $sPosition
+                )
+            ),
+            'form' => $aForm['form'],
+            'form_id' => $aForm['form_id'],
+        ));
+    }
+
+    protected function _getFormPost($iCmtParentId = 0)
+    {
+        $oForm = $this->_getForm(BX_CMT_ACTION_POST, $iCmtParentId);
+        $oForm->aInputs['cmt_parent_id']['value'] = $iCmtParentId;
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $iCmtAuthorId = $this->_getAuthorId();
+            $iCmtParentId = $oForm->getCleanValue('cmt_parent_id');
+            $sCmtText = $oForm->getCleanValue('cmt_text');
+            $sCmtText = $this->_prepareTextForSave ($sCmtText);
+            $oForm->setSubmittedValue('cmt_text', $sCmtText, $oForm->aFormAttrs['method']);
+
+            $iLevel = 0;
+            $iCmtVisualParentId = 0;
+            if((int)$iCmtParentId > 0) {
+                $aParent = $this->getCommentRow($iCmtParentId);
+
+                $iLevel = (int)$aParent['cmt_level'] + 1;
+                $iCmtVisualParentId = $iLevel > $this->getMaxLevel() ? $aParent['cmt_vparent_id'] : $iCmtParentId;
+            }
+
+            $iCmtId = (int)$oForm->insert(array('cmt_vparent_id' => $iCmtVisualParentId, 'cmt_object_id' => $this->_iId, 'cmt_author_id' => $iCmtAuthorId, 'cmt_level' => $iLevel, 'cmt_time' => time()));
+            if($iCmtId != 0) {
+                if($this->isAttachImageEnabled()) {
+                    $aImages = $oForm->getCleanValue('cmt_image');
+                    if(!empty($aImages) && is_array($aImages)) {
+                        bx_import('BxDolStorage');
+                        $oStorage = BxDolStorage::getObjectInstance($this->getStorageObjectName());
+
+                        foreach($aImages as $iImageId)
+                            if($this->_oQuery->saveImages($this->_aSystem['system_id'], $iCmtId, $iImageId))
+                                $oStorage->afterUploadCleanup($iImageId, $iCmtAuthorId);
+                    }
+                }
+
+                if($iCmtParentId) {
+                    $this->_oQuery->updateRepliesCount($iCmtParentId, 1);
+
+                    $this->_sendNotificationEmail($iCmtId, $iCmtParentId);
+                }
+
+                $this->_triggerComment();
+
+                $this->isPostReplyAllowed(true);
+
+                bx_import('BxDolAlerts');
+                $oZ = new BxDolAlerts($this->_sSystem, 'commentPost', $this->getId(), $iCmtAuthorId, array('comment_id' => $iCmtId, 'comment_author_id' => $iCmtAuthorId));
+                $oZ->alert();
+
+                return array('id' => $iCmtId, 'parent_id' => $iCmtParentId);
+            }
+
+            return array('msg' => _t('_cmt_err_cannot_perform_action'));
+        }
+
+        return array('form' => $oForm->getCode(), 'form_id' => $oForm->id);
+    }
+
+    protected function _getFormEdit($iCmtId)
+    {
+        $aCmt = $this->_oQuery->getCommentSimple ($this->getId(), $iCmtId);
+        if(!$aCmt)
+            return array('msg' => _t('_No such comment'));
+
+        $iCmtAuthorId = $this->_getAuthorId();
+        if(!$this->isEditAllowed($aCmt))
+            return array('msg' => $aCmt['cmt_author_id'] == $iCmtAuthorId ? strip_tags($this->msgErrEditAllowed()) : _t('_Access denied'));
+
+        $oForm = $this->_getForm(BX_CMT_ACTION_EDIT, $aCmt['cmt_id']);
+        $aCmt['cmt_text'] = $this->_prepareTextForEdit($aCmt['cmt_text']);
+
+        $oForm->initChecker($aCmt);
+        if($oForm->isSubmittedAndValid()) {
+            $sCmtText = $oForm->getCleanValue('cmt_text');
+            $sCmtText = $this->_prepareTextForSave ($sCmtText);
+            $oForm->setSubmittedValue('cmt_text', $sCmtText, $oForm->aFormAttrs['method']);
+
+            if($oForm->update($iCmtId)) {
+                $this->isEditAllowed(true);
+
+                bx_import('BxDolAlerts');
+                $oZ = new BxDolAlerts($this->_sSystem, 'commentUpdated', $this->getId(), $iCmtAuthorId, array('comment_id' => $aCmt['cmt_id'], 'comment_author_id' => $aCmt['cmt_author_id']));
+                $oZ->alert();
+
+                return array('id' => $iCmtId, 'text' => $sCmtText);
+            }
+
+            return array('msg' => _t('_cmt_err_cannot_perform_action'));
+        }
+
+        return array('form' => $oForm->getCode(), 'form_id' => $oForm->id);
+    }
+
+    protected function _getForm($sAction, $iId)
+    {
+        $oForm = $this->_getFormObject($sAction);
+        $oForm->setId(sprintf($oForm->aFormAttrs['id'], $sAction, $this->_sSystem, $iId));
+        $oForm->setName(sprintf($oForm->aFormAttrs['name'], $sAction, $this->_sSystem, $iId));
+        $oForm->aParams['db']['table'] = $this->_aSystem['table'];
+        $oForm->aInputs['sys']['value'] = $this->_sSystem;
+        $oForm->aInputs['id']['value'] = $this->_iId;
+        $oForm->aInputs['action']['value'] = 'Submit' . ucfirst($sAction) . 'Form';
+
+        if(!$this->isAttachImageEnabled())
+            unset($oForm->aInputs['cmt_image']);
+
+        if(isset($oForm->aInputs['cmt_text'])) {
+            $iCmtTextMin = (int)$this->_aSystem['chars_post_min'];
+            $iCmtTextMax = (int)$this->_aSystem['chars_post_max'];
+
+            $oForm->aInputs['cmt_text']['checker']['params'] = array($iCmtTextMin, $iCmtTextMax);
+            $oForm->aInputs['cmt_text']['checker']['error'] = _t('_Please enter n1-n2 characters', $iCmtTextMin, $iCmtTextMax);
+        }
+
+        return $oForm;
+    }
+
+    protected function _getMoreLink($sCmts, $aBp = array(), $aDp = array())
+    {
+        $iStart = $iPerView = 0;
+        switch($aBp['type']) {
+            case BX_CMT_BROWSE_HEAD:
+            case BX_CMT_BROWSE_POPULAR:
+            case BX_CMT_BROWSE_CONNECTION:
+                $iPerView = $aBp['per_view'];
+
+                $iStart = $aBp['start'] + $iPerView;
+                if($iStart >= $aBp['count'])
+                    return $sCmts;
+
+                break;
+
+            case BX_CMT_BROWSE_TAIL:
+                $iPerView = $aBp['per_view'];
+
+                $iStart = $aBp['start'] - $iPerView;
+                if($iStart < 0) {
+                    $iPerView += $iStart;
+                    $iStart = 0;
+                }
+
+                if($iStart == 0 && $iPerView == 0)
+                    return $sCmts;
+
+                break;
+        }
+
+        $bRoot = (int)$aBp['vparent_id'] <= 0;
+
+        $sMore = BxDolTemplate::getInstance()->parseHtmlByName('comment_more.html', array(
+            'js_object' => $this->_sJsObjName,
+            'style_prefix' => $this->_sStylePrefix,
+            'bx_if:is_root' => array(
+                'condition' => $bRoot,
+                'content' => array()
+            ),
+            'parent_id' => $aBp['vparent_id'],
+            'start' => $iStart,
+            'per_view' => $iPerView,
+            'title' => _t('_load_more_' . ($aBp['vparent_id'] == 0 ? 'comments' : 'replies') . '_' . $aBp['type'])
+        ));
+
+        switch($aBp['type']) {
+            case BX_CMT_BROWSE_HEAD:
+                case BX_CMT_BROWSE_POPULAR:
+                case BX_CMT_BROWSE_CONNECTION:
+                $sCmts .= $sMore;
+                break;
+
+            case BX_CMT_BROWSE_TAIL:
+                $sCmts = $sMore . $sCmts;
+                break;
+        }
+
+        return $sCmts;
+    }
+
+    protected function _getEmpty()
+    {
+        return BxDolTemplate::getInstance()->parseHtmlByName('comment_empty.html', array(
+            'style_prefix' => $this->_sStylePrefix
+        ));
+    }
+
+    protected function _getViewImagePopup()
+    {
+    	$sViewImagePopupId = 'cmts-box-' . $this->_sSystem . '-' . $this->getId() . '-view-image-popup' ;
+        $sViewImagePopupContent = BxDolTemplate::getInstance()->parseHtmlByName('popup_image.html', array(
+    		'image_url' => ''
+    	));
+
+    	bx_import('BxTemplFunctions');
+    	return BxTemplFunctions::getInstance()->transBox($sViewImagePopupId, $sViewImagePopupContent, true);
+    }
+    protected function _echoResultJson($a, $isAutoWrapForFormFileSubmit = false)
+    {
+
+        header('Content-type: text/html; charset=utf-8');
+
+        $s = json_encode($a);
+        if ($isAutoWrapForFormFileSubmit && !empty($_FILES))
+            $s = '<textarea>' . $s . '</textarea>'; // http://jquery.malsup.com/form/#file-upload
+        echo $s;
+    }
+}
+
+/** @} */

+ 58 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseCmtsForm.php

@@ -0,0 +1,58 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxTemplFormView');
+
+class BxBaseCmtsForm extends BxTemplFormView
+{
+	protected $_sStorageObject;
+    protected $_sTranscoderPreview;
+    protected $_aImageUploaders;
+
+    public function __construct($aInfo, $oTemplate)
+    {
+        parent::__construct($aInfo, $oTemplate);
+
+        $this->_sStorageObject = 'sys_cmts_images';
+        $this->_sTranscoderPreview = 'sys_cmts_images_preview';
+
+        $this->_aImageUploaders = array('sys_cmts_simple');
+
+    	if(isset($this->aInputs['cmt_image'])) {
+            $aFormNested = array(
+                'params' =>array(
+                    'nested_form_template' => 'comments_uploader_nfw.html'
+                ),
+                'inputs' => array(),
+            );
+
+            bx_import('BxDolFormNested');
+            $oFormNested = new BxDolFormNested('cmt_image', $aFormNested, 'cmt_submit');
+
+            $this->aInputs['cmt_image']['storage_object'] = $this->_sStorageObject;
+            $this->aInputs['cmt_image']['images_transcoder'] = $this->_sTranscoderPreview;
+            $this->aInputs['cmt_image']['uploaders'] = $this->_aImageUploaders;
+            $this->aInputs['cmt_image']['upload_buttons_titles'] = array('Simple' => 'camera');
+            $this->aInputs['cmt_image']['multiple'] = true;
+            $this->aInputs['cmt_image']['ghost_template'] = $oFormNested;
+        }
+    }
+
+	public function getStorageObjectName()
+    {
+        return $this->_sStorageObject;
+    }
+
+    public function getTranscoderPreviewName()
+    {
+    	return $this->_sTranscoderPreview;
+    }
+}
+
+/** @} */

+ 1240 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseFormView.php

@@ -0,0 +1,1240 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolForm');
+
+class BxBaseFormView extends BxDolForm
+{
+    protected static $_isToggleJsAdded = false;
+
+    protected static $_isCssJsAdded = false;
+    protected static $_isCssJsAddedViewMode = false;
+
+    /**
+     * Enable or disable error message displaying
+     */
+    protected $bEnableErrorIcon = true;
+
+    /**
+     * HTML Code of this form
+     */
+    protected $sCode;
+
+    /**
+     * Code which will be added to the beginning of the form.
+     * For example, hidden inputs.
+     * For internal use only
+     */
+    protected $_sCodeAdd = '';
+
+    /**
+     * for internal use only
+     */
+    protected $_isSectionOpened = false;
+
+    /**
+     * Default divider for several inputs
+     */
+    protected $_sDivider = '<span class="bx-def-margin-left"></span>';
+
+    /**
+     * Alternative divider for several inputs
+     */
+    protected $_sDividerAlt = '<br />';
+
+    /**
+     * Form is added dynamically.
+     */
+    protected $_bDynamicMode = false;
+
+    /**
+     * Function name for generation close form section HTML.
+     */
+    protected $_sSectionClose = 'getCloseSection';
+
+    /**
+     * Function name for generation open form section HTML.
+     */
+    protected $_sSectionOpen = 'getOpenSection';
+
+    /**
+     * Constructor
+     *
+     * @param array $aInfo Form contents
+     *
+     * $aInfo['params'] = array(
+     *     'remove_form' => true|false,
+     * );
+     *
+     * @return BxBaseFormView
+     */
+    function __construct($aInfo, $oTemplate)
+    {
+        parent::__construct($aInfo, $oTemplate);
+
+        if (isset($this->aParams['view_mode']) && $this->aParams['view_mode']) {
+            $this->_sSectionClose = 'getCloseSectionViewMode';
+            $this->_sSectionOpen = 'getOpenSectionViewMode';
+        }
+    }
+
+    /**
+     * Return Form code
+     * @param $bDynamicMode - set it to true if form is added via JS/AJAX call, for example form in AJAX popup.
+     * @return string
+     */
+    function getCode($bDynamicMode = false)
+    {
+        $this->_bDynamicMode = $bDynamicMode;
+        $this->addCssJs ();
+        $this->aFormAttrs = $this->_replaceMarkers($this->aFormAttrs);
+        return ($this->sCode = $this->genForm());
+    }
+
+    /**
+     * Generate the whole form
+     *
+     * @return string
+     */
+    function genForm()
+    {
+        $this->_sCodeAdd = '';
+
+        $sTable = $this->genRows();
+
+        $sHtmlBefore = isset($this->aParams['html_before']) ? $this->aParams['html_before'] : '';
+        $sHtmlAfter = isset($this->aParams['html_after']) ? $this->aParams['html_after'] : '';
+
+        if (!empty($this->aParams['remove_form']) || (isset($this->aParams['view_mode']) && $this->aParams['view_mode'])) {
+            $sForm = <<<BLAH
+                    $sHtmlBefore
+                    {$this->_sCodeAdd}
+                    <div class="bx-form-advanced-wrapper {$this->id}_wrapper">
+                        $sTable
+                    </div>
+                    $sHtmlAfter
+BLAH;
+        } else {
+            $sFormAttrs = bx_convert_array2attrs($this->aFormAttrs, 'bx-form-advanced');
+
+            $sForm = <<<BLAH
+                $sHtmlBefore
+                <form $sFormAttrs>
+                    {$this->_sCodeAdd}
+                    <div class="bx-form-advanced-wrapper {$this->id}_wrapper">
+                        $sTable
+                    </div>
+                </form>
+                <script>
+                    $(document).ready(function() {
+                        $(this).addWebForms();
+                    });
+                </script>
+                $sHtmlAfter
+BLAH;
+        }
+
+        return $sForm;
+    }
+
+    /**
+     * Generate Table HTML code
+     *
+     * @return string
+     */
+    function genRows()
+    {
+        // add CSRF token if it's needed.
+        if (!(isset($this->aParams['view_mode']) && $this->aParams['view_mode']) && getParam('sys_security_form_token_enable') == 'on' && (!isset($this->aParams['csrf']['disable']) || (isset($this->aParams['csrf']['disable']) && $this->aParams['csrf']['disable'] !== true)) && ($mixedCsrfToken = BxDolForm::getCsrfToken()) !== false) {
+            $this->aInputs['csrf_token'] = array(
+                'type' => 'hidden',
+                'name' => 'csrf_token',
+                'value' => $mixedCsrfToken,
+                'db' => array ('pass' => 'Xss'),
+                'visible_for_levels' => PHP_INT_MAX,
+            );
+        }
+
+        // check if we need to generate open section clause
+        $sOpenSection = '';
+        foreach ($this->aInputs as $aInput) {
+            if (isset($aInput['type']) && 'hidden' == $aInput['type'])
+                continue;
+            if (isset($aInput['type']) && 'block_header' != $aInput['type'])
+                $sOpenSection = $this->{$this->_sSectionOpen}();
+            break;
+        }
+
+        // generate rows contents
+        $sCont = '';
+        $sFuncGenRow = isset($this->aParams['view_mode']) && $this->aParams['view_mode'] ? 'genViewRow' : 'genRow';
+        foreach ($this->aInputs as $aInput)
+            if (!isset($aInput['visible_for_levels']) || $this->_isVisible($aInput))
+                $sCont .= $this->$sFuncGenRow($aInput);
+
+        $sCloseSection = $this->{$this->_sSectionClose}();
+
+        return $sOpenSection . $sCont . $sCloseSection;
+    }
+
+    /**
+     * Generate single Table Row
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genRow(&$aInput)
+    {
+        if (!isset($aInput['type']))
+            $aInput['type'] = false;
+
+        if (!empty($aInput['name'])) {
+            $sCustomMethod = 'genCustomRow' . $this->_genMethodName($aInput['name']);
+            if (method_exists($this, $sCustomMethod))
+                return $this->$sCustomMethod($aInput);
+        }
+
+        switch ($aInput['type']) {
+
+            case 'block_header':
+                $sRow = $this->genRowBlockHeader($aInput);
+            break;
+
+            case 'block_end':
+                $sRow = $this->genBlockEnd($aInput);
+            break;
+
+            case 'hidden':
+                // do not generate row for hidden inputs
+                $sRow = '';
+                $this->_sCodeAdd .= $this->genInput($aInput);
+            break;
+
+            case 'select_box':
+                $sRow = $this->genRowCustom($aInput, 'genInputSelectBox');
+            break;
+
+            case 'files':
+                $sRow = $this->genRowCustom($aInput, 'genInputFiles');
+            break;
+
+            case 'switcher':
+            case 'checkbox':
+                $sRow = $this->genRowStandard($aInput, true);
+            break;
+
+            default:
+                $sRow = $this->genRowStandard($aInput);
+        }
+
+        return $sRow;
+    }
+
+    /**
+     * Generate single Table Row for view mode
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genViewRow(&$aInput)
+    {
+        if (!isset($aInput['type']))
+            $aInput['type'] = false;
+
+        if (!empty($aInput['name'])) {
+            $sCustomMethod = 'genCustomRow' . $this->_genMethodName($aInput['name']);
+            if (method_exists($this, $sCustomMethod))
+                return $this->$sCustomMethod($aInput);
+        }
+
+        switch ($aInput['type']) {
+
+            case 'block_header':
+                $sRow = $this->genRowBlockHeader($aInput);
+            break;
+
+            case 'block_end':
+                $sRow = $this->genBlockEnd($aInput);
+            break;
+
+            default:
+                $sRow = $this->genViewRowWrapped($aInput);
+        }
+
+        return $sRow;
+    }
+
+    /**
+     * Generate complete wrapped row for view mode form
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genViewRowWrapped(&$aInput)
+    {
+        $sValue = $this->genViewRowValue($aInput);
+        if (null === $sValue)
+            return '';
+
+        $sCaption = isset($aInput['caption']) ? bx_process_output($aInput['caption']) : '';
+
+        return <<<EOS
+            <div class="bx-form-row-view-wrapper bx-form-row-view-wrapper-{$aInput['type']} bx-def-padding-sec-top">
+                <div class="bx-form-row-view-caption bx-def-margin-sec-right">{$sCaption}:</div>
+                <div class="bx-form-row-view-value">{$sValue}</div>
+            </div>
+EOS;
+    }
+
+    /**
+     * Generate value for view mode row
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genViewRowValue(&$aInput)
+    {
+        switch ($aInput['type']) {
+
+            case 'hidden':
+                $sValue = null;
+            break;
+
+            case 'select':
+            case 'radio_set':
+                $sValue = isset($aInput['value']) && isset($aInput['values'][$aInput['value']]) ? $aInput['values'][$aInput['value']] : null;
+            break;
+
+            case 'datepicker':
+                $iTime = bx_process_input ($aInput['value'], BX_DATA_DATE_TS, false, false);
+                $sValue = bx_time_js ($iTime, BX_FORMAT_DATE);
+            break;
+            case 'date_time':
+            case 'datetime':
+                $iTime = bx_process_input ($aInput['value'], BX_DATA_DATETIME_TS, false, false);
+                $sValue = bx_time_js ($iTime, BX_FORMAT_DATE_TIME);
+            break;
+
+            case 'checkbox_set':
+            case 'select_multiple':
+                $sValue = null;
+                if (!empty($aInput['value']) && is_array($aInput['value'])) {
+                    $sValue = '';
+                    foreach ($aInput['value'] as $sVal)
+                        $sValue .= $aInput['values'][$sVal] . ', ';
+                    $sValue = trim ($sValue, ', ');
+                }
+            break;
+
+            case 'checkbox':
+            case 'switcher':
+                $sValue = isset($aInput['checked']) && $aInput['checked'] ? _t('_sys_form_checkbox_value_on') : _t('_sys_form_checkbox_value_off');
+            break;
+
+            case 'textarea':
+                $sValue = isset($aInput['value']) ? $aInput['value'] : null;
+            break;
+
+            default:
+                $sValue = isset($aInput['value']) ? bx_process_output($aInput['value']) : null;
+        }
+
+        return $sValue;
+    }
+
+    /**
+     * Generate standard row
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genRowStandard(&$aInput, $isOneLine = false)
+    {
+        $sCaption = isset($aInput['caption']) ? bx_process_output($aInput['caption']) : '';
+
+        $sRequired = !empty($aInput['required']) ? '<span class="bx-form-required">*</span> ' : '';
+
+        $sClassAdd = !empty($aInput['error']) ? ' bx-form-error' : '';
+        $sInfoIcon = !empty($aInput['info']) ? $this->genInfoIcon($aInput['info']) : '';
+
+        $sErrorIcon = $this->genErrorIcon(empty($aInput['error']) ? '' : $aInput['error']);
+
+        if (isset($aInput['name']))
+            $aInput['tr_attrs']['id'] = "bx-form-element-" . $aInput['name'];
+        $sTrAttrs = bx_convert_array2attrs(isset($aInput['tr_attrs']) && is_array($aInput['tr_attrs']) ? $aInput['tr_attrs'] : array(), "bx-form-element-wrapper bx-def-margin-top");
+
+        $sClassOneLineCaption = '';
+        $sClassOneLineValue = '';
+        if ($isOneLine) {
+            $sClassOneLineCaption = ' bx-form-caption-oneline bx-form-caption-oneline-' . $aInput['type'] . ' bx-def-margin-sec-left';
+            $sClassOneLineValue = ' bx-form-value-oneline bx-form-value-oneline-' . $aInput['type'];
+            $aInput['attrs']['id'] = $this->getInputId($aInput);
+            if ($sCaption)
+                $sCaption = '<label for="' . $aInput['attrs']['id'] . '">' . $sCaption . '</label>';
+        }
+
+        $sInput = $this->genInput($aInput);
+
+        $sCaptionCode = '';
+        if ($sCaption)
+            $sCaptionCode = '<div class="bx-form-caption' . $sClassOneLineCaption . '">' . $sCaption . $sRequired . '</div>';
+        else
+            $sInput .= $sRequired;
+
+        $sInputCode = $this->genWrapperInput($aInput, $sInput);
+
+
+        if (empty($sInputCodeExtra)) $sInputCodeExtra = '';
+        if (empty($sInfoIcon)) $sInfoIcon = '';
+        if (empty($sInputCode)) $sInputCode = '';
+        if (empty($sErrorIcon)) $sErrorIcon = '';
+
+        $sValueCode = '
+                    <div class="bx-form-value bx-clearfix' . $sClassAdd . $sClassOneLineValue . '">
+                        ' . $sInputCode . '
+                        ' . ($isOneLine ? '' : $sInfoIcon . $sErrorIcon) . '
+                        ' . $sInputCodeExtra . '
+                    </div>';
+
+        if ($isOneLine)
+            $sCode = $sValueCode . $sCaptionCode . '<div class="bx-clear"></div>' . $sInfoIcon . $sErrorIcon;
+        else
+            $sCode = $sCaptionCode . $sValueCode;
+
+        return "<div $sTrAttrs>" . $sCode . "</div>";
+    }
+
+
+    function genWrapperInput($aInput, $sContent)
+    {
+        $sAttr = isset($aInput['attrs_wrapper']) && is_array($aInput['attrs_wrapper']) ? bx_convert_array2attrs($aInput['attrs_wrapper']) : '';
+
+        $sCode = <<<BLAH
+                <div class="bx-form-input-wrapper bx-form-input-wrapper-{$aInput['type']}" $sAttr>
+                    $sContent
+                </div>
+BLAH;
+
+        return $sCode;
+    }
+
+    /**
+     * Generate custom row
+     *
+     * @param  array  $aInput
+     * @param  string $sCustomMethod custom method to generate code for input
+     * @return string
+     */
+    function genRowCustom(&$aInput, $sCustomMethod)
+    {
+        $sCaption = isset($aInput['caption']) ? bx_process_output($aInput['caption']) : '';
+
+        $sRequired = !empty($aInput['required']) ? '<span class="bx-form-required">*</span> ' : '';
+
+        $sClassAdd = !empty($aInput['error']) ? ' bx-form-error' : '';
+        $sInfoIcon = !empty($aInput['info']) ? $this->genInfoIcon($aInput['info']) : '';
+
+        $sErrorIcon = $this->genErrorIcon(empty($aInput['error']) ? '' : $aInput['error']);
+        $sInput = $this->$sCustomMethod($aInput, $sInfoIcon, $sErrorIcon);
+
+        if (isset($aInput['name']))
+            $aInput['tr_attrs']['id'] = "bx-form-element-" . $aInput['name'];
+        $sTrAttrs = bx_convert_array2attrs(empty($aInput['tr_attrs']) ? array() : $aInput['tr_attrs'], "bx-form-element-wrapper bx-def-margin-top");
+
+        $sCaptionCode = '';
+        if ($sCaption)
+            $sCaptionCode = '<div class="bx-form-caption">' . $sCaption . $sRequired . '</div>';
+
+        $sCode = <<<BLAH
+                <div $sTrAttrs>
+                    $sCaptionCode
+                    <div class="bx-form-value$sClassAdd">
+                        <div class="bx-clear"></div>
+                            $sInput
+                        <div class="bx-clear"></div>
+                    </div>
+                </div>
+BLAH;
+
+        return $sCode;
+    }
+
+    /**
+     * Generate Block Headers row
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genRowBlockHeader(&$aInput)
+    {
+        $aAttrs = empty($aInput['attrs']) ? '' : $aInput['attrs'];
+
+        // if there is no caption - show divider only
+
+        if (empty($aInput['caption'])) {
+            $sCode = $this->{$this->_sSectionClose}();
+            $sCode .= $this->{$this->_sSectionOpen}($aAttrs);
+            return $sCode;
+        }
+
+        // if section is collapsed by default, add necessary code
+
+        $sClassAddCollapsable = 'bx-form-collapsable';
+        if (isset($aInput['collapsed']) and $aInput['collapsed'])
+            $sClassAddCollapsable .= ' bx-form-collapsed bx-form-section-hidden';
+
+        // display section with caption
+
+        $sCode = $this->{$this->_sSectionClose}();
+
+        if (empty($aAttrs))
+            $aAttrs['class'] = 'bx-form-collapsable ' . $sClassAddCollapsable;
+        else
+            $aAttrs['class'] .= ' bx-form-collapsable ' . $sClassAddCollapsable;
+
+
+        if (isset($this->aParams['view_mode']) && $this->aParams['view_mode'])
+            $sLegend = '<legend class="bx-def-padding-sec-right bx-def-font-grayed bx-def-font-h3">' . bx_process_output($aInput['caption']) . '</legend>';
+        else
+            $sLegend = '<legend class="bx-def-padding-left bx-def-padding-sec-right bx-def-font-grayed bx-def-font-h3"><a href="javascript:void(0);">' . bx_process_output($aInput['caption']) . '</a></legend>';
+
+        $sCode .= $this->{$this->_sSectionOpen}($aAttrs, $sLegend);
+
+        return $sCode;
+    }
+
+    function genBlockEnd()
+    {
+        $aNextTbodyAdd = false; // need to have some default
+        $sCode = '';
+        $sCode .= $this->{$this->_sSectionClose}();
+        $sCode .= $this->{$this->_sSectionOpen}($aNextTbodyAdd);
+        return $sCode;
+    }
+
+    /**
+     * Generate HTML Input Element
+     *
+     * @param  array  $aInput
+     * @return string Output HTML Code
+     */
+    function genInput(&$aInput)
+    {
+        if (!empty($aInput['name'])) {
+            $sCustomMethod = 'genCustomInput' . $this->_genMethodName($aInput['name']);
+            if (method_exists($this, $sCustomMethod))
+                return $this->$sCustomMethod($aInput);
+        }
+
+        switch ($aInput['type']) {
+
+            // standard inputs (and non-standard, interpreted as standard)
+            case 'text':
+            case 'datepicker':
+            case 'date_time':
+            case 'datetime':
+            case 'number':
+            case 'checkbox':
+            case 'radio':
+            case 'file':
+            case 'image':
+            case 'password':
+            case 'slider':
+            case 'doublerange':
+            case 'hidden':
+                $sInput = $this->genInputStandard($aInput);
+            break;
+
+            case 'switcher':
+                $sInput = $this->genInputSwitcher($aInput);
+            break;
+
+            case 'button':
+            case 'reset':
+            case 'submit':
+                $sInput = $this->genInputButton($aInput);
+            break;
+
+            case 'textarea':
+                $sInput = $this->genInputTextarea($aInput);
+            break;
+
+            case 'select':
+                $sInput = $this->genInputSelect($aInput);
+            break;
+
+            case 'select_multiple':
+                $sInput = $this->genInputSelectMultiple($aInput);
+            break;
+
+            case 'checkbox_set':
+                $sInput = $this->genInputCheckboxSet($aInput);
+            break;
+
+            case 'radio_set':
+                $sInput = $this->genInputRadioSet($aInput);
+            break;
+
+            case 'input_set': // numeric array of inputs
+                $sInput = '';
+                $sDivider = isset($aInput['dv']) ? $aInput['dv'] : ' ';
+                foreach ($aInput as $iKey => $aSubInput) {
+                    if (!is_int($iKey) or !$aSubInput)
+                        continue; // parse only integer keys and existing values
+
+                    $sInput .= $this->genInput($aSubInput); // recursive call
+                    $sInput .= $sDivider;
+                }
+                $sInput .= '<div class="bx-clear"></div>';
+            break;
+
+            case 'custom':
+                $sInput = isset($aInput['content']) ? $aInput['content'] : '';
+            break;
+
+            case 'captcha':
+                $sInput = $this->genInputCaptcha($aInput);
+            break;
+
+            case 'value':
+                $sInput = $aInput['value'];
+            break;
+
+            default:
+                //unknown control type
+                $sInput = 'Unknown control type';
+        }
+
+        // create input label
+        $sInput .= $this->genLabel($aInput);
+
+        return $sInput;
+    }
+
+    /**
+     * Generate new Input Element id
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function getInputId(&$aInput)
+    {
+        if (isset($aInput['id']))
+            return $aInput['id'];
+
+        $sPattern = 'a-z0-9';
+
+        $sName = preg_replace("/[^$sPattern]/i", '_', $aInput['name']);
+
+        $sID = $this->id . '_input_' . $sName;
+
+        if ( // multiple elements cause identical id's
+            (
+                (
+                    $aInput['type'] == 'checkbox' and
+                    substr($aInput['name'], -2) == '[]' // it is multiple element
+                ) or
+                $aInput['type'] == 'radio' // it is always multiple (i think so... hm)
+            ) and
+            isset($aInput['value']) // if we can make difference
+        ) {
+            $sValue = preg_replace("/[^$sPattern]/i", '_', $aInput['value']);
+
+            // add value
+            $sID .= '_' . $sValue;
+        }
+
+        $sID = trim($sID, '_');
+
+        $aInput['id'] = $sID; // just for repeated calls
+
+        return $sID;
+    }
+
+    /**
+     * Generate standard Input Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputStandard(&$aInput)
+    {
+        // clone attributes for system use ;)
+        $aAttrs = empty($aInput['attrs']) ? array() : $aInput['attrs'];
+
+        // add default className to attributes
+        $aAttrs['type'] = $aInput['type'];
+        if ('datetime' == $aAttrs['type'])
+            $aAttrs['type'] = 'date_time';
+
+        if (isset($aInput['name'])) $aAttrs['name'] = $aInput['name'];
+        if (isset($aInput['value'])) $aAttrs['value'] = $aInput['value'];
+
+
+
+        // for inputs with labels generate id
+        if (isset($aInput['label']))
+            $aAttrs['id'] = $this->getInputId($aInput);
+
+        // for checkboxes
+        if (isset($aInput['checked']) and $aInput['checked'])
+            $aAttrs['checked'] = 'checked';
+
+        $sAttrs = bx_convert_array2attrs($aAttrs, "bx-def-font-inputs bx-form-input-{$aInput['type']}");
+
+        return  "<input $sAttrs />\n";
+    }
+
+    /**
+     * Generate Switcher Input Element (based on checkbox)
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputSwitcher(&$aInput)
+    {
+        $aInput['type'] = 'checkbox';
+        $sCheckbox = $this->genInputStandard($aInput);
+        $aInput['type'] = 'switcher';
+
+        $sClass = 'off';
+        if (isset($aInput['checked']) and $aInput['checked'])
+            $sClass = 'on';
+
+        return '
+            <div class="bx-switcher-cont ' . $sClass . '">' . $sCheckbox . '
+                <div class="bx-switcher-canvas">
+                    <div class="bx-switcher-on"><i class="sys-icon check"></i></div>
+                    <div class="bx-switcher-off"><i class="sys-icon times"></i></div>
+                    <div class="bx-switcher-handler">&nbsp;</div>
+                </div>
+            </div>';
+    }
+
+    /**
+     * Generate Button Input Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputButton(&$aInput)
+    {
+        // clone attributes for system use ;)
+        $aAttrs = empty($aInput['attrs']) ? array() : $aInput['attrs'];
+
+        // add default className to attributes
+        $aAttrs['type'] = $aInput['type'];
+        if (isset($aInput['value']))
+            $aAttrs['value'] = $aInput['value'];
+
+        if (isset($aInput['name'])) $aAttrs['name'] = $aInput['name'];
+
+        $sAttrs = bx_convert_array2attrs($aAttrs, "bx-def-font-inputs bx-form-input-{$aInput['type']} bx-btn" . ('submit' == $aInput['type'] ? ' bx-btn-primary' : ''));
+
+        return  "<button $sAttrs>" . $aInput['value'] . "</button>\n";
+    }
+
+    /**
+     * Generate Textarea Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputTextarea(&$aInput)
+    {
+        // clone attributes for system use ;)
+        $aAttrs = empty($aInput['attrs']) ? array() : $aInput['attrs'];
+
+        $aAttrs['name'] = $aInput['name'];
+
+        // for inputs with labels generate id
+        if (isset($aInput['label']))
+            $aAttrs['id'] = $this->getInputId($aInput);
+
+        $sAttrs = bx_convert_array2attrs($aAttrs, "bx-def-font-inputs bx-form-input-{$aInput['type']}" . ((isset($aInput['html']) and $aInput['html'] and $this->addHtmlEditor($aInput['html'], $aInput)) ? ' bx-form-input-html' : ''));
+
+        $sValue = isset($aInput['value']) ? bx_process_output($aInput['value']) : '';
+
+        return "<textarea $sAttrs>$sValue</textarea>";
+    }
+
+    function addHtmlEditor($iViewMode, &$aInput)
+    {
+        bx_import('BxDolEditor');
+        $oEditor = BxDolEditor::getObjectInstance();
+        if (!$oEditor)
+            return false;
+
+        $this->_sCodeAdd .= $oEditor->attachEditor ('#' . $this->aFormAttrs['id'] . ' [name='.$aInput['name'].']', $iViewMode, $this->_bDynamicMode);
+
+        return true;
+    }
+
+    /**
+     * Generate Select Box Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputSelectBox(&$aInput, $sInfo = '', $sError = '')
+    {
+        $aNewInput = $aInput;
+
+        $aNewInput['type'] = 'select';
+        $aNewInput['name'] .= '[]';
+
+        $sInput = $this->genInput($aNewInput);
+        return <<<BLAH
+                <div class="bx-form-input-wrapper input-wrapper-{$aInput['type']}">
+                   $sInput
+                </div>
+                $sInfo
+                $sError
+BLAH;
+
+    }
+
+    /**
+     * Generate Select Box Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputFiles(&$aInput, $sInfo = '', $sError = '')
+    {
+        bx_import('BxDolUploader');
+
+        $sUniqId = genRndPwd (8, false);
+        $sUploaders = '';
+        $oUploader = null;
+        foreach ($aInput['uploaders'] as $sUploaderObject) {
+            $oUploader = BxDolUploader::getObjectInstance($sUploaderObject, $aInput['storage_object'], $sUniqId);
+            if (!$oUploader)
+                continue;
+
+            $sGhostTemplate = false;
+            if (isset($aInput['ghost_template']) && is_object($aInput['ghost_template'])) { // form is not submitted and ghost template is BxDolFormNested object
+
+                $oFormNested = $aInput['ghost_template'];
+                if ($oFormNested instanceof BxDolFormNested)
+                    $sGhostTemplate = $oFormNested->getCode();
+
+            } elseif (isset($aInput['ghost_template']) && is_array($aInput['ghost_template']) && isset($aInput['ghost_template']['inputs'])) { // form is not submitted and ghost template is form array
+
+                bx_import('BxDolFormNested');
+                $oFormNested = new BxDolFormNested($aInput['name'], $aInput['ghost_template'], $this->aParams['db']['submit_name'], $this->oTemplate);
+                $sGhostTemplate = $oFormNested->getCode();
+
+            } elseif (isset($aInput['ghost_template']) && is_array($aInput['ghost_template']) && $aInput['ghost_template']) { // form is submitted and ghost template is array of BxDolFormNested objects
+
+                bx_import('BxDolFormNested');
+                $sGhostTemplate = array ();
+                foreach ($aInput['ghost_template'] as $iFileId => $oFormNested)
+                    if (is_object($oFormNested) && $oFormNested instanceof BxDolFormNested)
+                        $sGhostTemplate[$iFileId] = $oFormNested->getCode();
+
+            } elseif (isset($aInput['ghost_template']) && is_string($aInput['ghost_template'])) { // ghost template is just string template, without nested form
+
+                $sGhostTemplate = $aInput['ghost_template'];
+
+            }
+
+            $aParams = array(
+                'button_title' => bx_js_string($oUploader->getUploaderButtonTitle(isset($aInput['upload_buttons_titles']) ? $aInput['upload_buttons_titles'] : false)),
+                'content_id' => isset($aInput['content_id']) ? $aInput['content_id'] : '',
+            );
+            if (isset($aInput['images_transcoder']) && $aInput['images_transcoder'])
+                $aParams['images_transcoder'] = bx_js_string($aInput['images_transcoder']);
+
+            $sUploaders .= $oUploader->getUploaderButton($sGhostTemplate, isset($aInput['multiple']) ? $aInput['multiple'] : true, $aParams);
+        }
+
+        return $this->oTemplate->parseHtmlByName('form_field_uploader.html', array(
+            'uploaders_buttons' => $sUploaders,
+            'info' => $sInfo,
+            'error' => $sError,
+            'id_container_errors' => $oUploader ? $oUploader->getIdContainerErrors() : '',
+            'id_container_result' => $oUploader ? $oUploader->getIdContainerResult() : '',
+            'uploader_instance_name' => $oUploader ? $oUploader->getNameJsInstanceUploader() : '',
+            'is_init_ghosts' => isset($aInput['init_ghosts']) && !$aInput['init_ghosts'] ? 0 : 1,
+        ));
+    }
+
+    /**
+     * Generate Select Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputSelect(&$aInput)
+    {
+        $sCurValue = isset($aInput['value']) ? $aInput['value'] : '';
+        return $this->_genInputSelect($aInput, false, $sCurValue, '_isSelected');
+    }
+
+    /**
+     * Generate Multiple Select Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputSelectMultiple(&$aInput)
+    {
+        $aCurValues = array();
+        if (isset($aInput['value']) && $aInput['value'])
+            $aCurValues = is_array($aInput['value']) ? $aInput['value'] : array();
+        return $this->_genInputSelect($aInput, true, $aCurValues, '_isSelectedMultiple');
+    }
+
+    /**
+     * Generate Checkbox Set Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputCheckboxSet(&$aInput)
+    {
+        $aCurValues = array();
+        if (isset($aInput['value']) && $aInput['value'])
+            $aCurValues = is_array($aInput['value']) ? $aInput['value'] : array();
+        return $this->_genInputsSet($aInput, 'checkbox', $aCurValues, '_isSelectedMultiple', '[]');
+    }
+    /**
+     * Generate Radiobuttons Set Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputRadioSet(&$aInput)
+    {
+        $sCurValue = isset($aInput['value']) ? $aInput['value'] : '';
+        return $this->_genInputsSet($aInput, 'radio', $sCurValue, '_isSelected');
+    }
+
+    function _isSelected ($sValue, $sCurValue)
+    {
+        return ((string)$sValue === (string)$sCurValue);
+    }
+
+    function _isSelectedMultiple ($sValue, $aCurValues)
+    {
+        return in_array($sValue, $aCurValues);
+    }
+
+    function _genInputSelect(&$aInput, $isMultiple, $mixedCurrentVal, $sIsSelectedFunc)
+    {
+        $aAttrs = empty($aInput['attrs']) ? array() : $aInput['attrs'];
+
+        $aAttrs['name'] = $aInput['name'];
+        if ($isMultiple) {
+            $aAttrs['name'] .= '[]';
+            $aAttrs['multiple'] = 'multiple';
+        }
+
+        // for inputs with labels generate id
+        if (isset($aInput['label']))
+            $aAttrs['id'] = $this->getInputId($aInput);
+
+        $sAttrs = bx_convert_array2attrs($aAttrs, "bx-def-font-inputs bx-form-input-{$aInput['type']}");
+
+        // generate options
+        $sOptions = '';
+        if (isset($aInput['values']) and is_array($aInput['values'])) {
+            foreach ($aInput['values'] as $sValue => $sTitle) {
+                $sClassC = $sStyleC = "";
+                if(is_array($sTitle)) {
+                    if(isset($sTitle['type'])) {
+                        switch($sTitle['type']) {
+                            case 'group_header':
+                                $sTitle = bx_process_output($sTitle['value']);
+                                $sOptions .= <<<BLAH
+                                       <optgroup label="$sTitle">
+BLAH;
+                                break;
+                            case 'group_end':
+                                $sOptions .= <<<BLAH
+                                       </optgroup>
+BLAH;
+                                break;
+                        }
+                        continue;
+                    }
+
+                    $sClassC = isset($sTitle['class']) ? " class=\"" . $sTitle['class'] . "\"" : "";
+                    $sStyleC = isset($sTitle['style']) ? " style=\"" . $sTitle['style'] . "\"" : "";
+                    $sValue = $sTitle['key'];
+                    $sTitle = $sTitle['value'];
+                }
+                $sValueC = bx_html_attribute($sValue);
+                $sTitleC = bx_process_output($sTitle);
+
+                $sSelected = $this->$sIsSelectedFunc($sValue, $mixedCurrentVal) ? 'selected="selected"' : '';
+
+                $sOptions .= <<<BLAH
+                   <option value="$sValueC"$sClassC$sStyleC $sSelected>$sTitleC</option>
+BLAH;
+
+            }
+        }
+
+        // generate element
+        $sCode = <<<BLAH
+            <select $sAttrs>
+                $sOptions
+            </select>
+BLAH;
+
+        return $sCode;
+    }
+
+    function _genInputsSet(&$aInput, $sType, $mixedCurrentVal, $sIsCheckedFunc, $sNameAppend = '')
+    {
+        $aAttrs = empty($aInput['attrs']) || 'radio_set' == $aInput['type'] || 'checkbox_set' == $aInput['type'] ? array() : $aInput['attrs'];
+
+        $aAttrs['name']  = $aInput['name'];
+
+        // for inputs with labels generate id
+        if (isset($aInput['label']))
+            $aAttrs['id'] = $this->getInputId($aInput);
+
+        $sAttrs = bx_convert_array2attrs($aAttrs, "bx-form-input-{$aInput['type']}");
+
+        // generate options
+        $sDivider = isset($aInput['dv']) ? $aInput['dv'] : $this->_sDivider;
+
+        $sOptions = '';
+
+        if (isset($aInput['values']) and is_array($aInput['values'])) {
+            if (count($aInput['values']) > 3 && $sDivider == $this->_sDivider)
+                $sDivider = $this->_sDividerAlt;
+            // generate complex input using simple standard inputs
+            foreach ($aInput['values'] as $sValue => $sLabel) {
+                // create new simple input
+                $aNewInput = array(
+                    'type'    => $sType,
+                    'name'    => $aInput['name'] . $sNameAppend,
+                    'value'   => $sValue,
+                    'checked' => $this->$sIsCheckedFunc($sValue, $mixedCurrentVal),
+                    'label'   => $sLabel,
+                    'attrs'   => !empty($aInput['attrs']) && ('radio_set' == $aInput['type'] || 'checkbox_set' == $aInput['type']) ? $aInput['attrs'] : false,
+                );
+
+                $sNewInput  = $this->genInput($aNewInput);
+
+                // attach new input to complex
+                $sOptions .= ($sNewInput . $sDivider);
+            }
+        }
+
+        // generate element
+        $sCode = <<<BLAH
+            <div $sAttrs>
+                $sOptions
+            </div>
+BLAH;
+
+        return $sCode;
+    }
+
+    function genInputCaptcha(&$aInput)
+    {
+        $aAttrs = empty($aInput['attrs']) ? array() : $aInput['attrs'];
+
+        // for inputs with labels generate id
+        if (isset($aInput['label']))
+            $aAttrs['id'] = $this->getInputId($aInput);
+
+        $sAttrs = bx_convert_array2attrs($aAttrs, "bx-form-input-{$aInput['type']}");
+
+        bx_import('BxDolCaptcha');
+        $oCaptcha = BxDolCaptcha::getObjectInstance();
+
+        return "<div $sAttrs>" . ($oCaptcha ? $oCaptcha->display($this->_bDynamicMode) : _t('_sys_txt_captcha_not_available')) . "</div>";
+    }
+
+    /**
+     * Generate Label Element
+     *
+     * @param  string $sLabel   Text of the Label
+     * @param  string $sInputID Dependant Input Element ID
+     * @return string HTML code
+     */
+    function genLabel(&$aInput)
+    {
+        if (!isset($aInput['label']) or empty($aInput['label']))
+            return '';
+
+        $sLabel   = $aInput['label'];
+        $sInputID = $this->getInputId($aInput);
+
+        $sRet = '<label for="' . $sInputID . '">' . bx_process_output($sLabel) . '</label>';
+
+        return $sRet;
+    }
+
+    function genInfoIcon($sInfo)
+    {
+        return '<div class="bx-form-info bx-def-font-grayed bx-def-font-small">' . bx_process_output($sInfo) . '</div>';
+    }
+
+    function genErrorIcon( $sError = '' )
+    {
+        if ($this->bEnableErrorIcon) {
+            $sStyle = '';
+            if (!$sError)
+                $sStyle = 'style="display:none;"';
+            return '<div class="bx-form-warn" ' . $sStyle . '>' . $sError . '</div>';
+        }
+    }
+
+    function getOpenSection($aAttrs = array(), $sLegend = '')
+    {
+        if (!$this->_isSectionOpened) {
+
+            if (!$aAttrs || !is_array($aAttrs))
+                $aAttrs = array();
+
+            if ($sLegend)
+                $sClassesAdd = "bx-form-section-header";
+            else
+                $sClassesAdd = "bx-form-section-divider";
+
+            $sAttrs = bx_convert_array2attrs($aAttrs, "bx-form-section bx-def-padding-sec-top bx-def-border-top " . $sClassesAdd);
+
+            $this->_isSectionOpened = true;
+
+            return "<!-- form header content begins -->\n <div class=\"bx-form-section-wrapper bx-def-margin-top\"> <fieldset $sAttrs> $sLegend <div class=\"bx-form-section-content bx-def-padding-top bx-def-padding-bottom" . ($sLegend ? ' bx-def-padding-left bx-def-padding-right' : '') . "\">\n";
+
+        } else {
+
+            return '';
+        }
+    }
+
+    function getCloseSection()
+    {
+        if ($this->_isSectionOpened) {
+
+            $this->_isSectionOpened = false;
+            return "</div> </fieldset> </div> \n<!-- form header content ends -->\n";
+
+        } else {
+
+            return '';
+        }
+    }
+
+    function getOpenSectionViewMode($aAttrs = array(), $sLegend = '')
+    {
+        if (!$this->_isSectionOpened) {
+
+            if (!$aAttrs || !is_array($aAttrs))
+                $aAttrs = array();
+
+            if ($sLegend)
+                $sClassesAdd = "bx-form-section-header";
+            else
+                $sClassesAdd = "bx-form-section-divider";
+
+            $sAttrs = bx_convert_array2attrs($aAttrs, "bx-form-section bx-form-view-section bx-def-padding-sec-top bx-def-border-top " . $sClassesAdd);
+
+            $this->_isSectionOpened = true;
+
+            return "<!-- form header content begins -->\n <div class=\"bx-form-section-wrapper bx-def-margin-top\"> <fieldset $sAttrs> $sLegend <div class=\"bx-form-section-content bx-def-padding-top\">\n";
+
+        } else {
+
+            return '';
+        }
+    }
+
+    function getCloseSectionViewMode()
+    {
+        if ($this->_isSectionOpened) {
+
+            $this->_isSectionOpened = false;
+            return "</div> </fieldset> </div> \n<!-- form header content ends -->\n";
+
+        } else {
+
+            return '';
+        }
+    }
+
+    function addCssJs ()
+    {
+        if (isset($this->aParams['view_mode']) && $this->aParams['view_mode']) {
+
+            if (self::$_isCssJsAddedViewMode)
+                return;
+
+            $this->oTemplate->addCss('forms.css');
+
+            self::$_isCssJsAddedViewMode = true;
+
+        } else {
+
+            if (self::$_isCssJsAdded)
+                return;
+
+            $aCss = array(
+                'forms.css',
+
+                'jquery-ui/jquery-ui.css',
+
+                'timepicker-addon/jquery-ui-timepicker-addon.css',
+            );
+
+            $aUiLangs = array ('af' => 1, 'ar-DZ' => 1, 'ar' => 1, 'az' => 1, 'be' => 1, 'bg' => 1, 'bs' => 1, 'ca' => 1, 'cs' => 1, 'cy-GB' => 1, 'da' => 1, 'de' => 1, 'el' => 1, 'en-AU' => 1, 'en-GB' => 1, 'en-NZ' => 1, 'en' => 1, 'eo' => 1, 'es' => 1, 'et' => 1, 'eu' => 1, 'fa' => 1, 'fi' => 1, 'fo' => 1, 'fr-CA' => 1, 'fr-CH' => 1, 'fr' => 1, 'gl' => 1, 'he' => 1, 'hi' => 1, 'hr' => 1, 'hu' => 1, 'hy' => 1, 'id' => 1, 'is' => 1, 'it' => 1, 'ja' => 1, 'ka' => 1, 'kk' => 1, 'km' => 1, 'ko' => 1, 'ky' => 1, 'lb' => 1, 'lt' => 1, 'lv' => 1, 'mk' => 1, 'ml' => 1, 'ms' => 1, 'nb' => 1, 'nl-BE' => 1, 'nl' => 1, 'nn' => 1, 'no' => 1, 'pl' => 1, 'pt-BR' => 1, 'pt' => 1, 'rm' => 1, 'ro' => 1, 'ru' => 1, 'sk' => 1, 'sl' => 1, 'sq' => 1, 'sr-SR' => 1, 'sr' => 1, 'sv' => 1, 'ta' => 1, 'th' => 1, 'tj' => 1, 'tr' => 1, 'uk' => 1, 'vi' => 1, 'zh-CN' => 1, 'zh-HK' => 1, 'zh-TW' => 1);
+
+            $aCalendarLangs = array ('af' => 1, 'am' => 1, 'bg' => 1, 'ca' => 1, 'cs' => 1, 'da' => 1, 'de' => 1, 'el' => 1, 'es' => 1, 'et' => 1, 'eu' => 1, 'fi' => 1, 'fr' => 1, 'gl' => 1, 'he' => 1, 'hr' => 1, 'hu' => 1, 'id' => 1, 'it' => 1, 'ja' => 1, 'ko' => 1, 'lt' => 1, 'nl' => 1, 'no' => 1, 'pl' => 1, 'pt-BR' => 1, 'pt' => 1, 'ro' => 1, 'ru' => 1, 'sk' => 1, 'sr-RS' => 1, 'sr-YU' => 1, 'sv' => 1, 'th' => 1, 'tr' => 1, 'uk' => 1, 'vi' => 1, 'zh-CN' => 1, 'zh-TW' => 1);
+
+            bx_import('BxDolLanguages');
+            $sCalendarLang = BxDolLanguages::getInstance()->detectLanguageFromArray ($aCalendarLangs);
+            $sUiLang = BxDolLanguages::getInstance()->detectLanguageFromArray ($aUiLangs);
+
+            $aJs = array(
+                'jquery.webForms.js',
+
+                'jquery-ui/jquery.ui.core.min.js',
+                'jquery-ui/jquery.ui.widget.min.js',
+                'jquery-ui/jquery.ui.mouse.min.js',
+                'jquery-ui/jquery.ui.position.min.js',
+                'jquery-ui/jquery.ui.slider.min.js',
+                'jquery-ui/jquery.ui.datepicker.min.js',
+                'jquery-ui/i18n/jquery.ui.datepicker-' . $sUiLang . '.js',
+
+                'timepicker-addon/jquery-ui-timepicker-addon.min.js',
+                'timepicker-addon/jquery-ui-sliderAccess.js',
+                'timepicker-addon/i18n/jquery-ui-timepicker-' . $sCalendarLang . '.js',
+            );
+
+            foreach ($this->aInputs as $aInput) {
+                if (!isset($aInput['type']) || 'files' != $aInput['type'] || !isset($aInput['uploaders']))
+                    continue;
+
+                bx_import('BxDolUploader');
+                foreach ($aInput['uploaders'] as $sUploaderObject) {
+                    $oUploader = BxDolUploader::getObjectInstance($sUploaderObject, $aInput['storage_object'], '');
+                    if ($oUploader)
+                        $oUploader->addCssJs();
+                }
+            }
+                    
+
+            $this->oTemplate->addJs($aJs);
+            $this->oTemplate->addCss($aCss);
+
+            self::$_isCssJsAdded = true;
+
+        }
+    }
+}
+
+/** @} */

+ 376 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseFunctions.php

@@ -0,0 +1,376 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolTemplate');
+
+class BxBaseFunctions extends BxDol implements iBxDolSingleton
+{
+    protected $_oTemplate;
+    protected $_sDesignBoxIcon = 'chevron';
+
+    protected function __construct($oTemplate)
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
+
+        parent::__construct();
+
+        $this->_oTemplate = $oTemplate ? $oTemplate : BxDolTemplate::getInstance();
+    }
+
+    /**
+     * Prevent cloning the instance
+     */
+    public function __clone()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
+    }
+
+    public static function getInstance()
+    {
+        if(!isset($GLOBALS['bxDolClasses']['BxTemplFunctions']))
+            $GLOBALS['bxDolClasses']['BxTemplFunctions'] = new BxTemplFunctions();
+
+        return $GLOBALS['bxDolClasses']['BxTemplFunctions'];
+    }
+
+    function TemplPageAddComponent($sKey)
+    {
+        switch( $sKey ) {
+            case 'something':
+                return false; // return here additional components
+            default:
+                return false; // if you have not such component, return false!
+        }
+    }
+
+    function msgBox($sText, $iTimer = 0, $sOnClose = "")
+    {
+        $iId = time() . mt_rand(1, 1000);
+
+        return $this->_oTemplate->parseHtmlByName('messageBox.html', array(
+            'id' => $iId,
+            'msgText' => $sText,
+            'bx_if:timer' => array(
+                'condition' => $iTimer > 0,
+                'content' => array(
+                    'id' => $iId,
+                    'time' => 1000 * $iTimer,
+                    'on_close' => $sOnClose,
+                )
+            )
+        ));
+    }
+
+    /**
+     * Get standard popup box with title.
+     *
+     * @param  string $sName    - unique name
+     * @param  string $sTitle   - translated title
+     * @param  string $sContent - content of the box
+     * @return HTML   string
+     */
+    function popupBox($sName, $sTitle, $sContent, $isHiddenByDefault = false)
+    {
+        $iId = !empty($sName) ? $sName : time();
+
+        return $this->_oTemplate->parseHtmlByName('popup_box.html', array(
+            'id' => $iId,
+            'wrapper_style' => $isHiddenByDefault ? 'display:none;' : '',
+            'title' => $sTitle,
+            'content' => $sContent
+        ));
+    }
+
+    /**
+     * Get popup box without title.
+     *
+     * @param  string $sName    - unique name
+     * @param  string $sContent - content of the box
+     * @return HTML   string
+     */
+    function transBox($sName, $sContent, $isHiddenByDefault = false, $isPlaceInCenter = false)
+    {
+        $iId = !empty($sName) ? $sName : time();
+
+        return
+            ($isPlaceInCenter ? '<div class="login_ajax_wrap">' : '') .
+                $this->_oTemplate->parseHtmlByName('popup_trans.html', array(
+                    'id' => $iId,
+                    'wrapper_style' => $isHiddenByDefault ? 'display:none;' : '',
+                    'content' => $sContent
+                )) .
+            ($isPlaceInCenter ? '</div>' : '');
+    }
+
+    function slideBox($sName, $sContent, $isHiddenByDefault = false)
+    {
+        $iId = !empty($sName) ? $sName : time();
+
+        return $this->_oTemplate->parseHtmlByName('popup_slide.html', array(
+            'id' => $iId,
+            'wrapper_style' => $isHiddenByDefault ? 'display:none;' : '',
+            'content' => $sContent
+        ));
+    }
+
+    function getTemplateIcon($sName)
+    {
+        $sUrl = $this->_oTemplate->getIconUrl($sName);
+        return !empty($sUrl) ? $sUrl : $this->_oTemplate->getIconUrl('spacer.gif');
+    }
+
+    function getTemplateImage($sName)
+    {
+        $sUrl = $this->_oTemplate->getImageUrl($sName);
+        return !empty($sUrl) ? $sUrl : $this->_oTemplate->getImageUrl('spacer.gif');
+    }
+
+    function sysIcon ($sIcon, $sName, $sUrl = '', $iWidth = 0)
+    {
+        return '<div class="sys_icon">' . ($sUrl ? '<a title="'.$sName.'" href="'.$sUrl.'">' : '') . '<img alt="'.$sName.'" src="'.$sIcon.'" '.($iWidth ? 'width='.$iWidth : '').' />' . ($sUrl ? '</a>' : '') . '</div>';
+    }
+
+    /**
+     * functions for limiting maximal string length
+     */
+    function getStringWithLimitedLength($sString, $iWidth = 45, $isPopupOnOverflow = false, $bReturnString = true)
+    {
+        if (empty($sString) || mb_strlen($sString, 'UTF-8') <= $iWidth)
+            return $bReturnString ? $sString : array($sString);
+
+        $sResult = '';
+        $aWords = mb_split("[\s\r\n]", $sString);
+        $iPosition = 0;
+        $iWidthReal = $iWidth - 3;
+        $iWidthMin = $iWidth - 15;
+        foreach($aWords as $sWord) {
+            $sWord = trim($sWord);
+            $iWord = mb_strlen($sWord, 'UTF-8');
+            if ($iPosition + $iWord > $iWidthReal)
+                break;
+
+            // add word and continue
+            $sResult .= ' ' . $sWord;
+            $iPosition += 1 + $iWord;
+        }
+
+        // last word is too long, cut it
+        if(!$iPosition || $iPosition < $iWidthMin)
+            $sResult .= ' ' . mb_substr($sWord, 0, $iWidthReal - $iPosition - $iWord, 'UTF-8');
+        $sResult = trim($sResult);
+
+        // add tripple dot
+        if(!$isPopupOnOverflow) {
+            $sResult .= '...';
+            return $bReturnString ? $sResult : array($sResult);
+        }
+
+        // add button width popup
+        $sId = 'bx-str-limit-' . rand(1, PHP_INT_MAX);
+        $sPopup = '<img class="bx-str-limit" onclick="$(\'#' . $sId . '\').dolPopup({pointer:{el:$(this), offset:\'10 1\'}})" src="' . $this->getTemplateImage('str-limit.png') . '"/>';
+        $sPopup .= '<div id="' . $sId . '" style="display:none;">' . BxTemplFunctions::getInstance()->transBox('', '<div class="bx-def-padding bx-def-color-bg-block">'.$sString.'</div>') . '</div>';
+
+        return $bReturnString ? $sResult . $sPopup : array($sResult, $sPopup);
+    }
+
+    /**
+     * Display design box with specified title, template, content and menu.
+     * @param $sTitle - design box title, please note that some templates don't use title.
+     * @param $sContent - design box content.
+     * @param $iTemplateNum - number of design box template, use predefined contants only, default is BX_DB_DEF.
+     * @param $mixedMenu - design box menu, it can be:
+     *      - object: instance of BxTemplMenu class
+     *      - string: menu object identifier
+     *      - array: array of menu links to create menu from
+     * @return string
+     *
+     * @see BX_DB_CONTENT_ONLY
+     * @see BX_DB_DEF
+     * @see BX_DB_EMPTY
+     * @see BX_DB_NO_CAPTION
+     * @see BX_DB_PADDING_CONTENT_ONLY
+     * @see BX_DB_PADDING_DEF
+     * @see BX_DB_PADDING_NO_CAPTION
+     */
+    function designBoxContent ($sTitle, $sContent, $iTemplateNum = BX_DB_DEF, $mixedMenu = false)
+    {
+        return $this->_oTemplate->parseHtmlByName('designbox_' . (int)$iTemplateNum . '.html', array(
+            'title' => $sTitle,
+            'designbox_content' => $sContent,
+            'caption_item' => $this->designBoxMenu ($mixedMenu, array (array('menu' => 1))),
+        ));
+    }
+
+    function designBoxMenu ($mixedMenu, $aButtons = array ())
+    {
+        $sCode = '';
+        $aButtonMenu = false;
+        if ($mixedMenu) {
+
+            $sMenu = '';
+
+            if (is_string($mixedMenu)) {
+
+                bx_import('BxTemplMenu');
+                $oMenu = BxTemplMenu::getObjectInstance($mixedMenu);
+                $sMenu = $oMenu ? $oMenu->getCode () : $mixedMenu;
+
+            } elseif (is_object($mixedMenu) && is_a($mixedMenu, 'BxTemplMenu')) {
+
+                $sMenu = $mixedMenu->getCode();
+
+            } elseif (is_array($mixedMenu)) {
+
+                if (isset($mixedMenu['template']) && isset($mixedMenu['menu_items']))
+                    $aMenu = $mixedMenu;
+                else
+                    $aMenu = array ('template' => 'menu_vertical.html', 'menu_items' => $mixedMenu);
+                bx_import('BxTemplMenu');
+                $oMenu = new BxTemplMenu($aMenu, $this->_oTemplate);
+                $sMenu = $oMenu->getCode ();
+            }
+
+            if ($sMenu) {
+                $sId = 'bx-menu-db-' . time() . rand(0, PHP_INT_MAX);
+                $sCode .= $this->slideBox($sId, '<div class="bx-def-padding">' . $sMenu . '</div>', true);
+                $aButtonMenu = array ('icon-a' => $this->_sDesignBoxIcon, 'onclick' => "bx_menu_slide('#" . $sId . "', this)");
+            }
+
+        }
+
+        if ($aButtons) {
+            $sCode .= '<div class="bx-db-menu"><div class="bx-db-menu-tab bx-db-menu-tab-btn">';
+            foreach ($aButtons as $aButton) {
+                if (isset($aButton['menu']) && $aButton['menu']) {
+                    if (!$aButtonMenu)
+                        continue;
+                    $aButton = $aButtonMenu;
+                }
+
+                $aAttrs = array ();
+                if (!empty($aButton['onclick']))
+                    $aAttrs['onclick'] = $aButton['onclick'];
+                if (!empty($aButton['class']))
+                    $aAttrs['class'] = $aButton['class'];
+                $sAttrs = bx_convert_array2attrs ($aAttrs, 'bx-def-margin-sec-left');
+
+                $sCode .= '<a href="javascript:void(0);" ' . $sAttrs . '>';
+                $sCode .= !empty($aButton['icon']) ? '<i class="sys-icon ' . $aButton['icon'] . ' bx-def-font-h2"></i>' : '';
+                $sCode .= !empty($aButton['icon-a']) ? '<i class="sys-icon-a" data-rotate="down" data-icon="' . $aButton['icon-a'] . '"></i>' : '';
+                $sCode .= !empty($aButton['title']) ? $aButton['title'] : '';
+                $sCode .= '</a>';
+            }
+            $sCode .= '</div></div>';
+        }
+
+        return $sCode;
+    }
+
+    /**
+     * Get logo HTML.
+     * @return string
+     */
+    function getMainLogo()
+    {
+        bx_import('BxDolConfig');
+
+        $sAlt = getParam('sys_site_logo_alt') ? getParam('sys_site_logo_alt') : getParam('site_title');
+        $sLogo = $sAlt;
+
+        $iFileId = (int)getParam('sys_site_logo');
+        if ($iFileId) {
+            bx_import('BxDolStorage');
+            $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+            $sFileUrl = $oStorage->getFileUrlById($iFileId);
+            if ($sFileUrl)
+                $sLogo = '<img src="' . $sFileUrl . '" id="bx-logo" class="bx-def-margin-sec" alt="' . bx_html_attribute($sAlt, BX_ESCAPE_STR_QUOTE) . '" />';
+        }
+
+        return '<a class="bx-def-font-contrasted" href="' . BX_DOL_URL_ROOT . '" title="' . bx_html_attribute($sAlt, BX_ESCAPE_STR_QUOTE) . '">' . $sLogo . '</a>';
+    }
+
+    /**
+     * Get HTML code for meta icons.
+     * @return HTML string to insert into HEAD section
+     */
+    function getMetaIcons()
+    {
+        $iId = (int)getParam('sys_site_icon');
+        $sImageUrlFav = $sImageUrlFcb = $sImageUrlApl = '';
+
+        if(!empty($iId)) {
+            bx_import('BxDolImageTranscoder');
+
+            // favicon icon
+            $oTranscoder = BxDolImageTranscoder::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_FAVICON);
+            $sImageUrlFav = $oTranscoder->getImageUrl($iId);
+
+            // facebook icon
+            $oTranscoder = BxDolImageTranscoder::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_FACEBOOK);
+            $sImageUrlFcb = $oTranscoder->getImageUrl($iId);
+
+            // apple touch icon
+            $oTranscoder = BxDolImageTranscoder::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_APPLE);
+            $sImageUrlApl = $oTranscoder->getImageUrl($iId);
+        }
+
+        if(empty($sImageUrlFav))
+            $sImageUrlFav = $this->_oTemplate->getIconUrl('favicon.png');
+
+        if(empty($sImageUrlFcb))
+            $sImageUrlFcb = $this->_oTemplate->getIconUrl('facebook-icon.png');
+
+        if(empty($sImageUrlApl))
+            $sImageUrlApl = $this->_oTemplate->getIconUrl('apple-touch-icon.png');
+
+        $sRet = '';
+        $sRet .= '<link rel="icon" sizes="16x16" type="image/png" href="' . $sImageUrlFav . '" />';
+        $sRet .= '<link rel="image_src" sizes="100x100" href="' . $sImageUrlFcb . '" />';
+        $sRet .= '<link rel="apple-touch-icon" sizes="152x152" href="' . $sImageUrlApl . '" />';
+
+        return $sRet;
+    }
+
+    /**
+     * Output time wrapped in <time> tag in HTML.
+     * Then time is autoformatted using JS upon page load, this is aumatically converted to user's timezone and
+     * updated in realtime in case of short periods of 'from now' time format.
+     *
+     * Short version of this function:
+     * @see bx_time_js
+     *
+     * @param $iUnixTimestamp time as unixtimestamp
+     * @param $sFormatIdentifier output format identifier
+     *     @see BX_FORMAT_DATE
+     *     @see BX_FORMAT_TIME
+     *     @see BX_FORMAT_DATE_TIME
+     * @param $bForceFormat force provided format and don't use "from now" time autoformat.
+     */
+    function timeForJs ($iUnixTimestamp, $sFormatIdentifier = BX_FORMAT_DATE, $bForceFormat = false)
+    {
+        $sDateUTC = bx_time_utc ($iUnixTimestamp);
+        return '<time datetime="' . $sDateUTC . '" data-bx-format="' . getParam($sFormatIdentifier) . '" data-bx-autoformat="' . ($bForceFormat ? 0 : getParam('sys_format_timeago')) . '">' . $sDateUTC . '</time>';
+    }
+
+    function statusOnOff ($mixed, $isMsg = false)
+    {
+        if ((is_bool($mixed) && !$mixed) || (is_string($mixed) && 'fail' == $mixed))
+            return '<i class="sys-icon circle col-red2"></i> ' . ($isMsg ? _t('_sys_off') : '');
+        elseif (is_string($mixed) && 'warn' == $mixed)
+            return '<i class="sys-icon circle col-red3"></i> ' . ($isMsg ? _t('_sys_warn') : '');
+        elseif (is_string($mixed) && 'undef' == $mixed)
+            return '<i class="sys-icon circle col-gray"></i> ' . ($isMsg ? _t('_undefined') : '');
+        else
+            return '<i class="sys-icon circle col-green1"></i> ' . ($isMsg ? _t('_sys_on') : '');
+    }
+}
+
+/** @} */

+ 167 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseMenu.php

@@ -0,0 +1,167 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolMenu');
+bx_import('BxDolPaginate');
+
+/**
+ * Menu representation.
+ * @see BxDolMenu
+ */
+class BxBaseMenu extends BxDolMenu
+{
+    protected $_oTemplate;
+    protected $_aOptionalParams = array('target' => '', 'onclick' => '');
+
+    public function __construct ($aObject, $oTemplate)
+    {
+        parent::__construct ($aObject);
+
+        if ($oTemplate)
+            $this->_oTemplate = $oTemplate;
+        else
+            $this->_oTemplate = BxDolTemplate::getInstance();
+    }
+
+    /**
+     * Get menu code.
+     * @return string
+     */
+    public function getCode ()
+    {
+        $sMenuTitle = isset($this->_aObject['title']) ? _t($this->_aObject['title']) : 'Menu-' . rand(0, PHP_INT_MAX);
+        if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->beginMenu($sMenuTitle);
+
+        $s = '';
+        $aVars = $this->_getTemplateVars ();
+        if (!empty($aVars['bx_repeat:menu_items'])) {
+            $this->_addJsCss();
+            $s = $this->_oTemplate->parseHtmlByName($this->_aObject['template'], $aVars);
+        }
+
+        if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->endMenu($sMenuTitle);
+
+        return $s;
+    }
+
+    /**
+     * Get template variables array
+     * @return array
+     */
+    protected function _getTemplateVars ()
+    {
+        return array (
+            'object' => $this->_sObject,
+            'bx_repeat:menu_items' => $this->getMenuItems (),
+        );
+    }
+
+    /**
+     * Get menu items array, which are ready to pass to template.
+     * @return array
+     */
+    public function getMenuItems ()
+    {
+        if (!isset($this->_aObject['menu_items']))
+            $this->_aObject['menu_items'] = $this->_oQuery->getMenuItems();
+
+		$aItems = array();
+        foreach ($this->_aObject['menu_items'] as $aItem) {
+        	$aItem = $this->_getMenuItem ($aItem);
+        	if($aItem !== false)
+            	$aItems[] = $aItem;
+        }
+
+        return $aItems;
+    }
+
+	protected function _getMenuItem ($a)
+	{
+		if (isset($a['active']) && !$a['active'])
+			return false;
+
+		if (isset($a['visible_for_levels']) && !$this->_isVisible($a))
+        	return false;
+
+		$a['title'] = _t($a['title']);
+
+		$a = $this->_replaceMarkers($a);
+
+		$mixedAddon = $this->_getMenuAddon($a);
+		$this->addMarkers(array('addon' => $mixedAddon));
+
+		$a = $this->_replaceMarkers($a);
+
+		list ($sIcon, $sIconUrl) = $this->_getMenuIcon($a);
+
+		$a['class_add'] = $this->_isSelected($a) ? 'bx-menu-tab-active' : '';
+		$a['link'] = isset($a['link']) ? $this->_oPermalinks->permalink($a['link']) : 'javascript:void(0);';
+		$a['title_attr'] = bx_html_attribute($a['title']);
+		$a['bx_if:image'] = array (
+			'condition' => (bool)$sIconUrl,
+			'content' => array('icon_url' => $sIconUrl),
+		);
+		$a['bx_if:icon'] = array (
+        	'condition' => (bool)$sIcon,
+            'content' => array('icon' => $sIcon),
+		);
+		$a['bx_if:title'] = array (
+			'condition' => (bool)$a['title'],
+			'content' => array('title' => $a['title']),
+		);
+		$a['bx_if:addon'] = array (
+			'condition' => (bool)$mixedAddon,
+			'content' => array('addon' => $mixedAddon),
+		);
+
+		foreach ($this->_aOptionalParams as $sName => $sDefaultValue)
+        	if (!isset($a[$sName]))
+            	$a[$sName] = $sDefaultValue;
+
+		return $a;
+	}
+
+    protected function _getMenuIcon ($a)
+    {
+        $sIcon = false;
+        $sIconUrl = false;
+        if (!empty($a['icon'])) {
+            if ((int)$a['icon'] > 0 ) {
+                bx_import('BxDolStorage');
+                $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+                $sIconUrl = $oStorage ? $oStorage->getFileUrlById((int)$a['icon']) : false;
+            } else {
+                if (false === strpos($a['icon'], '.')) // font icons
+                    $sIcon = $a['icon'];
+                else
+                    $sIconUrl = $this->_oTemplate->getIconUrl($a['icon']);
+            }
+        }
+        return array ($sIcon, $sIconUrl);
+    }
+
+    protected function _getMenuAddon ($aMenuItem)
+    {
+        if (empty($aMenuItem['addon']))
+            return '';
+
+        return BxDolService::callSerialized($aMenuItem['addon'], $this->_aMarkers);
+    }
+
+    /**
+     * Add css/js files which are needed for menu display and functionality.
+     */
+    protected function _addJsCss()
+    {
+        $this->_oTemplate->addCss('menu.css');
+    }
+
+}
+
+/** @} */

+ 85 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBasePageAccount.php

@@ -0,0 +1,85 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxTemplPage');
+
+/**
+ * Account page.
+ */
+class BxBasePageAccount extends BxTemplPage
+{
+    protected $_aMapStatus2LangKey = array (
+        BX_PROFILE_STATUS_PENDING => '_sys_txt_account_pending',
+        BX_PROFILE_STATUS_SUSPENDED => '_sys_txt_account_suspended',
+    );
+
+    public function __construct($aObject, $oTemplate)
+    {
+        parent::__construct($aObject, $oTemplate);
+
+        bx_import('BxDolProfile');
+        $oProfile = BxDolProfile::getInstance();
+        $aProfileInfo = $oProfile ? $oProfile->getInfo() : false;
+
+        $this->addMarkers(array(
+            'account_id' => $aProfileInfo ? $aProfileInfo['account_id'] : 0,
+            'profile_id' => $aProfileInfo ? $aProfileInfo['id'] : 0,
+            'profile_type' => $aProfileInfo ? $aProfileInfo['type'] : 0,
+            'profile_content_id' => $aProfileInfo ? $aProfileInfo['content_id'] : 0,
+        ));
+
+        // set settings submenu
+        bx_import('BxDolMenu');
+        $oMenuSubmenu = BxDolMenu::getObjectInstance('sys_site_submenu');
+        if ($oMenuSubmenu) {
+            $oMenuSubmenu->setObjectSubmenu('sys_account_settings_submenu', array (
+                'title' => _t('_sys_menu_item_title_account_settings'),
+                'link' => BX_DOL_URL_ROOT . 'member.php',
+                'icon' => '',
+            ));
+        }
+
+        // display message if profile isn't active
+        if ($oProfile) {
+            $sStatus = $oProfile->getStatus();
+            if (isset($this->_aMapStatus2LangKey[$sStatus])) {
+                bx_import('BxDolInformer');
+                $oInformer = BxDolInformer::getInstance($this->_oTemplate);
+                if ($oInformer)
+                    $oInformer->add('sys-account-status-not-active', _t($this->_aMapStatus2LangKey[$sStatus]), BX_INFORMER_ALERT);
+            }
+        }
+
+        // switch profile context
+        if ($iSwitchToProfileId = (int)bx_get('switch_to_profile')) {
+            bx_import('BxDolInformer');
+            $oInformer = BxDolInformer::getInstance($this->_oTemplate);
+            $oProfile = BxDolProfile::getInstance($iSwitchToProfileId);
+            $sInformerMsg = '';
+
+            if ($oProfile && $oProfile->getAccountId() == getLoggedId()) {
+                bx_import('BxDolProfile');
+                $oAccount = BxDolAccount::getInstance();
+                if ($oAccount->updateProfileContext($iSwitchToProfileId))
+                    $sInformerMsg = _t('_sys_txt_account_profile_context_changed_success', $oProfile->getDisplayName());
+            }
+
+            if ($oInformer)
+                $oInformer->add('sys-account-profile-context-change-result', $sInformerMsg ? $sInformerMsg : _t('_error occured'), $sInformerMsg ? BX_INFORMER_INFO : BX_INFORMER_ERROR);
+        }
+
+    }
+
+    protected function _getPageCacheParams ()
+    {
+        return getLoggedId(); // cache is different for every account
+    }
+}
+
+/** @} */

+ 309 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/scripts/BxBaseVote.php

@@ -0,0 +1,309 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolVote');
+bx_import('BxDolTemplate');
+
+/**
+ * @see BxDolVote
+ */
+class BxBaseVote extends BxDolVote
+{
+    protected $_sJsObjName;
+    protected $_sStylePrefix;
+
+    protected $_aHtmlIds;
+
+    protected $_aElementDefaults;
+
+    protected $_sTmplNameCounter;
+    protected $_sTmplNameDoVoteLikes;
+    protected $_sTmplNameDoVoteStars;
+
+    public function __construct($sSystem, $iId, $iInit = 1)
+    {
+        parent::__construct($sSystem, $iId, $iInit);
+
+        $this->_sJsObjName = 'oVote' . bx_gen_method_name($sSystem, array('_' , '-')) . $iId;
+        $this->_sStylePrefix = 'bx-vote';
+
+        $sHtmlId = str_replace(array('_' , ' '), array('-', '-'), $sSystem) . '-' . $iId;
+        $this->_aHtmlIds = array(
+            'main_stars' => 'bx-vote-stars-' . $sHtmlId,
+            'main_likes' => 'bx-vote-likes-' . $sHtmlId,
+            'counter' => 'bx-vote-counter-' . $sHtmlId,
+            'by_popup' => 'bx-vote-by-popup-' . $sHtmlId
+        );
+
+        $this->_aElementDefaults = array(
+            'stars' => array(
+                'show_do_vote_legend' => false,
+                'show_counter' => true
+            ),
+            'likes' => array(
+                'show_do_vote_as_button' => false,
+                'show_do_vote_as_button_small' => false,
+                'show_do_vote_icon' => true,
+                'show_do_vote_label' => false,
+                'show_counter' => true
+            )
+        );
+
+        $this->_sTmplNameCounter = 'vote_counter.html';
+        $this->_sTmplNameDoVoteLikes = 'vote_do_vote_likes.html';
+        $this->_sTmplNameDoVoteStars = 'vote_do_vote_stars.html';
+    }
+
+    public function addCssJs()
+    {
+        $oTemplate = BxDolTemplate::getInstance();
+
+        $oTemplate->addJs('BxDolVote.js');
+        $oTemplate->addCss('vote.css');
+    }
+
+    public function getJsObjectName()
+    {
+        return $this->_sJsObjName;
+    }
+
+    public function getJsScript()
+    {
+        $aParams = array(
+            'sObjName' => $this->_sJsObjName,
+            'sSystem' => $this->getSystemName(),
+            'iAuthorId' => $this->_getAuthorId(),
+            'iObjId' => $this->getId(),
+            'iLikeMode' => $this->isLikeMode() ? 1 : 0,
+            'sRootUrl' => BX_DOL_URL_ROOT,
+            'sStylePrefix' => $this->_sStylePrefix,
+            'aHtmlIds' => $this->_aHtmlIds
+        );
+
+        $this->addCssJs();
+        return BxDolTemplate::getInstance()->_wrapInTagJsCode("var " . $this->_sJsObjName . " = new BxDolVote(" . json_encode($aParams) . ");");
+    }
+
+    public function getJsClick()
+    {
+        if(!$this->isLikeMode())
+            return false;
+
+        return $this->getJsObjectName() . '.vote(this, ' . $this->getMaxValue() . ')';
+    }
+
+    public function getCounter($aParams = array())
+    {
+        $sJsObject = $this->getJsObjectName();
+
+        $aVote = $this->_oQuery->getVote($this->getId());
+
+        return BxDolTemplate::getInstance()->parseHtmlByName($this->_sTmplNameCounter, array(
+            'href' => 'javascript:void(0)',
+            'title' => _t('_vote_do_like_by'),
+            'bx_repeat:attrs' => array(
+                array('key' => 'id', 'value' => $this->_aHtmlIds['counter']),
+                array('key' => 'class', 'value' => $this->_sStylePrefix . '-counter'),
+                array('key' => 'onclick', 'value' => 'javascript:' . $sJsObject . '.toggleByPopup(this)')
+            ),
+            'content' => (int)$aVote['count'] > 0 ? $this->_getLabelCounter($aVote['count']) : ''
+        ));
+    }
+
+    public function getElementBlock($aParams = array())
+    {
+        $aParams['usage'] = BX_DOL_VOTE_USAGE_BLOCK;
+
+        return $this->getElement($aParams);
+    }
+
+    public function getElementInline($aParams = array())
+    {
+        $aParams['usage'] = BX_DOL_VOTE_USAGE_INLINE;
+
+        return $this->getElement($aParams);
+    }
+
+    public function getElement($aParams = array())
+    {
+        if(!$this->isAllowedVote())
+            return '';
+
+        $bLike =  $this->isLikeMode();
+        $sType = $bLike ? BX_DOL_VOTE_TYPE_LIKES : BX_DOL_VOTE_TYPE_STARS;
+
+        $bShowDoVoteAsButtonSmall = $bLike && isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true;
+        $bShowDoVoteAsButton = $bLike && !$bShowDoVoteAsButtonSmall && isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true;
+
+        $sMethodDoVote = '_getDoVote' . ucfirst($sType);
+        if(!method_exists($this, $sMethodDoVote))
+            return '';
+
+		$iObjectId = $this->getId();
+		$iAuthorId = $this->_getAuthorId();
+        $aVote = $this->_oQuery->getVote($iObjectId);
+
+        $aParams = array_merge($this->_aElementDefaults[$sType], $aParams);
+        $aParams['is_voted'] = $this->_oQuery->isVoted($iObjectId, $iAuthorId) ? true : false;
+
+        $sTmplName = 'vote_element_' . (!empty($aParams['usage']) ? $aParams['usage'] : BX_DOL_VOTE_USAGE_DEFAULT) . '.html';
+        return BxDolTemplate::getInstance()->parseHtmlByName($sTmplName, array(
+            'style_prefix' => $this->_sStylePrefix,
+            'html_id' => $this->_aHtmlIds['main_' . $sType],
+            'class' => $this->_sStylePrefix . '-' . $sType . ($bShowDoVoteAsButton ? '-button' : '') . ($bShowDoVoteAsButtonSmall ? '-button-small' : ''),
+            'rate' => $aVote['rate'],
+            'count' => $aVote['count'],
+            'do_vote' => $this->$sMethodDoVote($aParams),
+            'bx_if:show_counter' => array(
+                'condition' => isset($aParams['show_counter']) && $aParams['show_counter'] === true,
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+        			'bx_if:show_hidden' => array(
+        				'condition' => (int)$aVote['count'] == 0,
+        				'content' => array()
+        			),
+                    'counter' => $this->getCounter()
+                )
+            ),
+            'script' => $this->getJsScript()
+        ));
+    }
+
+    protected function _getDoVoteStars($aParams = array())
+    {
+        $sJsObject = $this->getJsObjectName();
+        $iMinValue = $this->getMinValue();
+        $iMaxValue = $this->getMaxValue();
+
+        $aTmplVarsButtons = $aTmplVarsStars = array();
+        for($i = $iMinValue; $i <= $iMaxValue; $i++) {
+            $aTmplVarsStars[] = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'value' => $i
+            );
+
+            $aTmplVarsLegend[] = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'value' => $i
+            );
+
+            $aTmplVarsSlider[] = array(
+                'style_prefix' => $this->_sStylePrefix
+            );
+
+            $aTmplVarsButtons[] = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'js_object' => $sJsObject,
+                'value' => $i
+            );
+        }
+
+        return BxDolTemplate::getInstance()->parseHtmlByName($this->_sTmplNameDoVoteStars, array(
+            'style_prefix' => $this->_sStylePrefix,
+            'bx_repeat:stars' => $aTmplVarsStars,
+            'bx_if:show_legend' => array(
+                'condition' => isset($aParams['show_do_vote_legend']) && $aParams['show_do_vote_legend'] === true,
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+                    'bx_repeat:legend' => $aTmplVarsLegend,
+                )
+            ),
+            'bx_repeat:slider' => $aTmplVarsSlider,
+            'bx_repeat:buttons' => $aTmplVarsButtons,
+        ));
+    }
+
+    protected function _getDoVoteLikes($aParams = array())
+    {
+    	$bVoted = isset($aParams['is_voted']) && $aParams['is_voted'] === true;
+        $bShowDoVoteAsButtonSmall = isset($aParams['show_do_vote_as_button_small']) && $aParams['show_do_vote_as_button_small'] == true;
+        $bShowDoVoteAsButton = !$bShowDoVoteAsButtonSmall && isset($aParams['show_do_vote_as_button']) && $aParams['show_do_vote_as_button'] == true;
+		$bDisabled = $bVoted && !$this->isUndo();
+
+        $sClass = '';
+		if($bShowDoVoteAsButton)
+			$sClass = 'bx-btn';
+		else if ($bShowDoVoteAsButtonSmall)
+			$sClass = 'bx-btn bx-btn-small';
+
+		if($bDisabled)
+			$sClass .= ' bx-btn-disabled';
+
+        return BxDolTemplate::getInstance()->parseHtmlByName($this->_sTmplNameDoVoteLikes, array(
+            'style_prefix' => $this->_sStylePrefix,
+            'class' => $sClass,
+            'title' => _t($bVoted && $this->isUndo() ? '_vote_do_unlike' : '_vote_do_like'),
+        	'bx_if:show_onclick' => array(
+        		'condition' => !$bDisabled,
+        		'content' => array(
+        			'js_object' => $this->getJsObjectName(),
+        			'value' => $this->getMinValue(),
+        		)
+        	),
+            'do_vote' => $this->_getLabelDoLike($aParams),
+        ));
+    }
+
+    protected function _getLabelCounter($iCount)
+    {
+        return $iCount;
+    }
+
+    protected function _getLabelDoLike($aParams = array())
+    {
+    	$bVoted = isset($aParams['is_voted']) && $aParams['is_voted'] === true;
+    	$oTemplate = BxDolTemplate::getInstance();
+
+        $sResult = '';
+        if(isset($aParams['show_do_vote_icon']) && $aParams['show_do_vote_icon'] == true)
+            $sResult .= $oTemplate->parseHtmlByName('bx_icon.html', array('name' => $this->_getIconDoLike($bVoted)));
+
+        if(isset($aParams['show_do_vote_label']) && $aParams['show_do_vote_label'] == true)
+            $sResult .= ($sResult != '' ? ' ' : '') . $oTemplate->parseHtmlByName('bx_span.html', array(
+            	'bx_repeat:attrs' => array(),
+            	'content' => _t($this->_getTitleDoLike($bVoted))
+            ));
+
+        return $sResult;
+    }
+
+    protected function _getVotedBy()
+    {
+        $aTmplUsers = array();
+
+        $aUserIds = $this->_oQuery->getVotedBy($this->getId());
+        foreach($aUserIds as $iUserId) {
+            list($sUserName, $sUserUrl, $sUserIcon, $sUserUnit) = $this->_getAuthorInfo($iUserId);
+            $aTmplUsers[] = array(
+                'style_prefix' => $this->_sStylePrefix,
+                'user_unit' => $sUserUnit
+            );
+        }
+
+        if(empty($aTmplUsers))
+            $aTmplUsers = MsgBox(_t('_Empty'));
+
+        return BxDolTemplate::getInstance()->parseHtmlByName('vote_by_list.html', array(
+            'style_prefix' => $this->_sStylePrefix,
+            'bx_repeat:list' => $aTmplUsers
+        ));
+    }
+
+    protected function _echoResultJson($a, $isAutoWrapForFormFileSubmit = false)
+    {
+        header('Content-type: text/html; charset=utf-8');
+
+        $s = json_encode($a);
+        if ($isAutoWrapForFormFileSubmit && !empty($_FILES))
+            $s = '<textarea>' . $s . '</textarea>'; // http://jquery.malsup.com/form/#file-upload
+        echo $s;
+    }
+}
+
+/** @} */

+ 43 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/uploader_form_html5.html

@@ -0,0 +1,43 @@
+<div id="__form_container_id__" class="bx-uploader-form-cont bx-def-padding bx-def-color-bg-block">
+    <div class="bx-def-padding-sec-bottom">__restrictions_text__</div>
+    <div id="__errors_container_id__"></div>
+    <div id="__div_id__"></div>
+</div>
+<script>
+    __uploader_instance_name__.initUploader({
+        template: '<div class="bx-uploader-html5">' + 
+                '<ul class="bx-uploader-html5-list bx-def-margin-bottom"></ul>' +
+                '<div class="bx-btn bx-btn-primary bx-uploader-html5-button bx-def-margin-sec-right"><bx_text_js:_sys_uploader_html5_shoose_file /></div>' + 
+                '<div class="bx-btn" onclick="__uploader_instance_name__.onClickCancel()"><bx_text:_sys_uploader_close /></div>' + 
+                '<div class="bx-clear">&nbsp;</div>' +  
+                '<div class="bx-uploader-html5-drop-area bx-def-margin-sec-top bx-def-z-index-front"><bx_text_js:_sys_uploader_html5_drop_area /></div>' +
+             '</div>',
+
+        // template for one item in file list
+        fileTemplate: '<li>' +
+                '<span class="bx-uploader-html5-file"></span>' +
+                '<span class="bx-uploader-html5-spinner"></span>' +
+                '<span class="bx-uploader-html5-size"></span>' +
+                '<a class="bx-uploader-html5-cancel" href="#"><bx_text_js:_Cancel /></a>' +
+                '<span class="bx-uploader-html5-failed-text"><bx_text_js:_Failed /></span>' +
+            '</li>',
+        
+        classes: {
+            // used to get elements from templates
+            button: 'bx-uploader-html5-button',
+            drop: 'bx-uploader-html5-drop-area',
+            dropActive: 'bx-uploader-html5-drop-area-active',
+            list: 'bx-uploader-html5-list',
+                        
+            file: 'bx-uploader-html5-file',
+            spinner: 'bx-uploader-html5-spinner',
+            size: 'bx-uploader-html5-size',
+            cancel: 'bx-uploader-html5-cancel',
+
+            // added to list item when upload completes
+            // used in css to hide progress spinner
+            success: 'bx-uploader-html5-success',
+            fail: 'bx-uploader-html5-fail'
+        }
+    });
+</script>

+ 1 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/vote_do_vote_likes.html

@@ -0,0 +1 @@
+<a class="__style_prefix__-do-vote __class__" href="javascript:void(0)" <bx_if:show_onclick>onclick="javascript:__js_object__.vote(this, __value__)"</bx_if:show_onclick> title="__title__">__do_vote__</a>

+ 7 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/vote_element_block.html

@@ -0,0 +1,7 @@
+<div id="__html_id__" class="__style_prefix__ __class__ bx-def-margin-left-auto bx-clearfix" bx_vote_data_rate="__rate__" bx_vote_data_count="__count__">
+	<div class="__style_prefix__-element-holder">__do_vote__</div>
+	<bx_if:show_counter>
+		<div class="__style_prefix__-counter-holder bx-def-margin-sec-left"<bx_if:show_hidden> style="display:none"</bx_if:show_hidden>>__counter__</div>
+	</bx_if:show_counter>
+</div>
+__script__

+ 4 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/files/template/vote_element_inline.html

@@ -0,0 +1,4 @@
+<span id="__html_id__" class="__style_prefix__ __class__" bx_vote_data_rate="__rate__" bx_vote_data_count="__count__">
+	<span class="__style_prefix__-element-holder">__do_vote__</span><bx_if:show_counter><span class="__style_prefix__-counter-holder bx-def-margin-sec-left"<bx_if:show_hidden> style="display:none"</bx_if:show_hidden>>__counter__</span></bx_if:show_counter>
+</span>
+__script__

+ 2 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/script.php

@@ -0,0 +1,2 @@
+<?php
+    return true;

+ 16 - 0
upgrade/files/8.0.0.A6-8.0.0.A7/sql.sql

@@ -0,0 +1,16 @@
+
+ALTER TABLE `sys_menu_items` ADD `editable` tinyint(4) NOT NULL DEFAULT '1' AFTER `copyable`;
+
+-- can be safely applied multiple times
+
+UPDATE `sys_objects_form` SET `override_class_name` = 'BxTemplCmtsForm' WHERE `object` = 'sys_comment';
+
+DELETE FROM `sys_menu_templates` WHERE `id` = 15;
+INSERT INTO `sys_menu_templates` (`id`, `template`, `title`) VALUES
+(15, 'menu_custom.html', '_sys_menu_template_title_custom');
+
+
+-- last step is to update current version
+
+UPDATE `sys_modules` SET `version` = '8.0.0-A7' WHERE `version` = '8.0.0-A6' AND `name` = 'system';
+

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio