Răsfoiți Sursa

Upgrade script from 8.0.0.B1 to 8.0.0.B2

Alex Trofimov 11 ani în urmă
părinte
comite
84e7c8021b
39 a modificat fișierele cu 16679 adăugiri și 0 ștergeri
  1. 8 0
      upgrade/files/8.0.0.B1-8.0.0.B2/check.php
  2. 14 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/README.md
  3. 56 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/cmts.php
  4. 84 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDol.php
  5. 1035 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolCmts.php
  6. 852 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolDb.php
  7. 1533 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolForm.php
  8. 236 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolLiveUpdates.php
  9. 427 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolProfile.php
  10. 134 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolProfileUndefined.php
  11. 2278 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolTemplate.php
  12. 1325 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/utils.inc.php
  13. 12 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/version.inc.php
  14. 333 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/general/classes/BxBaseModGeneralModule.php
  15. 199 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/profile/classes/BxBaseModProfileGridAdministration.php
  16. 81 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/profile/classes/BxBaseModProfilePageEntry.php
  17. 53 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/text/classes/BxBaseModTextPageAuthor.php
  18. 194 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/text/template/css/main.css
  19. 325 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/plugins_public/codemirror/codemirror.css
  20. 16 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/plugins_public/codemirror/codemirror.min.js
  21. 615 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/classes/BxDolStudioInstallerUtils.php
  22. 473 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/classes/BxDolStudioLanguagesUtils.php
  23. 102 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/classes/BxDolStudioPolyglotKeys.php
  24. 646 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/classes/BxDolStudioToolsAudit.php
  25. 398 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioDesigner.php
  26. 1358 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioFormsField.php
  27. 353 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioFormsPreLists.php
  28. 148 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioNavigationImport.php
  29. 795 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioNavigationItems.php
  30. 399 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioNavigationMenus.php
  31. 342 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioNavigationSets.php
  32. 340 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioPolyglotKeys.php
  33. 105 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/template/css/media-phone.css
  34. 525 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/template/css/menu.css
  35. 29 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/template/menu_main.html
  36. 806 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files/template/scripts/BxBaseCmts.php
  37. 43 0
      upgrade/files/8.0.0.B1-8.0.0.B2/files_delete.php
  38. 2 0
      upgrade/files/8.0.0.B1-8.0.0.B2/script.php
  39. 5 0
      upgrade/files/8.0.0.B1-8.0.0.B2/sql.sql

+ 8 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/check.php

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

+ 14 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/README.md

@@ -0,0 +1,14 @@
+#[Trident](http://www.online.me)
+
+Community software platform
+
+##License
+
+Trident is licensed under the Creative Commons Public License, see license.txt for full details.
+
+##Help
+
+- [Wiki](https://github.com/boonex/trident/wiki)
+- [Auto generated documentation](http://ci.boonex.com/docs)
+- [Forum](http://www.boonex.com/forums)
+- [Contact us](http://www.boonex.com/help/contact)

+ 56 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/cmts.php

@@ -0,0 +1,56 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    TridentCore Trident Core
+ * @{
+ */
+
+require_once('./inc/header.inc.php');
+require_once(BX_DIRECTORY_PATH_INC . "design.inc.php");
+
+bx_import('BxDolLanguages');
+
+check_logged();
+
+$sSys = isset($_REQUEST['sys']) ? bx_process_input($_REQUEST['sys']) : '';
+$iObjectId = isset($_REQUEST['id']) ? bx_process_input($_REQUEST['id'], BX_DATA_INT) : 0;
+$sAction = isset($_REQUEST['action']) && preg_match ('/^[A-Za-z_-]+$/', $_REQUEST['action']) ? bx_process_input($_REQUEST['action']) : '';
+
+$oCmts = BxDolCmts::getObjectInstance($sSys, $iObjectId, true);
+
+if ($oCmts && $sSys && $iObjectId) {
+    if($sAction) {
+        header('Content-Type: text/html; charset=utf-8');
+        $sMethod = 'action' . $sAction;
+        echo $oCmts->$sMethod();
+        exit;
+    }
+
+    $iCmtId = bx_get('cmt_id');
+    if($iCmtId !== false) {
+        $oTemplate = BxDolTemplate::getInstance();
+        $sComment = $oCmts->getCommentBlock($iCmtId);
+        if ($sComment) {
+
+            $sObjectTitle = $oCmts->getObjectTitle($iObjectId);
+
+            $sHeader = _t('_cmt_page_view_header', $sObjectTitle);
+            $sTitle = _t('_cmt_page_view_title', $oCmts->getBaseUrl(), $sObjectTitle);
+            $sContent = DesignBoxContent($sTitle, $sComment, BX_DB_PADDING_DEF);
+            
+            $oTemplate->setPageNameIndex(BX_PAGE_DEFAULT);
+            $oTemplate->setPageHeader($sHeader);
+            $oTemplate->setPageContent('page_main_code', $sContent);
+            $oTemplate->getPageCode();
+
+        } else {
+
+            $oTemplate->displayPageNotFound();
+
+        }
+    }
+}
+
+/** @} */

+ 84 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDol.php

@@ -0,0 +1,84 @@
+<?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    TridentCore Trident Core
+ * @{
+ */
+
+/**
+ * @page objects Objects
+ * Classes which represents high level programming constructions to generate ready functionality, like Comments, Votings, Forms.
+ */
+
+/**
+ * Classes instances are stored here
+ */
+$GLOBALS['bxDolClasses'] = array();
+
+/**
+ * Base class for all classes
+ */
+class BxDol
+{
+    public function BxDol () {}
+}
+
+/**
+ * Singleton interface, for objects with one instance only
+ */
+interface iBxDolSingleton
+{
+    public static function getInstance();
+}
+
+/**
+ * Factory interface for object instances, once instance per object name
+ */
+interface iBxDolFactoryObject
+{
+    static public function getObjectInstance($sObject);
+}
+
+/**
+ * Replacable interface, class has an ability to replace markers somewhere
+ */
+interface iBxDolReplaceable
+{
+    public function addMarkers ($a);
+}
+
+/**
+ * Profile interface, class must implement basic profile methods to be compliant with Profile interface
+ */
+interface iBxDolProfile
+{
+    public function id();
+    public function getDisplayName();
+    public function getUrl();
+    public function getUnit();
+    public function getAvatar();
+    public function getThumb();
+    public function getIcon();
+    public function getIconModule();
+    public function getEditUrl();
+    public function isActive();
+}
+
+/**
+ * Profile services module interface, module class must implement basic profile services to be compliant with Profile interface
+ */
+interface iBxDolProfileService
+{
+    public function serviceProfileUnit ($iContentId);
+    public function serviceProfileAvatar ($iContentId);
+    public function serviceProfileThumb ($iContentId);
+    public function serviceProfileIcon ($iContentId);
+    public function serviceProfileName ($iContentId);
+    public function serviceProfileUrl ($iContentId);
+    public function serviceProfileEditUrl ($iContentId);
+    public function serviceProfilesSearch ($sTerm, $iLimit);
+}
+
+/** @} */

+ 1035 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolCmts.php

@@ -0,0 +1,1035 @@
+<?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    TridentCore Trident Core
+ * @{
+ */
+
+bx_import('BxDolAcl');
+
+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_OTHERS', 'others');
+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
+ * $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 $_bBrowseFilter = false;
+    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->_aSystem['table_ids'] = 'sys_cmts_ids';
+
+        $this->_aSystem['is_browse_filter'] = (int)$this->_bBrowseFilter;
+
+        $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';
+
+        $this->_sMetatagsObj = 'sys_cmts';
+
+        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;
+
+        $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']);
+        }
+
+        $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']))
+        	$this->_aSystem['object_vote'] = 'sys_cmts';
+
+        $oVote = BxDolVote::getObjectInstance($this->_aSystem['object_vote'], $iId);
+        if(!$oVote || !$oVote->isEnabled())
+            return false;
+
+        return $oVote;
+    }
+
+	public function getNotificationId()
+	{
+		return 'cmts-notification-' . $this->_sSystem . '-' . $this->_iId;
+	}
+
+    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)
+    {
+    	if(empty($this->_aSystem['trigger_field_title']))
+    		return '';
+
+        return $this->_oQuery->getObjectTitle ($iObjectId ? $iObjectId : $this->getId());
+    }
+
+    public function getCommentsCountAll ($iObjectId = 0)
+    {
+        return $this->_oQuery->getCommentsCountAll ($iObjectId ? $iObjectId : $this->getId());
+    }
+
+    public function getCommentsCount ($iObjectId = 0, $iCmtVParentId = -1, $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 = $aCmtIds = array();
+        $this->_oQuery->deleteObjectComments ($iObjectId ? $iObjectId : $this->getId(), $aFiles, $aCmtIds);
+
+        // delete meta info
+        $this->deleteMetaInfo($aCmtIds);
+
+        // delete files
+        if ($aFiles) {
+            $oStorage = BxDolStorage::getObjectInstance($this->getStorageObjectName());
+            if ($oStorage)
+                $oStorage->queueFilesForDeletion($aFiles);
+        }
+    }
+
+    public static function onAuthorDelete ($iAuthorId)
+    {
+        $aSystems = self::getSystems();
+        foreach($aSystems as $sSystem => $aSystem) {
+            $o = self::getObjectInstance($sSystem, 0);
+            $oQuery = $o->getQueryObject();
+
+            // delete comments
+            $aFiles = $aCmtIds = array ();
+            $oQuery->deleteAuthorComments($iAuthorId, $aFiles, $aCmtIds);
+
+            // delete meta info
+            $o->deleteMetaInfo($aCmtIds);
+    
+            // delete files
+            $oStorage = BxDolStorage::getObjectInstance($o->getStorageObjectName());
+            if ($oStorage)
+                $oStorage->queueFilesForDeletion($aFiles);
+        }
+        return true;
+    }
+
+    public static function onModuleUninstall ($sModuleName, &$iFiles = null)
+    {
+        $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 = $aCmtIds = array ();
+            $oQuery->deleteAll($aSystem['system_id'], $aFiles, $aCmtIds);
+
+            // delete meta info
+            $o->deleteMetaInfo($aCmtIds);
+
+            // delete files
+            $oStorage = BxDolStorage::getObjectInstance($o->getStorageObjectName());
+            if ($oStorage && $aFiles)
+                $oStorage->queueFilesForDeletion($aFiles);
+
+            if (null !== $iFiles)
+                $iFiles += count($aFiles);
+        }
+
+        return true;
+    }
+
+    public function deleteMetaInfo ($mixedCmtId)
+    {
+        if (!$this->_sMetatagsObj)
+            return;
+
+        if (!is_array($mixedCmtId))
+            $mixedCmtId = array($mixedCmtId);
+
+        $oMetatags = BxDolMetatags::getObjectInstance($this->_sMetatagsObj);
+
+        foreach ($mixedCmtId as $iCmtId) {
+            $oMetatags->onDeleteContent($this->_oQuery->getUniqId($this->_aSystem['system_id'], $iCmtId));
+            $this->_oQuery->deleteCmtIds($this->_aSystem['system_id'], $iCmtId);
+        }
+    }
+
+    /**
+     * 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 '';
+
+        $aBp = $aDp = array();
+		$this->_getParams($aBp, $aDp);
+
+        return $this->getComments($aBp, $aDp);
+    }
+
+    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();
+
+            $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);
+
+            $this->deleteMetaInfo ($aCmt['cmt_id']);
+
+            $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')));
+    }
+
+    public function actionResumeLiveUpdate()
+    {
+    	$sKey = $this->getNotificationId();
+
+    	bx_import('BxDolSession');
+    	BxDolSession::getInstance()->unsetValue($sKey);
+    }
+
+	public function actionPauseLiveUpdate()
+    {
+    	$sKey = $this->getNotificationId();
+
+    	bx_import('BxDolSession');
+    	BxDolSession::getInstance()->setValue($sKey, 1);
+    }
+
+    /**
+     * 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)
+    {
+        $oProfile = BxDolProfile::getInstance($iAuthorId);
+        if (!$oProfile)
+            $oProfile = BxDolProfileUndefined::getInstance();
+
+        return $oProfile;
+    }
+
+	protected function _getFormObject($sAction = BX_CMT_ACTION_POST)
+    {
+        $sDisplayName = '_sFormDisplay' . ucfirst($sAction);
+
+        return BxDolForm::getObjectInstance($this->_sFormObject, $this->$sDisplayName);
+    }
+
+    protected function _getParams(&$aBp, &$aDp)
+    {
+    	$aBp['vparent_id'] = isset($_REQUEST['CmtParent'])? bx_process_input($_REQUEST['CmtParent'], BX_DATA_INT) : 0;
+    	$aBp['type'] = isset($_REQUEST['CmtBrowse']) ? bx_process_input($_REQUEST['CmtBrowse'], BX_DATA_TEXT) : '';
+    	$aBp['filter'] = isset($_REQUEST['CmtFilter']) ? bx_process_input($_REQUEST['CmtFilter'], BX_DATA_TEXT) : '';
+        $aBp['start'] = isset($_REQUEST['CmtStart']) ? bx_process_input($_REQUEST['CmtStart'], BX_DATA_INT) : -1;
+        $aBp['per_view'] = isset($_REQUEST['CmtPerView']) ? bx_process_input($_REQUEST['CmtPerView'], BX_DATA_INT) : -1;
+
+        $aDp['type'] = isset($_REQUEST['CmtDisplay']) ? bx_process_input($_REQUEST['CmtDisplay'], BX_DATA_TEXT) : '';
+        $aDp['blink'] = isset($_REQUEST['CmtBlink']) ? bx_process_input($_REQUEST['CmtBlink'], BX_DATA_TEXT) : '';
+    }
+
+    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, $iCmtId = 0)
+    {
+    	$s = bx_process_output($s, BX_DATA_HTML);
+    	$s = bx_convert_links($s);
+
+        if ($this->_sMetatagsObj && $iCmtId) {
+            $oMetatags = BxDolMetatags::getObjectInstance($this->_sMetatagsObj);
+            $s = $oMetatags->keywordsParse($this->_oQuery->getUniqId($this->_aSystem['system_id'], $iCmtId), $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;
+        $aDp['blink'] = isset($aDp['blink']) && !empty($aDp['blink']) ? $aDp['blink'] : array();
+        if(!is_array($aDp['blink']))
+        	$aDp['blink'] = explode(',', $aDp['blink']);
+
+        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();
+
+        $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();
+
+        $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']);
+
+        if($oProfile instanceof BxDolProfileUndefined)
+        	return;
+
+        $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=%d&cmt_id=%d', BX_DOL_URL_ROOT, $this->_sSystem, $this->_iId, $iCmtParentId);
+
+        $aTemplate = BxDolEmailTemplates::getInstance()->parseTemplate('t_CommentReplied', $aPlus);
+        return $aTemplate && sendMail($aAccount['email'], $aTemplate['Subject'], $aTemplate['Body']);
+    }
+}
+
+/** @} */

+ 852 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolDb.php

@@ -0,0 +1,852 @@
+<?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    TridentCore Trident Core
+ * @{
+ */
+
+class BxDolDb extends BxDol implements iBxDolSingleton
+{
+    protected $_bErrorChecking = true;
+    protected $_sErrorMessage;
+    protected $_sHost, $_sPort, $_sSocket, $_sDbname, $_sUser, $_sPassword;
+    protected $_rLink, $_rCurrentRes, $_iCurrentResType;
+
+    protected $_oDbCacheObject = null;
+    protected $_aParams = null;
+    protected $_sParamsCacheName = 'sys_options';
+    protected $_sStorageEngine = 'MYISAM';
+
+    /**
+     * set database parameters and connect to it
+     */
+    protected function __construct($aDbConf = false)
+    {
+        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();
+
+        if (false === $aDbConf) {
+            $this->_sHost = BX_DATABASE_HOST;
+            $this->_sPort = BX_DATABASE_PORT;
+            $this->_sSocket = BX_DATABASE_SOCK;
+            $this->_sDbname = BX_DATABASE_NAME;
+            $this->_sUser = BX_DATABASE_USER;
+            $this->_sPassword = BX_DATABASE_PASS;
+        } else {
+            $this->_sHost = $aDbConf['host'];
+            $this->_sPort = $aDbConf['port'];
+            $this->_sSocket = $aDbConf['sock'];
+            $this->_sDbname = $aDbConf['name'];
+            $this->_sUser = $aDbConf['user'];
+            $this->_sPassword = $aDbConf['pwd'];
+            $this->_bErrorChecking = isset($aDbConf['error_checking']) ? $aDbConf['error_checking'] : true;
+        }
+
+        $this->_iCurrentResType = MYSQL_ASSOC;
+
+        // connect to db automatically
+        if (empty($GLOBALS['bx_db__rLink'])) {
+            $this->connect();
+            $GLOBALS['gl_db_cache'] = array();
+        } else {
+            $this->_rLink = $GLOBALS['bx_db__rLink'];
+        }
+    }
+
+    /**
+     * 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($aDbConf = false, &$sError = null)
+    {
+        if (!isset($GLOBALS['bxDolClasses'][__CLASS__])) {
+            if (false === $aDbConf && !defined('BX_DATABASE_HOST'))
+                return null;
+            $o = new BxDolDb($aDbConf);
+            $sErrorMessage = $o->connect();
+            if ($sErrorMessage) {
+                if ($sError !== null)
+                    $sError = $sErrorMessage;
+                return null;
+            } else {
+                $GLOBALS['bxDolClasses'][__CLASS__] = $o;
+            }
+        }
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    /**
+     * connect to database with appointed parameters
+     */
+    function connect()
+    {
+        $full_host = $this->_sHost;
+        $full_host .= $this->_sPort ? ':'.$this->_sPort : '';
+        $full_host .= $this->_sSocket ? ':'.$this->_sSocket : '';
+
+        $this->_rLink = @mysql_pconnect($full_host, $this->_sUser, $this->_sPassword);
+        if (!$this->_rLink)
+            return 'Database connect failed';
+
+        if (!$this->select_db())
+            return 'Database select failed';
+
+        mysql_query("SET NAMES 'utf8'", $this->_rLink);
+        mysql_query("SET sql_mode = ''", $this->_rLink);
+        mysql_query("SET storage_engine=" . $this->_sStorageEngine, $this->_rLink);
+
+        $GLOBALS['bx_db__rLink'] = $this->_rLink;
+
+        return '';
+    }
+
+    function select_db()
+    {
+        return @mysql_select_db($this->_sDbname, $this->_rLink);
+    }
+
+    /**
+     * close mysql connection
+     */
+    function close()
+    {
+        mysql_close($this->_rLink);
+    }
+
+    /**
+     * get mysql server info
+     */
+    function getServerInfo()
+    {
+        return mysql_get_server_info($this->_rLink);
+    }
+
+    /**
+     * get mysql option
+     */
+    function getOption($sName)
+    {
+        return $this->getOne("SELECT @@{$sName}");
+    }
+
+    /**
+     * execute sql query and return one row result
+     */
+    function getRow($query, $arr_type = MYSQL_ASSOC)
+    {
+        if(!$query)
+            return array();
+        if($arr_type != MYSQL_ASSOC && $arr_type != MYSQL_NUM && $arr_type != MYSQL_BOTH)
+            $arr_type = MYSQL_ASSOC;
+        $res = $this->res ($query);
+        $arr_res = array();
+        if($res && mysql_num_rows($res)) {
+            $arr_res = mysql_fetch_array($res, $arr_type);
+            mysql_free_result($res);
+        }
+        return $arr_res;
+    }
+
+    /**
+     * execute sql query and return a column as result
+     */
+    function getColumn($sQuery)
+    {
+        if(!$sQuery)
+            return array();
+
+        $rResult = $this->res($sQuery);
+
+        $aResult = array();
+        if($rResult) {
+            while($aRow = mysql_fetch_array($rResult, MYSQL_NUM))
+                $aResult[] = $aRow[0];
+            mysql_free_result($rResult);
+        }
+        return $aResult;
+    }
+
+    /**
+     * execute sql query and return one value result
+     */
+    function getOne($query, $index = 0)
+    {
+        if(!$query)
+            return false;
+        $res = $this->res ($query);
+        $arr_res = array();
+        if($res && mysql_num_rows($res))
+            $arr_res = mysql_fetch_array($res);
+        if(count($arr_res))
+            return $arr_res[$index];
+        else
+            return false;
+    }
+
+    /**
+     * execute sql query and return the first row of result
+     * and keep $array type and poiter to all data
+     */
+    function getFirstRow($query, $arr_type = MYSQL_ASSOC)
+    {
+        if(!$query)
+            return array();
+        if($arr_type != MYSQL_ASSOC && $arr_type != MYSQL_NUM)
+            $this->_iCurrentResType = MYSQL_ASSOC;
+        else
+            $this->_iCurrentResType = $arr_type;
+        $this->_rCurrentRes = $this->res ($query);
+        $arr_res = array();
+        if($this->_rCurrentRes && mysql_num_rows($this->_rCurrentRes))
+            $arr_res = mysql_fetch_array($this->_rCurrentRes, $this->_iCurrentResType);
+        return $arr_res;
+    }
+
+    /**
+     * return next row of pointed last getFirstRow calling data
+     */
+    function getNextRow()
+    {
+        $arr_res = mysql_fetch_array($this->_rCurrentRes, $this->_iCurrentResType);
+        if($arr_res)
+            return $arr_res;
+        else {
+            mysql_free_result($this->_rCurrentRes);
+            $this->_iCurrentResType = MYSQL_ASSOC;
+            return array();
+        }
+    }
+
+    /**
+     * return number of affected rows in current mysql result
+     */
+    function getNumRows($res = false)
+    {
+        if ($res)
+            return (int)@mysql_num_rows($res);
+        elseif (!$this->_rCurrentRes)
+            return (int)@mysql_num_rows($this->_rCurrentRes);
+        else
+            return 0;
+    }
+
+    /**
+     * execute any query return number of rows affected/false
+     */
+    function getAffectedRows()
+    {
+        return mysql_affected_rows($this->_rLink);
+    }
+
+    /**
+     * execute any query return number of rows affected/false
+     */
+    function query($query)
+    {
+        $res = $this->res($query);
+        if ($res)
+            return mysql_affected_rows($this->_rLink);
+        return false;
+    }
+
+    /**
+     * execute any query
+     */
+    function res($query, $bErrorChecking = true)
+    {
+        if(!$query)
+            return false;
+
+        if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->beginQuery($query);
+
+        $res = @mysql_query($query, $this->_rLink);
+
+        if (false === $res)
+            $this->_sErrorMessage = @mysql_error($this->_rLink); // we need to remeber last error message since mysql_ping will reset it on the next line !
+        else
+            $this->_sErrorMessage = '';
+
+        if (!$res && !@mysql_ping($this->_rLink)) { // if mysql connection is lost - reconnect and try again
+            @mysql_close($this->_rLink);
+            $sErrorMessage = $this->connect();
+            if ($sErrorMessage)
+                $this->error($sErrorMessage, true);
+            $res = mysql_query($query, $this->_rLink);
+        }
+
+        if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->endQuery($res);
+
+        if (!$res && $bErrorChecking)
+            $this->error('Database query error', false, $query);
+        return $res;
+    }
+
+    /**
+     * execute sql query and return table of records as result
+     */
+    function getAll($query, $arr_type = MYSQL_ASSOC)
+    {
+        if(!$query)
+            return array();
+
+        if($arr_type != MYSQL_ASSOC && $arr_type != MYSQL_NUM && $arr_type != MYSQL_BOTH)
+            $arr_type = MYSQL_ASSOC;
+
+        $res = $this->res ($query);
+        $arr_res = array();
+        if($res) {
+            while($row = mysql_fetch_array($res, $arr_type))
+                $arr_res[] = $row;
+            mysql_free_result($res);
+        }
+        return $arr_res;
+    }
+
+    /**
+     * execute sql query and return table of records as result
+     */
+    function fillArray($res, $arr_type = MYSQL_ASSOC)
+    {
+        if(!$res)
+            return array();
+
+        if($arr_type != MYSQL_ASSOC && $arr_type != MYSQL_NUM && $arr_type != MYSQL_BOTH)
+            $arr_type = MYSQL_ASSOC;
+
+        $arr_res = array();
+        while($row = mysql_fetch_array($res, $arr_type))
+            $arr_res[] = $row;
+        mysql_free_result($res);
+
+        return $arr_res;
+    }
+
+    /**
+     * execute sql query and return table of records as result
+     */
+    function getAllWithKey($query, $sFieldKey)
+    {
+        if(!$query)
+            return array();
+
+        $res = $this->res ($query);
+        $arr_res = array();
+        if($res) {
+            while($row = mysql_fetch_array($res, MYSQL_ASSOC)) {
+                $arr_res[$row[$sFieldKey]] = $row;
+            }
+            mysql_free_result($res);
+        }
+        return $arr_res;
+    }
+
+    /**
+     * execute sql query and return table of records as result
+     */
+    function getPairs($query, $sFieldKey, $sFieldValue)
+    {
+        if(!$query)
+            return array();
+
+        $res = $this->res ($query);
+        $arr_res = array();
+        if($res) {
+            while($row = mysql_fetch_array($res, MYSQL_ASSOC)) {
+                $arr_res[$row[$sFieldKey]] = $row[$sFieldValue];
+            }
+            mysql_free_result($res);
+        }
+        return $arr_res;
+    }
+
+    function lastId()
+    {
+        return mysql_insert_id($this->_rLink);
+    }
+
+    function getErrorMessage ()
+    {
+        $s = mysql_error($this->_rLink);
+        if ($s)
+            return $s;
+        else
+            return $this->_sErrorMessage;
+    }
+
+    function error($text, $isForceErrorChecking = false, $sSqlQuery = '')
+    {
+        if ($this->_bErrorChecking || $isForceErrorChecking)
+            $this->genMySQLErr ($text, $sSqlQuery);
+        else
+            $this->log($text . ': ' . $this->getErrorMessage());
+    }
+
+    protected function isParamInCache($sKey)
+    {
+        return is_array($this->_aParams) && isset($this->_aParams[$sKey]);
+    }
+
+    protected function cacheParams($bForceCacheInvalidate = false)
+    {
+        if ($bForceCacheInvalidate)
+            $this->cacheParamsClear();
+
+        $this->_aParams = $this->fromCache($this->_sParamsCacheName, 'getPairs', "SELECT `name`, `value` FROM `sys_options`", "name", "value");
+        if (empty($this->_aParams)) {
+            $this->_aParams = array ();
+            return false;
+        }
+
+        return true;
+    }
+
+    public function cacheParamsClear()
+    {
+        return $this->cleanCache($this->_sParamsCacheName);
+    }
+
+    public function isParam($sKey, $bFromCache = true)
+    {
+        if ($bFromCache && $this->isParamInCache($sKey))
+           return true;
+
+        $sQuery = $this->prepare("SELECT `name` FROM `sys_options` WHERE `name` = ? LIMIT 1", $sKey);
+        return $this->getOne($sQuery) == $sKey;
+    }
+
+    function addParam($sName, $sValue, $iKateg, $sDesc, $sType)
+    {
+        $sQuery = $this->prepare("INSERT INTO `sys_options` SET `category_id` = ?, `name` = ?, `caption` = ?, `value` = ?, `type` = ?", $iKateg, $sName, $sDesc, $sValue, $sType);
+        $this->query($sQuery);
+
+        // renew params cache
+        $this->cacheParams(true);
+    }
+
+    function getParam($sKey, $bFromCache = true)
+    {
+        if (!$sKey)
+            return false;
+        if ($bFromCache && $this->isParamInCache($sKey)) {
+            return $this->_aParams[$sKey];
+        } else {
+            $sQuery = $this->prepare("SELECT `value` FROM `sys_options` WHERE `name` = ? LIMIT 1", $sKey);
+            return $this->getOne($sQuery);
+        }
+    }
+
+    function setParam($sKey, $mixedValue)
+    {
+        $sQuery = $this->prepare("UPDATE `sys_options` SET `value` = ? WHERE `name` = ? LIMIT 1", $mixedValue, $sKey);
+        $bResult = (int)$this->query($sQuery) > 0;
+
+        // renew params cache
+        $bResult &= $this->cacheParams(true);
+
+        return $bResult;
+    }
+
+    function listTables()
+    {
+        return mysql_list_tables($GLOBALS['db']['db'], $this->_rLink);
+    }
+
+    function getFields($sTable)
+    {
+        $rFields = mysql_list_fields($this->_sDbname, $sTable, $this->_rLink);
+        $iFields = mysql_num_fields($rFields);
+
+        $aResult = array('original' => array(), 'uppercase' => array());
+        for($i = 0; $i < $iFields; $i++) {
+            $sName = mysql_field_name($rFields, $i);
+            $aResult['original'][] = $sName;
+            $aResult['uppercase'][] = strtoupper($sName);
+        }
+
+        return $aResult;
+    }
+
+    function isFieldExists($sTable, $sFieldName)
+    {
+        $aFields = $this->getFields($sTable);
+        return in_array(strtoupper($sFieldName), $aFields['uppercase']);
+    }
+
+    function getEncoding()
+    {
+        return  mysql_client_encoding($this->_rLink) or $this->error('Database get encoding error');
+    }
+
+    function genMySQLErr($sOutput, $query = '')
+    {
+        $sParamsOutput = false;
+        $sFoundError = '';
+
+        $aBackTrace = debug_backtrace();
+        unset( $aBackTrace[0] );
+
+        if( $query ) {
+            //try help to find error
+
+            $aFoundError = array();
+
+            foreach( $aBackTrace as $aCall ) {
+
+                // truncating global settings since it repeated many times and output it separately
+                if (isset($aCall['object']) && is_a($aCall['object'], 'BxDolDb')) {
+                    if (false === $sParamsOutput)
+                        $sParamsOutput = var_export($aCall['object']->_aParams, true);
+                    $aCall['object']->_aParams = '[truncated]';
+                }
+
+                if (isset($aCall['args']) && is_array($aCall['args'])) {
+                    foreach( $aCall['args'] as $argNum => $argVal ) {
+                        if( is_string($argVal) and strcmp( $argVal, $query ) == 0 ) {
+                            $aFoundError['file']     = isset($aCall['file']) ? $aCall['file'] : (isset($aCall['class']) ? 'class: ' . $aCall['class'] : 'undefined');
+                            $aFoundError['line']     = isset($aCall['line']) ? $aCall['line'] : 'undefined';
+                            $aFoundError['function'] = $aCall['function'];
+                            $aFoundError['arg']      = $argNum;
+                        }
+                    }
+                }
+            }
+
+            if( $aFoundError ) {
+                $sFoundError = <<<EOJ
+Found error in the file '<b>{$aFoundError['file']}</b>' at line <b>{$aFoundError['line']}</b>.<br />
+Called '<b>{$aFoundError['function']}</b>' function.<br /><br />
+EOJ;
+            }
+        }
+
+        if (defined('BX_DB_FULL_VISUAL_PROCESSING') && BX_DB_FULL_VISUAL_PROCESSING) {
+
+            ob_start();
+
+            ?>
+                <div style="border:2px solid red;padding:4px;width:600px;margin:0px auto;">
+                    <div style="text-align:center;background-color:red;color:white;font-weight:bold;">Error</div>
+                    <div style="text-align:center;"><?php echo $sOutput; ?></div>
+            <?php
+            if (defined('BX_DB_FULL_DEBUG_MODE') && BX_DB_FULL_DEBUG_MODE)
+                echo $this->verboseErrorOutput ($query, $sFoundError, $aBackTrace, $sParamsOutput);
+            ?>
+                </div>
+            <?php
+
+            $sOutput = ob_get_clean();
+        } 
+
+        if (defined('BX_DB_DO_EMAIL_ERROR_REPORT') && BX_DB_DO_EMAIL_ERROR_REPORT) {
+            $sSiteTitle = $this->getParam('site_title');
+            $sMailBody = "Database error in " . $sSiteTitle . "<br /><br /> \n";
+
+            $sMailBody .= $this->verboseErrorOutput ($query, $sFoundError, $aBackTrace, $sParamsOutput);
+            $sMailBody .= "<hr />Auto-report system";
+
+            sendMail($this->getParam('site_email'), "Database error in " . $sSiteTitle, $sMailBody, 0, array(), BX_EMAIL_SYSTEM, 'html', true);
+        }
+
+        bx_show_service_unavailable_error_and_exit($sOutput);
+    }
+
+    protected function verboseErrorOutput ($query, $sFoundError, &$aBackTrace, $sParamsOutput)
+    {
+        ob_start();
+
+        if (!empty($query))
+            echo "<div><b>Query:</b><br />{$query}</div>";
+
+        if ($this->_rLink)
+            echo '<div><b>Mysql error:</b><br />' . $this->getErrorMessage() . '</div>';
+
+        echo '<div style="overflow:scroll;height:300px;border:1px solid gray;">';
+            echo $sFoundError;
+            echo "<b>Debug backtrace:</b><br />";
+
+            $sBackTrace = print_r($aBackTrace, true);
+            $sBackTrace = str_replace('[password] => ' . BX_DATABASE_PASS, '[password] => *****', $sBackTrace);
+            $sBackTrace = str_replace('[user] => ' . BX_DATABASE_USER, '[user] => *****', $sBackTrace);
+
+            echo '<pre>' . htmlspecialchars_adv($sBackTrace) . '</pre>';
+
+            if ($sParamsOutput) {
+                echo '<hr />';
+                echo "<b>Settings:</b><br />";
+                echo '<pre>' . htmlspecialchars_adv($sParamsOutput) . '</pre>';
+            }
+
+            echo "<b>Called script:</b> " . $_SERVER['PHP_SELF'] . "<br />";
+            echo "<b>Request parameters:</b><br />";
+            echoDbg( $_REQUEST );
+        echo '</div>';
+
+        return ob_get_clean();
+    }
+
+    function setErrorChecking ($b)
+    {
+        $this->_bErrorChecking = $b;
+    }
+
+    function getDbCacheObject ()
+    {
+        if ($this->_oDbCacheObject != null) {
+            return $this->_oDbCacheObject;
+        } else {
+            $sEngine = $this->getParam('sys_db_cache_engine');
+            $this->_oDbCacheObject = bx_instance ('BxDolCache'.$sEngine);
+            if (!$this->_oDbCacheObject->isAvailable())
+                $this->_oDbCacheObject = bx_instance ('BxDolCacheFile');
+            return $this->_oDbCacheObject;
+        }
+    }
+
+    function genDbCacheKey ($sName)
+    {
+        return 'db_' . $sName . '_' . bx_site_hash() . '.php';
+    }
+
+    function fromCache ($sName, $sFunc)
+    {
+        $aArgs = func_get_args();
+        array_shift ($aArgs); // shift $sName
+        array_shift ($aArgs); // shift $sFunc
+
+        if (!$this->getParam('sys_db_cache_enable'))
+            return call_user_func_array (array ($this, $sFunc), $aArgs); // pass other function parameters as database function parameters
+
+        $oCache = $this->getDbCacheObject ();
+
+        $sKey = $this->genDbCacheKey($sName);
+
+        $mixedRet = $oCache->getData($sKey);
+
+        if ($mixedRet !== null) {
+
+            return $mixedRet;
+
+        } else {
+
+            $mixedRet = call_user_func_array (array ($this, $sFunc), $aArgs); // pass other function parameters as database function parameters
+
+            $oCache->setData($sKey, $mixedRet);
+        }
+
+        return $mixedRet;
+    }
+
+    function cleanCache ($sName)
+    {
+        if (!$this->getParam('sys_db_cache_enable'))
+            return true;
+
+        $oCache = $this->getDbCacheObject ();
+
+        $sKey = $this->genDbCacheKey($sName);
+
+        return $oCache->delData($sKey);
+    }
+
+    function & fromMemory ($sName, $sFunc)
+    {
+        if (array_key_exists($sName, $GLOBALS['gl_db_cache'])) {
+            return $GLOBALS['gl_db_cache'][$sName];
+
+        } else {
+            $aArgs = func_get_args();
+            array_shift ($aArgs); // shift $sName
+            array_shift ($aArgs); // shift $sFunc
+            $GLOBALS['gl_db_cache'][$sName] = call_user_func_array (array ($this, $sFunc), $aArgs); // pass other function parameters as database function parameters
+            return $GLOBALS['gl_db_cache'][$sName];
+
+        }
+    }
+
+    function cleanMemory ($sName)
+    {
+        if (isset($GLOBALS['gl_db_cache'][$sName])) {
+            unset($GLOBALS['gl_db_cache'][$sName]);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * It escapes string to pass to mysql query.
+     * Try to use "prepare" function always (@see BxDolDb::prepare), use "escape" only if "prepare" function is not possible at all.
+     * Also consider using "implode_escape" function (@see BxDolDb::implode_escape).
+     *
+     * @param  string  $s string to escape
+     * @return escaped string whcich is ready to pass to SQL query.
+     */
+    function escape ($s)
+    {
+        return mysql_real_escape_string($s, $this->_rLink);
+    }
+
+    /**
+     * This function is usefull when you need to form array of parameters to pass to IN(...) SQL construction.
+     * Example:
+     * @code
+     * $a = array(2, 4.5, 'apple', 'car');
+     * $s = "SELECT * FROM `t` WHERE `a` IN (" . $oDb->implode_escape($a) . ")";
+     * echo $s; // outputs: SELECT * FROM `t` WHERE `a` IN (2, 4.5, 'apple', 'car')
+     * @endcode
+     *
+     * @param $mixed array or parameters or just one paramter
+     * @return string which is ready to pass to IN(...) SQL construction
+     */
+    function implode_escape ($mixed)
+    {
+        if (is_array($mixed)) {
+            $s = '';
+            foreach ($mixed as $v)
+                $s .= (is_numeric($v) ? $v : "'" . mysql_real_escape_string($v, $this->_rLink) . "'") . ',';
+            if ($s)
+                return substr($s, 0, -1);
+            else
+                return 'NULL';
+        }
+        return is_numeric($mixed) ? $mixed : ($mixed ? "'" . mysql_real_escape_string($mixed, $this->_rLink) . "'" : 'NULL');
+    }
+
+    /**
+     * @deprecated
+     */
+    function unescape ($mixed)
+    {
+        if (is_array($mixed)) {
+            foreach ($mixed as $k => $v)
+                $mixed[$k] = $this->getOne("SELECT '$v'");
+            return $mixed;
+        } else {
+            return $this->getOne("SELECT '$mixed'");
+        }
+    }
+
+    /**
+     * Prepare SQL query before execution if some arguments are need to be passed to it.
+     * All parameters marked with question (?) symbol in SQL query are replaced with parameters passed after SQL query parameter.
+     * Parameters are properly excaped and surrounded by qutes if needed.
+     * Example:
+     * @code
+     * $sSql = $oDb->prepare("SELECT `a`, `b` from `t` WHERE `c` = ? and `d` = ?", 12, 'aa');
+     * echo $sSql;// outputs: SELECT `a`, `b` from `t` WHERE `c` = 12 and `d` = 'aa'
+     * $a = $oDb->getAll($sSql);
+     * @endcode
+     *
+     * @param  string $sQuery SQL query, parameters for replacing are marked with ? symbol
+     * @param  mixed  $mixed  any number if parameters to replace, number of parameters whould match number of ? symbols in SQL query
+     * @return string with SQL query ready for execution
+     */
+    function prepare ($sQuery)
+    {
+        $aArgs = func_get_args();
+        $sQuery = array_shift($aArgs);
+        $iPos = 0;
+        foreach ($aArgs as $mixedArg) {
+            if (is_null($mixedArg))
+                $s = 'NULL';
+            elseif (is_numeric($mixedArg))
+                $s = $mixedArg;
+            else
+                $s = "'" . mysql_real_escape_string($mixedArg) . "'";
+
+            $i = bx_mb_strpos($sQuery, '?', $iPos);
+            $sQuery = bx_mb_substr_replace($sQuery, $s, $i, 1);
+            $iPos = $i + get_mb_len($s);
+        }
+        return $sQuery;
+    }
+
+    /**
+     * Convert array of key => values to SQL query.
+     * Array keys are field names and array values are field values.
+     * @param $a array
+     * @param $sDiv fields separator, by default it is ',', another useful value is ' AND '
+     * @return part of SQL query string
+     */
+    function arrayToSQL ($a, $sDiv = ',')
+    {
+        $s = '';
+        foreach ($a as $k => $v)
+            $s .= "`{$k}` = '" . $this->escape($v) . "'" . $sDiv;
+        return trim($s, $sDiv);
+    }
+
+    function log ($s)
+    {
+        return file_put_contents(BX_DIRECTORY_PATH_LOGS . 'db.err.log', date('Y-m-d H:i:s') . "\t" . $s . "\n", FILE_APPEND);
+    }
+
+    function executeSQL($sPath, $aReplace = array (), $isBreakOnError = true)
+    {
+        if(!file_exists($sPath) || !($rHandler = fopen($sPath, "r")))
+            return array(array ('query' => "fopen($sPath, 'r')", 'error' => 'file not found or permission denied'));
+
+        $sQuery = "";
+        $sDelimiter = ';';
+        $aResult = array();
+        while(!feof($rHandler)) {
+            $sStr = trim(fgets($rHandler));
+
+            if(empty($sStr) || $sStr[0] == "" || $sStr[0] == "#" || ($sStr[0] == "-" && $sStr[1] == "-"))
+                continue;
+
+            //--- Change delimiter ---//
+            if(strpos($sStr, "DELIMITER //") !== false || strpos($sStr, "DELIMITER ;") !== false) {
+                $sDelimiter = trim(str_replace('DELIMITER', '', $sStr));
+                continue;
+            }
+
+            $sQuery .= $sStr;
+
+            //--- Check for multiline query ---//
+            if(substr($sStr, -strlen($sDelimiter)) != $sDelimiter)
+                continue;
+
+            //--- Execute query ---//
+            if ($aReplace)
+                $sQuery = str_replace($aReplace['from'], $aReplace['to'], $sQuery);
+            if($sDelimiter != ';')
+                $sQuery = str_replace($sDelimiter, "", $sQuery);
+            $rResult = $this->res(trim($sQuery), false);
+            if(!$rResult) {
+                $aResult[] = array('query' => $sQuery, 'error' => $this->getErrorMessage());
+                if ($isBreakOnError)
+                    break;
+            }
+
+            $sQuery = "";
+        }
+        fclose($rHandler);
+
+        return empty($aResult) ? true : $aResult;
+    }
+}
+
+function getParam($sParamName, $bUseCache = true)
+{
+    return BxDolDb::getInstance()->getParam($sParamName, $bUseCache);
+}
+
+function setParam($sParamName, $sParamVal)
+{
+    return BxDolDb::getInstance()->setParam($sParamName, $sParamVal);
+}
+
+/** @} */

+ 1533 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolForm.php

@@ -0,0 +1,1533 @@
+<?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    TridentCore Trident Core
+ * @{
+ */
+
+define('BX_DOL_FORM_METHOD_GET', 'get');
+define('BX_DOL_FORM_METHOD_POST', 'post');
+define('BX_DOL_FORM_METHOD_SPECIFIC', 'specific');
+
+define('BX_DATA_LISTS_KEY_PREFIX', '#!');
+define('BX_DATA_VALUES_DEFAULT', 'LKey'); ///< Use default values for data items, @see BxDolForm::getDataItems
+define('BX_DATA_VALUES_ADDITIONAL', 'LKey2'); ///< Use additional values for data items, @see BxDolForm::getDataItems
+
+/**
+ * @page objects
+ * @section forms Forms
+ * @ref BxDolForm
+ */
+
+/**
+ * Forms allows to display forms from data stored in DB tables, before it was possible to display forms from PHP arrays only.
+ *
+ *
+ * The Form Objects have the following main advantages:
+ * - Minimal coding is needed to create different forms
+ * - Easy forms alterations
+ * - Multiple representations of the same form
+ * - Automated data inserting into database
+ * - Automated data updating
+ * - Automated data checking
+ * - Automatic security checking
+ * - Automatic spam filter
+ *
+ *
+ * Forms API uses several definitions:
+ * - Form or Form Object - record in sys_objects_form table, or instance of Form class.
+ * - Form Display - set of some form inputs in particular order, displayed for some purpose; one form can have several displays, for example add and edit displays.
+ * - Form Input or Form Field - form input field, like textarea, checkbox or set of radio buttons.
+ *
+ *
+ * @section form_creating_object Creating the Form Object
+ *
+ * 1. Create Form Object, add record to sys_objects_form table:
+ * - object - name of the Form Object, in the format: vendor prefix, underscore, module prefix, underscore, internal identifier or nothing; for example: bx_group - for group data processing, like group adding or editing.
+ * - title - Form Object title to display in studio forms builder.
+ * - action - url to submit form to, if url is not full and not empty, then site url is added automatically.
+ * - form_attrs - serialized array of additional form attributes.
+ * - submit_name - name of form field with submit button, it is used to determine if form is submitted.
+ * - table - DB table name (for automatic saving/updating).
+ * - key - DB table field with unique ID (for automatic updating).
+ * - uri - DB table field with URI (for automatic URI generation, aling with uri_title).
+ * - uri_title - DB table field with data title (for automatic URI generation, aling with uri).
+ * - params - serialized array of additional form parameters:
+ * - checker_helper - name of custom BxDolFormCheckerHelper class.
+ * - csrf - array of Cross-site request forgery attack prevention parameters, for now only one boolean parameter is supported - disabled, so it can be disabled for particular form.
+ * - active - 1 or 0, if form is inactive then it can not be used anywhere.
+ * - override_class_name - user defined class name which is derived from BxTemplFormView.
+ * - override_class_file - the location of the user defined class, leave it empty if class is located in system folders.
+ *
+ *
+ * 2. Create Form Displays, add records to sys_form_displays table:
+ * - display_name - name of the Form Display, in the format:
+ *                  form object name, underscore, internal identifier or nothing;
+ *                  for example: bx_group_add - for displaying group adding form, bx_group_edit - for displaying group editing form
+ * - module - module name this display belongs to, it must be associated with name field in sys_modules table.
+ * - object - form object name from sys_objects_form table this Form Display belongs to.
+ * - title - Form Display title to display in studio forms builder.
+ * - view_mode - display form as read-only.
+ *
+ *
+ * 3. Create Form Fields, add records to sys_form_inputs table:
+ * - object - form object name from sys_objects_form table this Form Field belongs to.
+ * - module - module name this field belongs to.
+ * - name - unique Form Field name in particular From Object.
+ * - value - default value, or empty if there is no default value.
+ * - values - possible values, for certain form field types.
+ * - checked - 0 or 1, it determines if form field is checked, for certain form field types.
+ * - type - form field type, for now the following types are supported:
+ *      - text - text input field.
+ *      - password - password input field.
+ *      - textarea - multiline input field.
+ *      - number - number input field.
+ *      - select - select one from all available values.
+ *      - select_multiple - select one, multiple or all items from all available values.
+ *      - switcher - on/off switcher.
+ *      - checkbox - one checkbox.
+ *      - checkbox_set - set of checkboxes.
+ *      - radio_set - set of radio buttons.
+ *      - slider - select some numeric value within the range using slider control.
+ *      - doublerange - select range values within the range using slider control.
+ *      - datepicker - date selection control.
+ *      - datetime - date/time selection control.
+ *      - captcha - image captcha.
+ *      - hidden - hidden input field.
+ *      - file - file upload input.
+ *      - button - button control.
+ *      - image - form image button.
+ *      - reset - form reset button.
+ *      - submit - form submit button.
+ *      - value - just displaying value without any crontol.
+ *      - block_header - start group of field.
+ *      - custom - custom control.
+ *      - input_set - set of other form controls.
+ *      detailed description of every type will be described below.
+ * - caption - input title.
+ * - info - some info to help user to input data into the field, it's better to specify format and limits here.
+ * - required - indicate that the input is required by displaying asterisk near the field,
+ *              please note that this field don't perform any checking automatically,
+ *              since you mark field as required you need to specify checked function which will check entered value.
+ * - collapsed - display section as collapsed by default, for block_header field type only.
+ * - html - display visual editor of certain type, for textarea field type only.
+ *      - 0 - no visual editor, leave textarea field as it is.
+ *      - 1 - standard(default) visual editor, see @BxDolEditor.
+ *      - 2 - full visual editor, see @BxDolEditor.
+ *      - 3 - mini visual editor, see @BxDolEditor.
+ * - attrs - serialized array of additional input attributes.
+ * - attrs_tr - serialized array of additional attributes for the whole input row.
+ * - attrs_wrapper - serialized array of additional attributes for input wrapper.
+ * - checker_func - checked function, if you marked field as required in textarea field you need to point one of the following checked functions:
+ *      - Length - check value length, additional params must contain min and/or max values for checking.
+ *      - Date - check if date is entered correctly.
+ *      - DateTime - check if datetime is entered correctly.
+ *      - Preg - check value with provided regular expression in checker_params field.
+ *      - Avail - just check if value isn't 0 or empty string, additional function parameters are not used.
+ *      - Email - check if value is written in valid email format.
+ *      - Captcha - check if captcha is entered correctly, for captcha field type only.
+ *      You can inherit BxDolFormCheckerHelper class and add own checker functions, you will need to point your custom class in Form Object params array.
+ * - checker_params - serialized array of checker_func parameters.
+ * - checker_error - error message to show in case of checking function returns false.
+ * - db_pass - function to pass value through before saving to database and after restoring from database (for example when date need to be converted from/to timestamp value),
+ *              available values are the following:
+ *      - Int - convert value to integer.
+ *      - Float - convert value to floating point number.
+ *      - Date - convert value to timestamp value before saving to database, and convert from timespamp value after restoring from database.
+ *      - DateTime - convert value to timestamp value before saving to database, and convert from timespamp value after restoring from database.
+ *      - Xss - it warns you that this text can contain XSS vulnerabilities and you need to be extra careful with this, and always use Forms engine to output string to the browser or use bx_process_output if going to output text manually.
+ *      - XssHtml - this text cam have HTML tags, so perform XSS vulnerabilies cleaning before saving to database.
+ *      - All - do not perform any conversion and pass text as it is, be careful with this, use it only when no other function can be used, and make all necessary security checking by yourself.
+ *      - Preg - perform regular expression on the text before saving data to database, regular expression can be provided in db_params field.
+ *      - Boolean - this is used for checkboxes with 'on' value which need to be converted into boolean value.
+ *      - Set - convert set of values into bit integer before saving to database, and restore bit integer into array of values upon restoration from database, it can be used for select_multiple and checkbox_set field types.
+ *      Please note that values for this field must be 1,2,4,8,... (values of power of 2); the max number of values are 31 for 32bit hardware and 63 for 64bit hardware.
+ *      You can inherit BxDolFormCheckerHelper class and add own pass functions, you will need to point your custom class in Form Object params array.
+ * - db_params - serialized array of db_pass parameters.
+ * - editable - allow to edit this field from admin forms builder.
+ * - deletable - allow to delete this field from admin forms builder.
+ *
+ *
+ * 4. Add Form Fields and Form Displays associations, add records to sys_form_display_inputs table:
+ * - display_name - name of the Form Display from sys_form_displays table.
+ * - input_name - name of the Form Field from sys_form_inputs table.
+ * - visible_for_levels - bit field with set of member level ids. To use member level id in bit field the level id minus 1 is used as power of 2, for example:
+ *      - user level id = 1 -> 2^(1-1) = 1
+ *      - user level id = 2 -> 2^(2-1) = 2
+ *      - user level id = 3 -> 2^(3-1) = 4
+ *      - user level id = 4 -> 2^(4-1) = 8
+ * - active - 1 - form field displayed on form, or 0 - isn't displayed.
+ * - order - fields are displayed in this order.
+ *
+ *
+ * @section form_field_types Form Field Types
+ *
+ * Detailed description of Form Field Types.
+ *
+ * Almost all fields have the following common parameters:
+ * - object
+ * - name
+ * - type
+ * - caption
+ * - info
+ * - required
+ * - attrs
+ * - attrs_tr
+ * - attrs_wrapper
+ *
+ * We will not describe above list of parameters in every type, since they work the same way for all types.
+ *
+ * The list below are field types with their unique parameters, which are designed especially for this field, or some parameters which work differently for the specified field type.
+ *
+ * text - text input field. It is displayed as regular single line text input.
+ *      Parameters:
+ *      - value - default value, or empty if there is no default value.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail, Email
+ *          Make no sense to use it here: Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int, Float, Xss, All, Preg
+ *          Make no sense to use it here: Date, DateTime, XssHtml, Boolean, Set
+ *
+ * password - password input field. It is displayed as HTML input element with invisible input.
+ *      Parameters:
+ *      - value - default value, or empty if there is no default value.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail.
+ *          Make no sense to use it here: Date, DateTime, Captcha, Email.
+ *      - db_pass
+ *          Can be used here: Xss, All.
+ *          Make no sense to use it here: Int, Float, Date, DateTime, XssHtml, Boolean, Set, Preg.
+ *
+ * textarea - multiline input field. It can be displayed as regular textarea field or as visual HTML editor.
+ *      Parameters:
+ *      - value - default value, or empty if there is no default value.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - use visual editor or not.
+ *          - 0 - no visual editor, leave textarea field as it is.
+ *          - 1 - standard(default) visual editor, see @BxDolEditor.
+ *          - 2 - full visual editor, see @BxDolEditor.
+ *          - 3 - mini visual editor, see @BxDolEditor.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail
+ *          Make no sense to use it here: Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int, Float, Xss, XssHtml, All, Preg
+ *          Make no sense to use it here: Date, DateTime, Boolean, Set
+ *
+ * number - number input field. It is displayed as HTL text input, but with limited width. Also some browsers can add additional controls to this field.
+ *      Parameters:
+ *      - value - default value, or empty if there is no default value.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail
+ *          Make no sense to use it here: Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int, Float
+ *          Make no sense to use it here: Xss, XssHtml, All, Preg, Date, DateTime, Boolean, Set
+ *
+ * select - select one from all available values. It is displayed as HTML combo-box.
+ *      Parameters:
+ *      - value - default value (array index of selected item from values array), or empty - if there is no default value.
+ *      - values - serialized array of available values, or reference to predefined set of values.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail
+ *          Make no sense to use it here: Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int, Float, Xss, All, Preg
+ *          Make no sense to use it here: Date, DateTime, XssHtml, Boolean, Set
+ *
+ * select_multiple - select one, multiple or all items from all available values. It is displayed as HTML multiple selection input.
+ *      Parameters:
+ *      - value - default value (bit integer of array indexes of selected items from values array), or empty - if there is no default value.
+ *      - values - serialized array of available values, or reference to predefined set of values. Array index must be power of 2. Max number of values is 31 on 32bit hardware or 63 on 64bit hardware.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail
+ *          Make no sense to use it here: Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int
+ *          Make no sense to use it here: Float, Xss, All, Preg, Date, DateTime, XssHtml, Boolean, Set
+ *
+ * switcher - on/off switcher. It is displayed as custom HTML element with own styles, but on background it works as regular HTML checkbox element.
+ *      Parameters:
+ *      - value - the value which will be submitted if switcher is on, if switcher is off - nothing is submitted.
+ *      - values - not applicable here.
+ *      - checked - if set to 1 then switcher is on by default, 0 - it is off by default.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail
+ *          Make no sense to use it here: Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int, Float, Xss, All, Preg, Boolean
+ *          Make no sense to use it here: Date, DateTime, XssHtml, Set
+ *
+ * checkbox - one checkbox. Displayed as HTML checkbox input element.
+ *      Parameters:
+ *      - value - the value which will be submitted if checkbox is checked, if checkbox isn't checked - nothing is submitted.
+ *      - values - not applicable here.
+ *      - checked - if set to 1 then checkbox is checked by default, 0 - it is unchecked by default.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail
+ *          Make no sense to use it here: Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int, Float, Xss, All, Preg, Boolean
+ *          Make no sense to use it here: Date, DateTime, XssHtml, Set
+ *
+ * checkbox_set - set of checkboxes. It is displayed as set of checkboxes. It is displayed in one row if number of items is equal or less than 3 or every item is displayed on new line if there is more than 3 items in the set.
+ *      Parameters:
+ *      - value - default value (bit integer of array indexes of selected items from values array), or empty - if there is no default value.
+ *      - values - serialized array of available values, or reference to predefined set of values. Array index must be power of 2. Max number of values is 31 on 32bit hardware or 63 on 64bit hardware.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail
+ *          Make no sense to use it here: Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int
+ *          Make no sense to use it here: Float, Xss, All, Preg, Date, DateTime, XssHtml, Boolean, Set
+ *
+ * radio_set - set of radio buttons. It is displayed as set of radio buttons. It is displayed in one row if number of items is equal or less than 3 or every item is displayed on new line if there is more than 3 items in the set.
+ *      Parameters:
+ *      - value - default value (array index of selected radio button from values array), or empty - if there is no default value.
+ *      - values - serialized array of available values, or reference to predefined set of values.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail
+ *          Make no sense to use it here: Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int, Float, Xss, All, Preg
+ *          Make no sense to use it here: Date, DateTime, XssHtml, Boolean, Set
+ *
+ * slider - select some numeric value within the range using slider control. It is displayed as jQuery UI HTML control, but on background it works as regular HTML text input element.
+ *      Parameters:
+ *      - value - default value in the format, or empty if there is no default value.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - attrs - the following additional attributes can be used here:
+ *      - min - minimal value, default is 0.
+ *      - max - maximal value, default is 100.
+ *      - step - value can be changed by this step only, default is 1.
+ *      - checker_func
+ *          Can be used here: Length, Avail
+ *          Make no sense to use it here: Preg, Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Int, Float
+ *          Make no sense to use it here: Xss, XssHtml, All, Preg, Date, DateTime, Boolean, Set
+ *
+ * doublerange - select range values within the range using slider control.
+ *      Parameters:
+ *      - value - default value in the format [min value]-[max value], for example 16-99, or empty if there is no default value.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - attrs - the following additional attributes can be used here:
+ *          - min - minimal value, default is 0.
+ *          - max - maximal value, default is 100.
+ *          - step - value can be changed by this step only, default is 1.
+ *      - checker_func
+ *          Can be used here: Length, Avail
+ *          Make no sense to use it here: Preg, Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Xss, All, Preg
+ *          Make no sense to use it here: Int, Float, XssHtml, Date, DateTime, Boolean, Set
+ *
+ * datepicker - date selection control. It is displayed as HTML text input control, when clicking on this input then popup with date selector control is appeared.
+ *      Parameters:
+ *      - value - default value, in the format YYYY-MMM-DD, or empty if there is no default value.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Date
+ *          Make no sense to use it here: Length, Preg, Avail, Email, DateTime, Captcha
+ *      - db_pass
+ *          Can be used here: Date
+ *          Make no sense to use it here: Int, Float, Xss, All, Preg, DateTime, XssHtml, Boolean, Set
+ *
+ * datetime - date/time selection control.
+ *      Parameters:
+ *      - value - default value, in the format YYYY-MMM-DD HH:MM:SS, or empty if there is no default value.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: DateTime
+ *          Make no sense to use it here: Length, Preg, Avail, Email, Date, Captcha
+ *      - db_pass
+ *          Can be used here: DateTime
+ *          Make no sense to use it here: Int, Float, Xss, All, Preg, Date, XssHtml, Boolean, Set
+ *
+ * captcha - image captcha. Displayed as image with some text along with HTML text input for entering displayed on the image text.
+ *      Parameters:
+ *      - value - not applicable here.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Captcha
+ *          Make no sense to use it here: Length, Preg, Avail, Email, Date, DateTime
+ *      - db_pass
+ *          Can be used here: Xss, All, Preg
+ *          Make no sense to use it here: Int, Float, Date, DateTime, XssHtml, Boolean, Set
+ *
+ * hidden - hidden input field. Displayed as hidden HTML input.
+ *      Parameters:
+ *      - value - hidden input value.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          Can be used here: Length, Preg, Avail, Email, Date, DateTime
+ *          Make no sense to use it here: Captcha
+ *      - db_pass
+ *          Can be used here: Int, Float, Xss, All, Preg, Date, DateTime, XssHtml, Boolean
+ *          Make no sense to use it here: Set
+ *
+ * file - file upload input. Displayed as file upload HTML input.
+ *      Parameters:
+ *      - value - not applicable here.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func
+ *          File name is passed for checking.
+ *          Can be used here: Avail, Length, Preg
+ *          Make no sense to use it here: Email, Date, DateTime, Captcha
+ *      - db_pass
+ *          File can't be stored in the database, so this field isn't applicable here.
+ *
+ * files - files upload input. Displayed as complex uploading HTML control.
+ *          This control is too complex to describe it using default set of database fields, you need to use custom class to display this control.
+ *
+ * button - button control. Displayes as HTML button element.
+ *      Parameters:
+ *      - value - translatable button caption.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func - not applicable here.
+ *      - db_pass - not applicable here.
+ *
+ * image - form image button. It is displayed as HTML form image input element.
+ *      Parameters:
+ *      - value - not applicable here.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - attrs - the following mandatory attribute must be specified here:
+ *          - src - image URL.
+ *      - checker_func - not applicable here.
+ *      - db_pass - not applicable here.
+ *
+ * reset - form reset button. Displayed as HTML form reset input button. By clicking on this button the form is reset to its default state.
+ *      Parameters:
+ *      - value - translatable button caption.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func - not applicable here.
+ *      - db_pass - not applicable here.
+ *
+ * submit - form submit button. Displayed as HTML form submit input button. This button have the primary button style to distinguish it from other buttons. By clicking on this button the form is submitted.
+ *      Parameters:
+ *      - value - translatable button caption.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func - not applicable here.
+ *      - db_pass - not applicable here.
+ *
+ * value - just displaying value without any control.
+ *      Parameters:
+ *      - value - the value to display.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func - not applicable here.
+ *      - db_pass - not applicable here.
+ *
+ * block_header - start group of fields. Displayed as form fields divider with caption - then it can be collapsible or without caption - then it is just divider without any functionality.
+ *      Parameters:
+ *      - value - not applicable here.
+ *      - values - not applicable here.
+ *      - checked - not applicable here.
+ *      - collapsed - display group of field collapsed by default, 1 - the group is collapsed, 0 - expanded (default value).
+ *      - html - not applicable here.
+ *      - checker_func - not applicable here.
+ *      - db_pass - not applicable here.
+ *
+ * custom - custom control. You need custom class to display this control, so the exact used values are determined by particular realisation.
+ *
+ * input_set - set of other form controls.
+ *      Parameters:
+ *      - value - not applicable here.
+ *      - values - comma separated list of field names (by name field) of fields to display here.
+ *      - checked - not applicable here.
+ *      - collapsed - not applicable here.
+ *      - html - not applicable here.
+ *      - checker_func - not applicable here.
+ *      - db_pass - not applicable here.
+ *
+ *
+ * @section form_using_own_class Using own class for custom behavior
+ *
+ * It is possible to provide own class for displaying and processing the form.
+ * To do this you need to point it in override_class_name and override_class_file fields in sys_objects_form table.
+ * Your custom class must be inherited from BxTemplFormView class or its descendants.
+ *
+ *
+ * @section form_display_custom_control Displaying custom field control
+ *
+ * It is possible to leave form field with default caption and override only the form field control.
+ * To override some field you need to define the following function:
+ * @code
+ *      protected function genCustomInput[field name] ($aInput).
+ * @endcode
+ * Where [field name] is form field name.
+ * For example:
+ *
+ * @code
+ *     protected function genCustomInputCustom ($aInput) {
+ *         return
+ *         'r: <input type="text" size="2" value="'.(isset($aInput['value'][0]) ? $aInput['value'][0] : '').'" name="'.$aInput['name'].'[]" />' .
+ *         'g: <input type="text" size="2" value="'.(isset($aInput['value'][1]) ? $aInput['value'][1] : '').'" name="'.$aInput['name'].'[]" />' .
+ *         'b: <input type="text" size="2" value="'.(isset($aInput['value'][2]) ? $aInput['value'][2] : '').'" name="'.$aInput['name'].'[]" />';
+ *     }
+ * @endcode
+ *
+ *
+ * @section form_display_custom_row Displaying custom field row
+ *
+ * Form row consists of caption and control, by default it is displayed with default design and functionality.
+ * If you need to display some field with custom header and control you need to declare the following function:
+ * @code
+ *      protected function genCustomRow[field name] ($aInput).
+ * @endcode
+ * Where [field name] is form field name.
+ *
+ *
+ * @section example Example of usage
+ *
+ * Printing the form for adding new record to the database:
+ *
+ * @code
+ *      $oForm = BxDolForm::getObjectInstance('sample_form_objects', 'sample_form_objects_add'); // get form instance for specified form object and display
+ *      if (!$oForm)
+ *          die('"sample_form_objects_add" form object or "sample_form_objects_add" display is not defined');
+ *      $oForm->initChecker(); // init form checker witout any data - adding new record
+ *      if ($oForm->isSubmittedAndValid())
+ *          echo 'inserted id: ' . $oForm->insert (); // add new record to the database
+ *      echo $oForm->getCode(); // display form
+ * @endcode
+ *
+ * Printing the form for editing existing record in the database:
+ *
+ * @code
+ *      // $iEditId - ID of edited row, for example from _GET parameter
+ *      $oDb = BxDolDb::getInstance();
+ *      $sQuery = $oDb->prepare("SELECT * FROM `sample_input_types` WHERE id = ?", $iEditId);
+ *      $aRecord = $oDb->getRow();
+ *      if (!$aRecord)
+ *          die("$iEditId record wasn't found.");
+ *
+ *      $oForm = BxDolForm::getObjectInstance('sample_form_objects', 'sample_form_objects_edit'); // get form instance for specified form object and display
+ *      if (!$oForm)
+ *          die('"sample_form_objects_edit" form object or "sample_form_objects_edit" display is not defined');
+ *      $oForm->initChecker($aRecord); // init form checker with edited data
+ *      if ($oForm->isSubmittedAndValid())
+ *          echo 'updated: ' . $oForm->update ($iEditId); // update database
+ *      echo $oForm->getCode(); // display form
+ * @endcode
+ *
+ * Example of custom form class and custom checking helper class:
+ *
+ * @code
+ *      class BxSampleForm extends BxTemplFormView {
+ *
+ *          public function __construct ($aInfo, $oTemplate = false) {
+ *              parent::__construct ($aInfo, $oTemplate);
+ *          }
+ *
+ *
+ *          // display input with 'custom' name
+ *          protected function genCustomInputCustom ($aInput) {
+ *              return
+ *              'r: <input type="text" size="2" value="'.(isset($aInput['value'][0]) ? $aInput['value'][0] : '').'" name="'.$aInput['name'].'[]" />' .
+ *              'g: <input type="text" size="2" value="'.(isset($aInput['value'][1]) ? $aInput['value'][1] : '').'" name="'.$aInput['name'].'[]" />' .
+ *              'b: <input type="text" size="2" value="'.(isset($aInput['value'][2]) ? $aInput['value'][2] : '').'" name="'.$aInput['name'].'[]" />';
+ *          }
+ *
+ *      }
+ *
+ *      class BxSampleFormCheckerHelper extends BxDolFormCheckerHelper {
+ *
+ *          protected $_sDiv = ',';
+ *
+ *          // prepare RBG values to save to the DB
+ *          function passRgb ($s) {
+ *              if (!is_array($s))
+ *                  return false;
+ *
+ *              $sRet = '';
+ *              foreach ($s as $k => $v)
+ *                  $sRet .= (int)trim($v) . $this->_sDiv;
+ *
+ *              return trim($sRet, $this->_sDiv);
+ *          }
+ *
+ *          // prepare RGB values to output to the screen
+ *          function displayRgb ($s) {
+ *              return explode($this->_sDiv, $s);
+ *          }
+ *      }
+ * @endcode
+ *
+ * The recommended way is to define forms in database, if it is impossible for some reasons you can init form object from array, there is an example:
+ *
+ * @code
+ *      $aForm = array(
+ *            'form_attrs' => array(
+ *                'name'     => 'form_my',
+ *                'method'   => 'post',
+ *            ),
+ *
+ *            'params' => array (
+ *                'db' => array(
+ *                    'table' => 'table_name', // table name
+ *                    'key' => 'ID', // key field name
+ *                    'uri' => 'EntryUri', // uri field name
+ *                    'uri_title' => 'Title', // title field to generate uri from
+ *                    'submit_name' => 'submit_form', // some filed name with non empty value to determine if the for was submitted,
+ *                                                       in most cases it is submit button name
+ *                ),
+ *                'csrf' => array(
+ *                      'disable' => true, //if it wasn't set or has some other value then CSRF checking is enabled for current form, take a look at sys_security_form_token_enable to disable CSRF checking completely.
+ *                )
+ *              ),
+ *
+ *            'inputs' => array(
+ *
+ *                'Title' => array(
+ *                    'type' => 'text',
+ *                    'name' => 'Title', // the same as key and database field name
+ *                    'caption' => 'Some caption',
+ *                    'required' => true,
+ *
+ *                    // checker params
+ *                    'checker' => array (
+ *                        'func' => 'length', // see BxDolFormCheckerHelper class for all check* functions
+ *                        'params' => array(3,100),
+ *                        'error' => 'length must be from 3 to 100 characters',
+ *                    ),
+ *                    // database params
+ *                    'db' => array (
+ *                        'pass' => 'Xss',  // do XSS clear before getting this value, see BxDolFormCheckerHelper class for all pass* functions
+ *                    ),
+ *                ),
+ *
+ *                'Description' => array(
+ *                    'type' => 'textarea',
+ *                    'name' => 'Description', // the same as key and database field name
+ *                    'caption' => 'Some caption',
+ *                    'required' => true,
+ *
+ *                    // checker params
+ *                    'checker' => array (
+ *                        'func' => 'length',
+ *                        'error' => 'enter at least 3 characters',
+ *                        'params' => array(3,64000),
+ *                    ),
+ *                    'db' => array (
+ *                        'pass' => 'XssHtml',  // do XSS clear, but keep HTML before getting this value
+ *                    ),
+ *                ),
+ *            );
+ * @endcode
+ *
+ * Using of above array:
+ *
+ * @code
+ *        $oForm = new BxTemplFormView ($aForm);
+ *        $oForm->initChecker();
+ *
+ *        if ($oForm->isSubmittedAndValid ()) {
+ *
+ *            // add additional vars to database, in this case creation date field is added
+ *            $aValsAdd = array (
+ *                'Date' => time(),
+ *            );
+ *
+ *            echo 'insert last id: ' . $oForm->insert ($aValsAdd); // insert validated data to database
+ *
+ *        } else {
+ *
+ *            echo $oForm->getCode (); // show form
+ *
+ *        }
+ *
+ * @endcode
+ *
+ */
+class BxDolForm extends BxDol implements iBxDolReplaceable
+{
+    static $TYPES_CHECKBOX = array('checkbox' => 1, 'switcher' => 1);
+    static $TYPES_TEXT = array('text' => 1, 'textarea' => 1);
+    static $TYPES_FILE = array('file' => 1);
+
+    protected $_aMarkers = array ();
+
+    protected $oTemplate;
+
+    protected $_isValid = true;
+    protected $_sCheckerHelper;
+    protected $_aSpecificValues;
+
+    public $aFormAttrs; ///< form html element attributes
+    public $aInputs; ///< form inputs
+    public $aParams; ///< additional form parameters
+    public $id; ///< Form element id
+
+    function __construct ($aInfo, $oTemplate)
+    {
+        parent::__construct();
+
+        if ($oTemplate)
+            $this->oTemplate = $oTemplate;
+        else
+            $this->oTemplate = BxDolTemplate::getInstance();
+
+        $this->aFormAttrs    = isset($aInfo['form_attrs'])   ? $aInfo['form_attrs']  : array();
+        $this->aInputs       = isset($aInfo['inputs'])       ? $aInfo['inputs']      : array();
+        $this->aParams       = isset($aInfo['params'])       ? $aInfo['params']      : array();
+
+        // get form element id
+        $this->id = $this->aFormAttrs['id'] = (!empty($this->aFormAttrs['id']) ? $this->aFormAttrs['id'] : (!empty($this->aFormAttrs['name']) ? $this->aFormAttrs['name'] : 'form_advanced'));
+
+        // set default method
+        if (!isset($this->aFormAttrs['method']))
+            $this->aFormAttrs['method'] = BX_DOL_FORM_METHOD_POST;
+
+        // set default action
+        if (!isset($this->aFormAttrs['action']))
+            $this->aFormAttrs['action'] = '';
+
+        $this->_sCheckerHelper = isset($this->aParams['checker_helper']) ? $this->aParams['checker_helper'] : '';
+
+        BxDolForm::genCsrfToken();
+    }
+
+    /**
+     * Get form object instance by object name
+     * @param $sObject object name
+     * @return object instance or false on error
+     */
+    static public function getObjectInstance($sObject, $sDisplayName, $oTemplate = false)
+    {
+        if (isset($GLOBALS['bxDolClasses']['BxDolForm!'.$sObject.'!'.$sDisplayName]))
+            return $GLOBALS['bxDolClasses']['BxDolForm!'.$sObject.'!'.$sDisplayName];
+
+        $aObject = BxDolFormQuery::getFormArray($sObject, $sDisplayName);
+        if (!$aObject || !is_array($aObject))
+            return false;
+
+        $sClass = 'BxTemplFormView';
+        if (!empty($aObject['override_class_name'])) {
+            $sClass = $aObject['override_class_name'];
+            if (!empty($aObject['override_class_file']))
+                require_once(BX_DIRECTORY_PATH_ROOT . $aObject['override_class_file']);
+        }
+
+        $o = new $sClass($aObject, $oTemplate);
+
+        return ($GLOBALS['bxDolClasses']['BxDolForm!'.$sObject.'!'.$sDisplayName] = $o);
+    }
+
+    /**
+     * Get data items array
+     * @param $sKey data items identifier
+     * @param $isUseForSet convert data items keys to use in set fields, make it power of 2 (1,2,4,8,16,etc).
+     * @param $sUseValues use default(BX_DATA_VALUES_DEFAULT) or additional(BX_DATA_VALUES_ADDITIONAL) value titles, if additinal value title is missing default title is used
+     * @return data items array
+     */
+    public static function getDataItems($sKey, $isUseForSet = false, $sUseValues = BX_DATA_VALUES_DEFAULT)
+    {
+        return BxDolFormQuery::getDataItems($sKey, $isUseForSet, $sUseValues);
+    }
+
+    function initChecker ($aValues = array (), $aSpecificValues = array())
+    {
+        $this->_aSpecificValues = $aSpecificValues;
+
+        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker->setFormMethod($this->aFormAttrs['method'], $aSpecificValues);
+
+        // init form with default values
+
+        $aValuesDef = array ();
+        foreach ($this->aInputs as $k => $a) {
+            if (!isset($a['value']) || !isset($a['db']['pass']) || isset(self::$TYPES_CHECKBOX[$a['type']]))
+                continue;
+            $aValuesDef[$k] = $a['value'];
+        }
+
+        $oChecker->fillWithValues($this->aInputs, $aValuesDef);
+
+        // init form with provided values
+
+        if ($aValues)
+            $oChecker->fillWithValues($this->aInputs, $aValues);
+
+
+        if ($this->isSubmitted ()) {
+
+            // init form with submitted data, overwrite prevously declared values
+
+            $oChecker->enableFormCsrfChecking(isset($this->aParams['csrf']['disable']) && $this->aParams['csrf']['disable'] === true ? false : true);
+
+            $this->_isValid = $oChecker->check($this->aInputs);
+
+            if (!$this->_initCheckerNestedForms ())
+                $this->_isValid = false;
+
+        }
+    }
+
+    function insert ($aValsToAdd = array(), $isIgnore = false)
+    {
+        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker->setFormMethod($this->aFormAttrs['method']);
+        $sSql = $oChecker->dbInsert($this->aParams['db'], $this->aInputs, $aValsToAdd, $isIgnore);
+        if (!$sSql)
+            return false;
+        $oDb = BxDolDb::getInstance();
+        if ($oDb->res($sSql))
+            return $oDb->lastId();
+        return false;
+    }
+
+    function update ($val, $aValsToAdd = array(), &$aTrackTextFieldsChanges = null)
+    {
+        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker->setFormMethod($this->aFormAttrs['method']);
+        $sSql = $oChecker->dbUpdate($val, $this->aParams['db'], $this->aInputs, $aValsToAdd, $aTrackTextFieldsChanges);
+        if (!$sSql)
+            return false;
+        return BxDolDb::getInstance()->res($sSql);
+    }
+
+    function delete ($val)
+    {
+        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker->setFormMethod($this->aFormAttrs['method']);
+        $sSql = $oChecker->dbDelete($val, $this->aParams['db'], $this->aInputs);
+        if (!$sSql)
+            return false;
+        return BxDolDb::getInstance()->res($sSql);
+    }
+
+    function generateUri ()
+    {
+        $f = &$this->aParams['db'];
+        $sUri = $this->getCleanValue ($f['uri_title']);
+        return uriGenerate($sUri, $f['table'], $f['uri']);
+    }
+
+    function getCleanValue ($sName)
+    {
+        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker->setFormMethod($this->aFormAttrs['method']);
+        $a = isset($this->aInputs[$sName]) ? $this->aInputs[$sName] : false;
+        if ($a && isset($a['db']['pass']))
+            return $oChecker->get ($a['name'], $a['db']['pass'], isset($a['db']['params']) && $a['db']['params'] ? $a['db']['params'] : array());
+        else
+           return $oChecker->get ($sName);
+    }
+
+    function isSubmitted ()
+    {
+        return BxDolForm::getSubmittedValue($this->aParams['db']['submit_name'], $this->aFormAttrs['method'], $this->_aSpecificValues) ? true : false;
+    }
+
+    function getId ()
+    {
+        return $this->id;
+    }
+
+    function setId ($sId)
+    {
+        $this->id = $sId;
+        $this->aFormAttrs['id'] = $sId;
+    }
+
+    function setName($sName)
+    {
+        $this->aFormAttrs['name'] = $sName;
+    }
+
+    function setValid ($isValid)
+    {
+        $this->_isValid = $isValid;
+    }
+
+    function isValid ()
+    {
+        return $this->_isValid;
+    }
+
+    function isSubmittedAndValid ()
+    {
+        return ($this->isSubmitted() && $this->isValid());
+    }
+
+    public static function getSubmittedValue($sKey, $sMethod, &$aSpecificValues = false)
+    {
+        $aData = array();
+        if($sMethod == BX_DOL_FORM_METHOD_GET) {
+            $aData = &$_GET;
+        } else if($sMethod == BX_DOL_FORM_METHOD_POST) {
+            $aData = &$_POST;
+        } else if($sMethod == BX_DOL_FORM_METHOD_SPECIFIC) {
+            $aData = &$aSpecificValues;
+        }
+
+        return isset($aData[$sKey]) ? $aData[$sKey] : false;
+    }
+
+    public static function setSubmittedValue($sKey, $mixedValue, $sMethod, &$aSpecificValues = false)
+    {
+        $aData = array();
+        if($sMethod == BX_DOL_FORM_METHOD_GET) {
+            $aData = &$_GET;
+        } else if($sMethod == BX_DOL_FORM_METHOD_POST) {
+            $aData = &$_POST;
+        } else if($sMethod == BX_DOL_FORM_METHOD_SPECIFIC) {
+            $aData = &$aSpecificValues;
+        }
+
+        return $aData[$sKey] = $mixedValue;
+    }
+
+    // Static Methods related to CSRF Tocken
+    public static function genCsrfToken($bReturn = false)
+    {
+        if (getParam('sys_security_form_token_enable') != 'on')
+            return false;
+
+        $oSession = BxDolSession::getInstance();
+
+        $iCsrfTokenLifetime = (int)getParam('sys_security_form_token_lifetime');
+        if($oSession->getValue('csrf_token') === false || ($iCsrfTokenLifetime != 0 && time() - (int)$oSession->getValue('csrf_token_time') > $iCsrfTokenLifetime)) {
+            $sToken = genRndPwd(20, true);
+            $oSession->setValue('csrf_token', $sToken);
+            $oSession->setValue('csrf_token_time', time());
+        } else
+            $sToken = $oSession->getValue('csrf_token');
+
+        if($bReturn)
+            return $sToken;
+    }
+    public static function getCsrfToken()
+    {
+        $oSession = BxDolSession::getInstance();
+        return $oSession->getValue('csrf_token');
+    }
+    public static function getCsrfTokenTime()
+    {
+        $oSession = BxDolSession::getInstance();
+        return $oSession->getValue('csrf_token_time');
+    }
+
+    function _initCheckerNestedForms ()
+    {
+        $isValid = true;
+
+        // process nested forms
+        foreach ($this->aInputs as $sKey => $aInput) {
+
+            if ((isset($aInput['type']) && $aInput['type'] != 'files') || !isset($aInput['ghost_template']))
+                continue;
+
+            if (!(is_array($aInput['ghost_template']) && isset($aInput['ghost_template']['inputs'])) && !(is_object($aInput['ghost_template']) && $aInput['ghost_template'] instanceof BxDolFormNested))
+                continue;
+
+            $sName = $aInput['name'];
+            $aIds = $this->getSubmittedValue($sName, $this->aFormAttrs['method']);
+            if (!$aIds)
+                continue;
+
+            $aNestedForms = array ();
+            foreach ($aIds as $i => $iFileId) {
+
+                // create separate form instance for each file
+                $oFormNested = false;
+                if (is_object($aInput['ghost_template'])) {
+                    $oFormNested = clone($aInput['ghost_template']);
+                } else {
+                    $oFormNested = new BxDolFormNested($aInput['name'], $aInput['ghost_template'], $this->aParams['db']['submit_name'], $this->oTemplate);
+                }
+                $aNestedForms[$iFileId] = $oFormNested;
+
+                // collect nested form values
+                $aSpecificValues = array ();
+                if (isset($this->aParams['db']['submit_name']))
+                    $aSpecificValues = array ($this->aParams['db']['submit_name'] => 1);
+                foreach ($oFormNested->aInputs as $r) {
+                    $sName = str_replace('[]', '', $r['name']);
+                    $aValue = $this->getSubmittedValue($sName, $this->aFormAttrs['method']);
+                    $aSpecificValues[$sName] = $aValue[$i];
+                }
+                $oFormNested->initChecker(array(), $aSpecificValues);
+
+                // if nested form in invalid - then the whole worm is failed
+                if (!$oFormNested->isValid ())
+                    $isValid = false;
+            }
+
+            if ($aNestedForms)
+                $this->aInputs[$sKey]['ghost_template'] = $aNestedForms;
+
+        }
+
+        return $isValid;
+    }
+
+    /**
+     * Check if form field is visible.
+     * @param $aInput form field array
+     * @return boolean
+     */
+    protected function _isVisible ($aInput)
+    {
+        return BxDolAcl::getInstance()->isMemberLevelInSet($aInput['visible_for_levels']);
+    }
+
+    protected function _genMethodName ($s)
+    {
+        return bx_gen_method_name($s);
+    }
+
+    /**
+     * Add replace markers. Curently markers are replaced in action, form_attrs fields.
+     * @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;
+    }
+
+    /**
+     * Replace provided markers in form array
+     * @param $a form description array
+     * @return array where markes are replaced with real values
+     */
+    protected function _replaceMarkers ($a)
+    {
+        return bx_replace_markers($a, $this->_aMarkers);
+    }
+}
+
+class BxDolFormChecker
+{
+    protected $_oChecker;
+    protected $_sFormMethod;
+    protected $_bFormCsrfChecking;
+    protected $_aSpecificValues;
+
+    function __construct ($sHelper = '')
+    {
+        $this->_sFormMethod = BX_DOL_FORM_METHOD_GET;
+        $this->_bFormCsrfChecking = true;
+
+        $sCheckerName = !empty($sHelper) ? $sHelper : 'BxDolFormCheckerHelper';
+        $this->_oChecker = new $sCheckerName();
+    }
+
+    function setFormMethod($sMethod, $aSpecificValues = array())
+    {
+        $this->_sFormMethod = $sMethod;
+        $this->_aSpecificValues = $aSpecificValues;
+    }
+
+    function enableFormCsrfChecking($bFormCsrfChecking)
+    {
+        $this->_bFormCsrfChecking = $bFormCsrfChecking;
+    }
+
+    // check function
+    function check (&$aInputs)
+    {
+        $oChecker = $this->_oChecker;
+        $iErrors = 0;
+
+        // check CSRF token if it's needed.
+        if (getParam('sys_security_form_token_enable') == 'on' && $this->_bFormCsrfChecking === true && ($mixedCsrfTokenSys = BxDolForm::getCsrfToken()) !== false) {
+            $mixedCsrfTokenUsr = BxDolForm::getSubmittedValue('csrf_token', $this->_sFormMethod, $this->_aSpecificValues);
+            unset($aInputs['csrf_token']);
+
+            if($mixedCsrfTokenUsr === false || $mixedCsrfTokenSys != $mixedCsrfTokenUsr)
+                return false;
+        }
+
+        $sSubmitName = false;
+
+        foreach ($aInputs as $k => $a) {
+            if (empty($a['name']) || 'submit' == $a['type'] || 'reset' == $a['type'] || 'button' == $a['type'] || 'value' == $a['type']) {
+                if (isset($a['type']) && 'submit' == $a['type'])
+                    $sSubmitName = $k;
+                continue;
+            }
+
+            if ('input_set' == $a['type'])
+                foreach ($a as $r)
+                    if (isset($r['type']) && 'submit' == $r['type'])
+                        $sSubmitName = $k;
+
+            $a['name'] = str_replace('[]', '', $a['name']);
+            $val = BxDolForm::getSubmittedValue($a['name'], $this->_sFormMethod, $this->_aSpecificValues);
+            if (isset(BxDolForm::$TYPES_FILE[$a['type']]))
+                $val = isset($_FILES[$a['name']]['name']) ? $_FILES[$a['name']]['name'] : '';
+
+            if (!isset ($a['checker']))  {
+                if (isset(BxDolForm::$TYPES_CHECKBOX[$a['type']]))
+                    $aInputs[$k]['checked'] = (isset($aInputs[$k]['value']) && $aInputs[$k]['value'] == $val);
+                elseif (!isset(BxDolForm::$TYPES_FILE[$a['type']]))
+                    $aInputs[$k]['value'] = bx_process_input($val);
+                continue;
+            }
+
+            $sCheckFunction = array($oChecker, 'check' . bx_gen_method_name($a['checker']['func']));
+
+            if (is_callable($sCheckFunction))
+                $bool = call_user_func_array ($sCheckFunction, !empty($a['checker']['params']) ? array_merge(array($val), $a['checker']['params']) : array ($val));
+            else
+                $bool = true;
+
+            if (is_string($bool)) {
+                ++$iErrors;
+                $aInputs[$k]['error'] = $bool;
+            } elseif (!$bool) {
+                ++$iErrors;
+                $aInputs[$k]['error'] = $a['checker']['error'];
+            }
+
+            if (isset(BxDolForm::$TYPES_CHECKBOX[$a['type']]))
+                $aInputs[$k]['checked'] = ($aInputs[$k]['value'] == $val);
+            elseif (!isset(BxDolForm::$TYPES_FILE[$a['type']]))
+                $aInputs[$k]['value'] = bx_process_input($val);
+        }
+
+        // check for spam
+        if (!$iErrors) {
+
+            foreach ($aInputs as $k => $a) {
+
+                if ($a['type'] != 'textarea')
+                    continue;
+
+                $a['name'] = str_replace('[]', '', $a['name']);
+                $val = BxDolForm::getSubmittedValue($a['name'], $this->_sFormMethod, $this->_aSpecificValues);
+                if (!$val)
+                    continue;
+
+                if (!$oChecker->checkIsSpam($val))
+                    continue;
+
+                ++$iErrors;
+
+                $sErr = _t('_sys_spam_detected');
+                if (BxDolRequest::serviceExists('bx_contact', 'get_contact_page_url') && ($sUrl = BxDolService::call('bx_contact', 'get_contact_page_url')))
+                    $sErr = _t('_sys_spam_detected_contact', $sUrl);
+                $aInputs[$k]['error'] = $sErr;
+            }
+        }
+
+        // add error message near submit button
+        if ($iErrors && $sSubmitName)
+            $aInputs[$sSubmitName]['error'] = _t('_sys_txt_form_submission_error');
+
+        return $iErrors ? false : true;
+    }
+
+    // get clean value from GET/POST
+    function get ($sName, $sPass = 'Xss', $aParams = array())
+    {
+        if (!$sPass)
+            $sPass = 'Xss';
+        $this->_oChecker;
+        $val = BxDolForm::getSubmittedValue($sName, $this->_sFormMethod, $this->_aSpecificValues);
+        return call_user_func_array (array($this->_oChecker, 'pass'.ucfirst($sPass)), $aParams ? array_merge(array($val), $aParams) : array ($val));
+    }
+
+    // db functions
+    function serializeDbValues (&$aInputs, &$aValsToAdd, &$aTrackTextFieldsChanges = null)
+    {
+        $oDb = BxDolDb::getInstance();
+        $aValsToUpdate = array();
+        $s = '';
+
+        if (null !== $aTrackTextFieldsChanges && isset($aTrackTextFieldsChanges['data']))
+            $aTrackTextFieldsChanges['changed_fields'] = array();
+
+        // get values from form description array
+        foreach ($aInputs as $k => $a) {
+            if (!isset ($a['db'])) continue;
+            $valClean = $this->get ($a['name'], $a['db']['pass'], !empty($a['db']['params']) ? $a['db']['params'] : array());
+            $aValsToUpdate[$a['name']] = $valClean;
+            $aInputs[$k]['db']['value'] = $valClean;
+
+            if (null !== $aTrackTextFieldsChanges && isset(BxDolForm::$TYPES_TEXT[$aInputs[$k]['type']]) && isset($aTrackTextFieldsChanges['data'][$a['name']]) && $aTrackTextFieldsChanges['data'][$a['name']] != $valClean)
+                $aTrackTextFieldsChanges['changed_fields'][] = $a['name'];
+        }
+
+        // get values which are provided manually
+        foreach ($aValsToAdd as $k => $val) {
+            $aValsToUpdate[$k] = $val;
+        }
+
+        // build SQL query part
+        foreach ($aValsToUpdate as $k => $val)
+            $s .= $oDb->prepare("`{$k}` = ?,", $val);
+        return $s ? substr ($s, 0, -1) : '';
+    }
+
+    function dbInsert (&$aDb, &$aInputs, $aValsToAdd = array(), $isIgnore = false)
+    {
+        if (!$aDb['table'])
+            return '';
+
+        $sFields = $this->serializeDbValues ($aInputs, $aValsToAdd);
+        if (!$sFields)
+            return '';
+
+        return "INSERT " . ($isIgnore ? 'IGNORE' : '') . " INTO `{$aDb['table']}` SET $sFields";
+    }
+
+    function dbUpdate ($val, &$aDb, &$aInputs, $aValsToAdd = array(), &$aTrackTextFieldsChanges = null)
+    {
+        if (!$aDb['table'] || !$aDb['key'])
+            return '';
+
+        $oDb = BxDolDb::getInstance();
+
+        if (null !== $aTrackTextFieldsChanges && !isset($aTrackTextFieldsChanges['data'])) {
+            // get row values to compare old and new values
+            $sQuery = $oDb->prepare("SELECT * FROM `{$aDb['table']}` WHERE `{$aDb['key']}` = ?", $val);
+            $aTrackTextFieldsChanges['data'] = $oDb->getRow($sQuery);
+        }
+
+        $sFields = $this->serializeDbValues ($aInputs, $aValsToAdd, $aTrackTextFieldsChanges);
+        if (!$sFields)
+            return '';
+
+        return "UPDATE `{$aDb['table']}` SET $sFields WHERE " . $oDb->prepare("`{$aDb['key']}` = ?", $val);
+    }
+
+    function dbDelete ($val, &$aDb, &$aInputs)
+    {
+        if (!$aDb['table'] || !$aDb['key'])
+            return '';
+
+        $oDb = BxDolDb::getInstance();
+
+        return "DELETE FROM `{$aDb['table']}` WHERE " . $oDb->prepare("`{$aDb['key']}` = ?", $val);
+    }
+
+    function fillWithValues (&$aInputs, &$aValues)
+    {
+        foreach ($aInputs as $k => $a) {
+            if (!isset($aValues[$k])) continue;
+
+            if (isset(BxDolForm::$TYPES_CHECKBOX[$aInputs[$k]['type']])) {
+                $aInputs[$k]['checked'] = isset($aInputs[$k]['value']) ? ($aInputs[$k]['value'] == $aValues[$k]) : false;
+            } else {
+                $sMethod = 'display' . (isset($a['db']['pass']) ? ucfirst($a['db']['pass']) : 'Undefined');
+                if (method_exists($this->_oChecker, $sMethod))
+                    $aInputs[$k]['value'] = call_user_func_array (array($this->_oChecker, $sMethod), !empty($a['db']['params']) ? array_merge(array($aValues[$k]), $a['db']['params']) : array ($aValues[$k]));
+                else
+                    $aInputs[$k]['value'] = $aValues[$k];
+            }
+        }
+    }
+}
+
+class BxDolFormCheckerHelper
+{
+    // check functions - check values for limits or patterns
+
+    static public function checkLength ($s, $iLenMin, $iLenMax)
+    {
+        if (is_array($s)) {
+            foreach ($s as $k => $v) {
+                $iLen = get_mb_len ($v);
+                if ($iLen < $iLenMin || $iLen > $iLenMax)
+                    return false;
+            }
+            return true;
+        }
+        $iLen = get_mb_len ($s);
+        return $iLen >= $iLenMin && $iLen <= $iLenMax ? true : false;
+    }
+    static public function checkDate ($s)
+    {
+        return self::checkPreg ($s, '#^\d+\-\d+\-\d+$#');
+    }
+    static public function checkDateTime ($s)
+    {
+        // remove unnecessary opera's input value;
+        $s = str_replace('T', ' ', $s);
+        $s = str_replace('Z', ':00', $s);
+
+        return self::checkPreg ($s, '#^\d+\-\d+\-\d+[\sT]{1}\d+:\d+[:\d+]$#');
+    }
+    static public function checkPreg ($s, $r)
+    {
+        if (is_array($s)) {
+            foreach ($s as $k => $v)
+                if (!preg_match($r, $v))
+                    return false;
+            return true;
+        }
+        return preg_match($r, $s) ? true : false;
+    }
+    static public function checkAvail ($s)
+    {
+        if (is_array($s)) {
+            return !self::_isEmptyArray($s);
+        }
+        return $s ? true : false;
+    }
+    static public function checkEmail($s)
+    {
+        return self::checkPreg ($s, "/(([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?/");
+    }
+    static public function checkCaptcha($s)
+    {
+        $oCaptcha = BxDolCaptcha::getObjectInstance();
+        if (!$oCaptcha)
+            return true;
+        return $oCaptcha->check ();
+    }
+    static public function checkIsSpam($val)
+    {
+        $bSpam = false;
+        bx_alert('system', 'check_spam', 0, getLoggedId(), array('is_spam' => &$bSpam, 'content' => $val, 'where' => 'form'));
+        return $bSpam;
+    }
+
+    // pass functions, prepare values to insert to database
+    static public function passInt ($s)
+    {
+        if (is_array($s)) {
+            $a = array ();
+            foreach ($s as $k => $v) {
+                $a[$k] = (int)trim($v);
+            }
+            return $a;
+        }
+        return (int)$s;
+    }
+    static public function passFloat ($s)
+    {
+        if (is_array($s)) {
+            $a = array ();
+            foreach ($s as $k => $v) {
+                $a[$k] = (float)$v;
+            }
+            return $a;
+        }
+        return (float)$s;
+    }
+    static public function passDate ($s)
+    {
+        if (is_array($s)) {
+            $a = array ();
+            foreach ($s as $k => $v) {
+                $a[$k] = self::_passDate ($v);
+            }
+            return $a;
+        }
+        return self::_passDate ($s);
+    }
+    static public function _passDate ($s)
+    {
+        $iRet = bx_process_input ($s, BX_DATA_DATE_TS);
+        if (false === $iRet)
+            return 0;
+        return $iRet;
+    }
+    static public function passDateTime ($s)
+    {
+        if (is_array($s)) {
+            $a = array ();
+            foreach ($s as $k => $v) {
+                $a[$k] = self::_passDateTime ($v);
+            }
+            return $a;
+        }
+        return self::_passDateTime ($s);
+    }
+    static public function _passDateTime ($s)
+    {
+        $iRet = bx_process_input ($s, BX_DATA_DATETIME_TS);
+        if (false === $iRet)
+            return 0;
+        return $iRet;
+    }
+    static public function passXss ($s)
+    {
+        if (is_array($s)) {
+            $a = array ();
+            foreach ($s as $k => $v) {
+                $a[$k] = bx_process_input ($v, BX_DATA_TEXT); // "strip tags" option was here in 7.0
+            }
+            return $a;
+        }
+        return bx_process_input ($s, BX_DATA_TEXT); // "strip tags" option was here in 7.0
+    }
+    static public function passXssHtml ($s)
+    {
+        if (is_array($s)) {
+            $a = array ();
+            foreach ($s as $k => $v) {
+                $a[$k] = bx_process_input ($v, BX_DATA_HTML);
+            }
+            return $a;
+        }
+        return bx_process_input ($s, BX_DATA_HTML);
+    }
+
+    static public function passAll ($s)
+    {
+        if (is_array($s)) {
+            $a = array ();
+            foreach ($s as $k => $v) {
+                $a[$k] = bx_process_input ($v);
+            }
+            return $a;
+        }
+        return bx_process_input ($s);
+    }
+
+    static public function passPreg ($s, $r)
+    {
+        if (is_array($s)) {
+            $a = array ();
+            foreach ($s as $k => $v) {
+                $a[$k] = self::_passPreg ($v, $r);
+            }
+            return $a;
+        }
+        return self::_passPreg($s, $r);
+    }
+    static public function _passPreg ($s, $r)
+    {
+        if (preg_match ($r, $s, $m)) {
+            return $m[1];
+        }
+        return '';
+    }
+    static public function passBoolean ($s)
+    {
+        if (is_array($s)) {
+            $a = array ();
+            foreach ($s as $k => $v) {
+                $a[$k] = $v == 'on' ? true : false;
+            }
+            return $a;
+        }
+        return $s == 'on' ? true : false;
+    }
+
+    static public function passSet ($s)
+    {
+        if (is_array($s)) {
+            $i = 0;
+            foreach ($s as $v)
+                $i |= pow (2, $v - 1);
+            return $i;
+        }
+        return (int)$s;
+    }
+
+    // display functions, prepare values to output to the screen
+
+    static public function displayDate ($i)
+    {
+        return bx_process_output ($i, BX_DATA_DATE_TS);
+    }
+    static public function displayDateTime ($i)
+    {
+        return bx_process_output ($i, BX_DATA_DATETIME_TS);
+    }
+    static public function displaySet ($i)
+    {
+        $bit = 1;
+        $a = array();
+        while ($bit < BX_DOL_INT_MAX && $bit > 0) {
+            if ($bit & $i)
+                $a[] = (int)log($bit, 2) + 1;
+            $bit <<= 1;
+        }
+        return $a;
+    }
+
+    // for internal usage only
+
+    static public function _isEmptyArray ($a)
+    {
+        if (!is_array($a))
+            return true;
+        if (empty($a))
+            return true;
+        foreach ($a as $k => $v)
+            if ($v)
+                return false;
+        return true;
+    }
+}
+
+/** @} */

+ 236 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolLiveUpdates.php

@@ -0,0 +1,236 @@
+<?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    TridentCore Trident Core
+ * @{
+ */
+
+/**
+ * @page objects
+ * @section live updates Live Updates
+ * @ref BxDolLiveUpdates
+ */
+
+class BxDolLiveUpdates extends BxDol
+{
+	protected $_oQuery;
+    protected $_aSystems;
+    protected $_aSystemsActive;
+
+    protected $_iInterval;
+
+	protected $_iCacheTTL;  
+	protected $_sCacheKey;
+
+    protected $_sJsClass;
+    protected $_sJsObject;
+
+    public function __construct()
+    {
+        parent::__construct();
+
+        $this->_oQuery = new BxDolLiveUpdatesQuery();
+
+        $this->_iInterval = (int)$this->_oQuery->getParam('sys_live_updates_interval');
+
+        $this->_iCacheTTL = 86400;
+        $this->_sCacheKey = 'sys_live_updates_' . bx_get_logged_profile_id();
+
+        $this->_sJsClass = 'BxDolLiveUpdates';
+    	$this->_sJsObject = 'oLiveUpdates';
+
+    	$this->_aSystemsActive = array();
+    	$this->_aSystems = $this->_getCachedSystems();
+    }
+
+    /**
+     * get live updates instanse
+     * @return ready to use class instance
+     */
+    public static function getInstance()
+    {
+		if(!isset($GLOBALS['bxDolClasses'][__CLASS__]))
+			$GLOBALS['bxDolClasses'][__CLASS__] = new BxTemplLiveUpdates();
+
+		return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    /**
+     * 
+     * Is called via Ajax Requests only. 
+     */
+    public function perform()
+    {
+		$iIndex = (int)bx_get('index');
+
+		$mixedSystemsActive = bx_get('systems_active');
+		if($mixedSystemsActive !== false)
+			$this->_aSystemsActive = $mixedSystemsActive;
+
+		$this->_aSystems = $this->_getCachedSystems();
+    	if(empty($this->_aSystems) || !is_array($this->_aSystems))
+    		return array();
+
+		$aCached = $this->_getCachedData();
+		$aRequested = $this->_getRequestedData($iIndex, true, $aCached);
+
+		$aResult = array();
+		$bUpdateCache = false;
+		foreach($aRequested as $sName => $aData) {
+			$bCached = isset($aCached[$sName]); 
+			if($bCached && (int)$aCached[$sName] == (int)$aData['count'])
+				continue;
+
+			if($bCached) {
+				$aResultData = array('count_new' => $aData['count'], 'count_old' => $aCached[$sName]);
+				if(isset($aData['data']))
+					$aResultData = array_merge($aResultData, $aData['data']);
+	
+				$aResult[] = array(
+					'data' => $aResultData,
+					'method' => $aData['method']
+				);
+			}
+
+			$aCached[$sName] = $aData['count'];
+			$bUpdateCache = true;
+		}
+
+		if($bUpdateCache)
+			$this->_updateCached('data', $aCached);
+
+    	return $aResult;
+    }
+
+    /**
+     * 
+     * Add transient live update for current user on this current page. 
+     * @param string $sName - unique name.
+     * @param integer $iFrequency - call frequency.
+     * @param string $sServiceCall - serialized service call.
+     * @param boolean $bActive - add active/not active live update.
+     */
+    public function add($sName, $iFrequency, $sServiceCall, $bActive = true)
+    {
+    	if(!in_array($sName, $this->_aSystemsActive))
+    		$this->_aSystemsActive[] = $sName;
+
+    	if(empty($this->_aSystems[$sName])) {
+	    	$this->_aSystems[$sName] = array(
+	    		'name' => $sName,
+	    		'frequency' => $iFrequency,
+	    		'service_call' => $sServiceCall,
+	    		'active' => $bActive ? 1 : 0
+	    	);
+
+	    	$this->_updateCached('systems', $this->_aSystems);
+    	}
+
+    	$mixedResponce = $this->_getRequestedDataBySystem($this->_aSystems[$sName]);
+    	if($mixedResponce !== false) {
+    		$aCachedData = $this->_getCachedData();
+    		$aCachedData[$sName] = $mixedResponce['count'];
+    		$this->_updateCached('data', $aCachedData);
+    	}
+
+    	return true;
+    }
+
+    protected function _getCacheInfo()
+    {
+    	return array(
+    		$this->_oQuery->getDbCacheObject(),
+    		$this->_oQuery->genDbCacheKey($this->_sCacheKey),
+    		$this->_iCacheTTL
+    	);
+    }
+
+    protected function _getCached($sType)
+    {
+    	list($oCache, $sCacheKey, $iCacheTtl) = $this->_getCacheInfo();
+
+    	$aCached = $oCache->getData($sCacheKey, $iCacheTtl);
+    	if(empty($aCached[$sType])) {
+    		switch($sType) {
+    			case 'systems':
+    				$aCached[$sType] = $this->_oQuery->getSystems();
+    				break;
+
+    			case 'data':
+    				if(!isset($aCached[$sType]))
+    					$aCached[$sType] = array();
+
+    				$aRequested = $this->_getRequestedData();
+    				foreach($this->_aSystems as $sName => $aSystem)
+    					$aCached[$sType][$sName] = !empty($aRequested[$sName]['count']) ? (int)$aRequested[$sName]['count'] : 0;
+    				break;
+    		}
+
+    		if(!empty($aCached[$sType]))
+    			$oCache->setData($sCacheKey, $aCached, $iCacheTtl);
+    	}
+
+    	return $aCached[$sType];
+    }
+
+	protected function _getCachedSystems()
+    {
+    	$aSystems = $this->_getCached('systems');
+    	if(!empty($aSystems) && !empty($this->_aSystemsActive))
+    		$aSystems = array_intersect_key($aSystems, array_flip($this->_aSystemsActive));
+
+    	return $aSystems;
+    }
+
+    protected function _getCachedData()
+    {
+    	return $this->_getCached('data');
+    }
+
+	protected function _updateCached($sKey, $aData)
+    {
+    	list($oCache, $sCacheKey, $iCacheTtl) = $this->_getCacheInfo();
+
+    	$aCached = $oCache->getData($sCacheKey, $iCacheTtl);
+    	$aCached[$sKey] = $aData;
+
+    	$oCache->setData($sCacheKey, $aCached, $iCacheTtl);
+    }
+
+    protected function _getRequestedData($iIndex = 0, $bIndexCheck = false, $aCachedData = array())
+    {
+    	$aResult = array();
+
+    	foreach($this->_aSystems as $sName => $aSystem) {
+    		if(empty($aSystem) || !is_array($aSystem) || (int)$aSystem['active'] != 1)
+    			continue;
+
+			if($bIndexCheck && $iIndex % (int)$aSystem['frequency'] != 0)
+				continue;
+
+			$mixedResponce = $this->_getRequestedDataBySystem($aSystem, (!empty($aCachedData) && isset($aCachedData[$sName]) ? (int)$aCachedData[$sName] : 0));
+			if($mixedResponce === false)
+				continue;
+
+			$aResult[$sName] = $mixedResponce;
+    	}
+
+    	return $aResult;
+    }
+
+    protected function _getRequestedDataBySystem($aSystem, $iCachedData = 0)
+    {
+		if(!BxDolService::isSerializedService($aSystem['service_call']))
+			return false;
+
+		$aResponce = BxDolService::callSerialized($aSystem['service_call'], array('count' => (int)$iCachedData));
+		if(empty($aResponce) || !is_array($aResponce) || !isset($aResponce['count'], $aResponce['method']))
+			return false;
+
+		return $aResponce;
+    }
+}
+
+/** @} */

+ 427 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolProfile.php

@@ -0,0 +1,427 @@
+<?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    TridentCore Trident Core
+ * @{
+ */
+
+define('BX_PROFILE_ACTION_AUTO', 0); ///< automatic action without any checking
+define('BX_PROFILE_ACTION_MANUAL', 1); ///< manual action performed by human
+define('BX_PROFILE_ACTION_ROBOT', 2); ///< action peformed by some robot based on some conditions
+
+class BxDolProfile extends BxDol implements iBxDolProfile
+{
+    protected $_iProfileID;
+    protected $_oQuery;
+
+    /**
+     * Constructor
+     */
+    protected function __construct ($iProfileId)
+    {
+        $iProfileId = (int)$iProfileId;
+        $sClass = get_class($this) . '_' . $iProfileId;
+        if (isset($GLOBALS['bxDolClasses'][$sClass]))
+            trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
+
+        parent::__construct();
+
+        $this->_iProfileID = $iProfileId; // since constructor is protected $iProfileId is always valid
+        $this->_oQuery = BxDolProfileQuery::getInstance();
+    }
+
+    /**
+     * Prevent cloning the instance
+     */
+    public function __clone()
+    {
+        $sClass = get_class($this) . '_' . $this->_iProfileID;
+        if (isset($GLOBALS['bxDolClasses'][$sClass]))
+            trigger_error('Clone is not allowed for the class: ' . get_class($this), E_USER_ERROR);
+    }
+
+    /**
+     * Get singleton instance of Account Profile by account id
+     */
+    public static function getInstanceAccountProfile($iAccountId = false)
+    {
+        if (!$iAccountId)
+            $iAccountId = getLoggedId();
+        $oQuery = BxDolProfileQuery::getInstance();
+        $aProfile = $oQuery->getProfileByContentTypeAccount($iAccountId, 'system', $iAccountId);
+        if (!$aProfile)
+            return false;
+        return self::getInstance($aProfile['id']);
+    }
+
+    /**
+     * Get singleton instance of Profile by account id, content id and type
+     */
+    public static function getInstanceByContentTypeAccount($iContent, $sType, $iAccountId = false)
+    {
+        if (!$iAccountId)
+            $iAccountId = getLoggedId();
+        $oQuery = BxDolProfileQuery::getInstance();
+        $aProfile = $oQuery->getProfileByContentTypeAccount($iContent, $sType, $iAccountId);
+        if (!$aProfile)
+            return false;
+        return self::getInstance($aProfile['id']);
+    }
+
+    /**
+     * Get singleton instance of Profile by content id and type
+     */
+    public static function getInstanceByContentAndType($iContent, $sType)
+    {
+        $oQuery = BxDolProfileQuery::getInstance();
+        $aProfile = $oQuery->getProfileByContentAndType($iContent, $sType);
+        if (!$aProfile)
+            return false;
+        return self::getInstance($aProfile['id']);
+    }
+
+    /**
+     * Get singleton instance of Profile by profile id
+     */
+    public static function getInstance($mixedProfileId = false)
+    {
+        if (!$mixedProfileId) {
+            $oQuery = BxDolProfileQuery::getInstance();
+            $mixedProfileId = $oQuery->getCurrentProfileByAccount(getLoggedId());
+        }
+
+        $iProfileId = self::getID($mixedProfileId);
+        if (!$iProfileId)
+            return false;
+
+        $sClass = __CLASS__ . '_' . $iProfileId;
+        if (!isset($GLOBALS['bxDolClasses'][$sClass]))
+            $GLOBALS['bxDolClasses'][$sClass] = new BxDolProfile($iProfileId);
+
+        return $GLOBALS['bxDolClasses'][$sClass];
+    }
+
+    /**
+     * Get profile id
+     */
+    public function id()
+    {
+        return $this->_oQuery->getIdById($this->_iProfileID);
+    }
+
+    /**
+     * Get account id associated with the profile
+     */
+    public function getAccountId()
+    {
+        $aInfo = $this->getInfo();
+        return $aInfo['account_id'];
+    }
+
+    /**
+     * Get account object associated with the profile
+     */
+    public function getAccountObject()
+    {
+        return BxDolAccount::getInstance($this->getAccountId());
+    }
+
+    /**
+     * Get content id associated with the profile
+     */
+    public function getContentId()
+    {
+        $aInfo = $this->getInfo();
+        return $aInfo['content_id'];
+    }
+
+    /**
+     * Check if profile status is active
+     */
+    public function isActive($iProfileId = false)
+    {
+        return BX_PROFILE_STATUS_ACTIVE == $this->getStatus($iProfileId);
+    }
+
+    /**
+     * Get profile status
+     */
+    public function getStatus($iProfileId = false)
+    {
+        $aInfo = $this->_oQuery->getInfoById((int)$iProfileId ? $iProfileId : $this->_iProfileID);
+        return $aInfo['status'];
+    }
+
+    /**
+     * Get profile info
+     */
+    public function getInfo($iProfileId = 0)
+    {
+        return $this->_oQuery->getInfoById((int)$iProfileId ? $iProfileId : $this->_iProfileID);
+    }
+
+    /**
+     * Validate profile id.
+     * @param $s - profile id
+     * @return profile id or false if profile was not found
+     */
+    static public function getID($s)
+    {
+        $iId = BxDolProfileQuery::getInstance()->getIdById((int)$s);
+        return $iId ? $iId : false;
+    }
+
+    /**
+     * Get name to display in thumbnail
+     */
+    public function getDisplayName($iProfileId = 0)
+    {
+        $aInfo = $this->getInfo($iProfileId);
+        return BxDolService::call($aInfo['type'], 'profile_name', array($aInfo['content_id']));
+    }
+
+    /**
+     * Get profile url
+     */
+    public function getUrl($iProfileId = 0)
+    {
+        $aInfo = $this->getInfo($iProfileId);
+        return BxDolService::call($aInfo['type'], 'profile_url', array($aInfo['content_id']));
+    }
+
+    /**
+     * Get profile unit
+     */
+    public function getUnit($iProfileId = 0)
+    {
+        $aInfo = $this->getInfo($iProfileId);
+        return BxDolService::call($aInfo['type'], 'profile_unit', array($aInfo['content_id']));
+    }
+
+    /**
+     * Get thumbnail url
+     */
+    public function getAvatar($iProfileId = 0)
+    {
+        $aInfo = $this->getInfo($iProfileId);
+        return BxDolService::call($aInfo['type'], 'profile_avatar', array($aInfo['content_id']));
+    }
+
+    /**
+     * Get thumbnail url
+     */
+    public function getThumb($iProfileId = 0)
+    {
+        $aInfo = $this->getInfo($iProfileId);
+        return BxDolService::call($aInfo['type'], 'profile_thumb', array($aInfo['content_id']));
+    }
+
+    /**
+     * Get icon url
+     */
+    public function getIcon($iProfileId = 0)
+    {
+        $aInfo = $this->getInfo($iProfileId);
+        return BxDolService::call($aInfo['type'], 'profile_icon', array($aInfo['content_id']));
+    }
+
+    /**
+     * Get module icon
+     */
+    public function getIconModule($iProfileId = 0)
+    {
+        $aInfo = $this->getInfo($iProfileId);
+        return BxDolService::call($aInfo['type'], 'module_icon');
+    }
+
+    /**
+     * get profile edit page url
+     */
+    public function getEditUrl($iProfileId = 0)
+    {
+        $aInfo = $this->getInfo($iProfileId);
+        return BxDolService::call($aInfo['type'], 'profile_edit_url', array($aInfo['content_id']));
+    }
+
+    /**
+     * Delete profile.
+     * @param $ID - optional profile id to delete
+     * @param $bDeleteWithContent - delete profile with all its contents
+     * @param $bForceDelete - force deletetion is case of account profile deletion
+     * @return false on error, or true on success
+     */
+    function delete($ID = false, $bDeleteWithContent = false, $bForceDelete = false)
+    {
+        $ID = (int)$ID;
+        if (!$ID)
+            $ID = $this->_iProfileID;
+
+        $aProfileInfo = $this->_oQuery->getInfoById($ID);
+        if (!$aProfileInfo)
+            return false;
+
+        // delete system profiles (accounts) is not allowed, instead - delete whole account
+        if (!$bForceDelete && 'system' == $aProfileInfo['type'])
+            return false;
+
+        // delete actual profile
+        if ($sErrorMsg = BxDolService::call($aProfileInfo['type'] , 'delete_entity_service', array($aProfileInfo['content_id'], $bDeleteWithContent)))
+            return false;
+
+        // switch profile context if deleted profile is active profile context
+        $oAccount = BxDolAccount::getInstance ($aProfileInfo['account_id']);
+        $aAccountInfo = $oAccount->getInfo();
+        if (!$bForceDelete && $ID == $aAccountInfo['profile_id']) {
+            $oProfileAccount = BxDolProfile::getInstanceAccountProfile($aProfileInfo['account_id']);
+            $oAccount->updateProfileContext($oProfileAccount->id());
+        }
+
+        // create system event before deletion
+        $isStopDeletion = false;
+        bx_alert('profile', 'before_delete', $ID, 0, array('delete_with_content' => $bDeleteWithContent, 'stop_deletion' => &$isStopDeletion));
+        if ($isStopDeletion)
+            return false;
+
+        // delete associated comments
+        if($bDeleteWithContent)
+	        BxDolCmts::onAuthorDelete($ID);
+
+        // delete connections
+        $oConn = BxDolConnection::getObjectInstance('sys_profiles_friends');
+        $oConn->onDeleteInitiatorAndContent($ID);
+
+        $oConn = BxDolConnection::getObjectInstance('sys_profiles_subscriptions');
+        $oConn->onDeleteInitiatorAndContent($ID);
+
+        // delete profile's acl levels
+        BxDolAcl::getInstance()->onProfileDelete($ID);
+
+        // delete profile
+        if (!$this->_oQuery->delete($ID))
+            return false;
+
+        // create system event
+        bx_alert('profile', 'delete', $ID, 0, array('delete_with_content' => $bDeleteWithContent));
+
+        // unset class instance to prevent creating the instance again
+        $this->_iProfileID = 0;
+        $sClass = get_class($this) . '_' . $ID;
+        unset($GLOBALS['bxDolClasses'][$sClass]);
+
+        return true;
+    }
+
+    /**
+     * Insert account and content id association. Also if currect profile id is not defined - it updates current profile id in account.
+     * @param $iAccountId account id
+     * @param $iContentId content id
+     * @param $sStatus profile status
+     * @param $sType profile content type
+     * @return inserted profile's id
+     */
+    static public function add ($iAction, $iAccountId, $iContentId, $sStatus, $sType = 'system')
+    {
+        $oQuery = BxDolProfileQuery::getInstance();
+        if (!($iProfileId = $oQuery->insertProfile ($iAccountId, $iContentId, $sStatus, $sType)))
+            return false;
+        bx_alert('profile', 'add', $iProfileId, 0, array('module' => $sType, 'content' => $iContentId, 'account' => $iAccountId, 'status' => $sStatus, 'action' => $iAction));
+        return $iProfileId;
+    }
+
+    /**
+     * Change profile status to 'Active'
+     */
+    public function activate($iAction, $iProfileId = 0)
+    {
+        $sStatus = $this->getStatus($iProfileId);
+        return $this->changeStatus(BX_PROFILE_STATUS_ACTIVE, BX_PROFILE_STATUS_PENDING == $sStatus ? 'approve' : 'activate', $iAction, $iProfileId);
+    }
+
+    /**
+     * Change profile status from 'Pending' to the next level - 'Active'
+     */
+    public function approve($iAction, $iProfileId = 0)
+    {
+        return $this->changeStatus(BX_PROFILE_STATUS_ACTIVE, 'approve', $iAction, $iProfileId);
+    }
+
+    /**
+     * Change profile status to 'Pending'
+     */
+    public function disapprove($iAction, $iProfileId = 0)
+    {
+        return $this->changeStatus(BX_PROFILE_STATUS_PENDING, 'disapprove', $iAction, $iProfileId);
+    }
+
+    /**
+     * Change profile status to 'Suspended'
+     */
+    public function suspend($iAction, $iProfileId = 0)
+    {
+        return $this->changeStatus(BX_PROFILE_STATUS_SUSPENDED, 'suspend', $iAction, $iProfileId);
+    }
+
+    protected function changeStatus($sStatus, $sAlertActionName, $iAction, $iProfileId = 0)
+    {
+        if (!$iProfileId)
+            $iProfileId = $this->_iProfileID;
+
+        // get account and profile objects
+        $oProfile = BxDolProfile::getInstance($iProfileId);
+        $oAccount = $oProfile->getAccountObject();
+        if (!$oProfile || !$oAccount)
+            return false;
+
+        // change status
+        if (!$this->_oQuery->changeStatus($iProfileId, $sStatus))
+            return false;
+
+        // alert about status changing
+        bx_alert('profile', $sAlertActionName, $iProfileId, false, array('action' => $iAction));
+
+        // send email to member about status change
+        sendMailTemplate('t_ChangeStatus' . ucfirst($sStatus), $oAccount->id(), $iProfileId, array('status' => $sStatus), BX_EMAIL_SYSTEM);
+
+        return true;
+    }
+
+    /**
+     * Display informer message if it is possible to switch to this profile
+     */
+    public function checkSwitchToProfile($oTemplate = null, $iViewerAccountId = false, $iViewerProfileId = false)
+    {
+        if (false === $iViewerAccountId)
+            $iViewerAccountId = getLoggedId();
+        if (false === $iViewerProfileId)
+            $iViewerProfileId = bx_get_logged_profile_id();
+
+        if (!$iViewerAccountId || !$iViewerProfileId)
+            return;
+
+        if ($iViewerAccountId != $this->getAccountId() ||  $iViewerProfileId == $this->id())
+            return;
+
+        $oInformer = BxDolInformer::getInstance($oTemplate);
+        if ($oInformer)
+            $oInformer->add('sys-switch-profile-context', _t('_sys_txt_account_profile_context_change_suggestion', BxDolPermalinks::getInstance()->permalink('page.php?i=account-profile-switcher', array('switch_to_profile' => $this->id()))), BX_INFORMER_INFO);
+
+    }
+
+	/**
+     * Add permament messages.
+     */
+    public function addInformerPermanentMessages ($oInformer)
+    {
+    	$aInfo = $this->getInfo();
+    	$aProfiles = $this->_oQuery->getProfilesByAccount($aInfo['account_id']);
+
+        if ($aInfo['type'] == 'system' && count($aProfiles) == 1) {
+            $sUrl = BxDolPermalinks::getInstance()->permalink('page.php?i=account-profile-switcher');
+            $oInformer->add('sys-account-profile-system', _t('_sys_txt_account_profile_system', $sUrl), BX_INFORMER_ALERT);
+        }
+    }
+}
+
+/** @} */

+ 134 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolProfileUndefined.php

@@ -0,0 +1,134 @@
+<?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    TridentCore Trident Core
+ * @{
+ */
+
+/**
+ * When profile is not available (for example profile is deleted) then use this special class.
+ *
+ * @section example Example of usage
+ *
+ * @code
+ * $oProfile = BxDolProfile::getInstance($iId);
+ * if (!$oProfile)
+ *     $oProfile = BxDolProfileUndefined::getInstance();
+ * @endcode
+ */
+class BxDolProfileUndefined extends BxDol implements iBxDolSingleton, iBxDolProfile
+{
+    /**
+     * Constructor
+     */
+    protected function __construct ()
+    {
+        parent::__construct();
+    }
+
+    /**
+     * Prevent cloning the instance
+     */
+    public function __clone()
+    {
+        $sClass = get_class($this);
+        if (isset($GLOBALS['bxDolClasses'][$sClass]))
+            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()
+    {
+        $sClass = __CLASS__;
+        if(!isset($GLOBALS['bxDolClasses'][$sClass]))
+            $GLOBALS['bxDolClasses'][$sClass] = new $sClass();
+
+        return $GLOBALS['bxDolClasses'][$sClass];
+    }
+
+    /**
+     * Get profile id
+     */
+    public function id()
+    {
+        return 0;
+    }
+
+    /**
+     * Get profile display name
+     */
+    public function getDisplayName()
+    {
+        return _t('_uknown');
+    }
+
+    /**
+     * Get profile url
+     */
+    public function getUrl()
+    {
+        return 'javascript:void(0);';
+    }
+
+    /**
+     * Get profile unit
+     */
+    public function getUnit()
+    {
+        return '<div>' . $this->getDisplayName() . '</div>';
+    }
+
+    /**
+     * Get profile avatar
+     */
+    public function getAvatar()
+    {
+        return BxDolTemplate::getInstance()->getImageUrl('account-avatar.png');
+    }
+
+    /**
+     * Get profile thumb
+     */
+    public function getThumb()
+    {
+        return BxDolTemplate::getInstance()->getImageUrl('account-thumb.png');
+    }
+
+    /**
+     * Get profile icon
+     */
+    public function getIcon()
+    {
+        return BxDolTemplate::getInstance()->getImageUrl('account-icon.png');
+    }
+
+    /**
+     * Get module icon
+     */
+    public function getIconModule($iProfileId = 0)
+    {
+        return 'user';
+    }
+
+    /**
+     * Get profile edit url
+     */
+    public function getEditUrl()
+    {
+        return '';
+    }
+
+    /**
+     * Check if profile is active
+     */
+    public function isActive()
+    {
+        return true;
+    }
+}
+
+/** @} */

+ 2278 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/classes/BxDolTemplate.php

@@ -0,0 +1,2278 @@
+<?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    TridentCore Trident 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 $_bCssMinify;
+    protected $_bCssArchive;
+    protected $_sCssCachePrefix;
+
+    protected $_bJsCache;
+    protected $_bJsMinify;
+    protected $_bJsArchive;
+    protected $_sJsCachePrefix;
+
+    /**
+     * Less related fields
+     */
+    protected $_bLessEnable;
+    protected $_sLessCachePrefix;
+
+    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])) {
+            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->_bCssMinify = $this->_bCssCache && getParam('sys_template_cache_minify_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->_bJsMinify = $this->_bJsCache && getParam('sys_template_cache_minify_js_enable') == 'on';
+        $this->_bJsArchive = $this->_bJsCache && $bArchive;
+        $this->_sJsCachePrefix = $this->_sCacheFilePrefix . 'js_';
+
+        $this->_bLessEnable = true;
+        $this->_sLessCachePrefix = $this->_sCacheFilePrefix . 'less_';
+
+        $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_DIRECTORY_PATH_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-locales.min.js',
+            'functions.js',
+            'jquery.webForms.js',
+            'jquery.dolPopup.js',
+            'marka/marka.min.js',
+            'headroom.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'); // TODO: for some reason autoloader isn't working here....
+        $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;
+
+        $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;
+
+        $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_script]/modules/[vendor_name]/[module_name]/'
+     * @param string $sLocationUrl  - location's url. For modules: '[url_to_script]/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_script]/modules/[vendor_name]/[module_name]/'
+     * @param  string   $sLocationUrl  - location's url. For modules: '[url_to_script]/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_script]/modules/[vendor_name]/[module_name]/js/'
+     * @param string $sLocationUrl  - location's url. For modules: '[url_to_script]/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_script]/modules/[vendor_name]/[module_name]/'
+     * @param  string   $sLocationUrl  - location's url. For modules: '[url_to_script]/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;
+    }
+    /**
+     * Set page rss link.
+     *
+     * @param $sTitle - rss feed title
+     * @param $sUrl - rss feed URL
+     */
+    function addPageRssLink($sTitle, $sUrl)
+    {
+        if (!isset($this->aPage['rss']))
+            $this->aPage['rss'] = array('title' => $sTitle, 'url' => $sUrl);
+        else
+            $this->aPage['rss'] = false;
+    }
+    /**
+     * 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
+            $sRet .= BxTemplFunctions::getInstance()->getMetaIcons();
+
+        if (!empty($this->aPage['rss']) && !empty($this->aPage['rss']['url']))
+            $sRet .= '<link rel="alternate" type="application/rss+xml" title="' . bx_html_attribute($this->aPage['rss']['title'], BX_ESCAPE_STR_QUOTE) . '" href="' . $this->aPage['rss']['url'] . '" />';
+
+        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)
+    {
+        $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':
+                $s = $this->parsePageByName('popup_loading.html', array());
+                $sRet = BxTemplFunctions::getInstance()->transBox('bx-popup-loading', $s, true);
+
+                $oSearch = new BxTemplSearch();
+                $oSearch->setLiveSearch(true);
+                $sRet .= $this->parsePageByName('search.html', array(
+                    'search_form' => $oSearch->getForm(BX_DB_CONTENT_ONLY),
+                    'results' => $oSearch->getResultsContainer(),
+                ));
+
+                $sRet .= $this->getMenu ('sys_site');                
+                $sRet .= isLogged() ? $this->getMenu ('sys_add_content') : '';
+                $sRet .= isLogged() ? $this->getMenu ('sys_account_popup') : '';
+                break;
+            case 'lang':
+                $sRet = bx_lang_name();
+                break;
+            case 'main_logo':
+                $sRet = BxTemplFunctions::getInstance()->getMainLogo();
+                break;
+            case 'informer':
+                $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 'copyright':
+                $sRet = _t( '_copyright', date('Y') ) . getVersionComment();
+                break;
+            case 'copyright_attr':
+                $sRet = bx_html_attribute(_t('_copyright', date('Y')));
+                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:
+                $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, '/', $sUrl);
+
+        $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
+        );
+    }
+
+	/**
+     * Minify JS
+     *
+     * @param  string $s JS string to minify
+     * @return string minified JS string.
+     */
+	function _minifyJs($s)
+    {
+        // since each JS file is minified separately, it has to be in own scope
+    	return "\n {\n" . BxDolMinify::getInstance()->minifyJs($s) . "\n }\n";
+    }
+
+    /**
+     * 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 = '';
+
+        	$aAPUrl = parse_url($sAbsolutePath);
+        	if(!empty($aAPUrl['path'])) {
+        		$aAPPath = pathinfo($aAPUrl['path']);
+        		if(!empty($aAPPath['basename'])) {
+        			$sPath = bx_rtrim_str($sAbsolutePath, $aAPPath['basename']);
+        			$sName = $aAPPath['basename'];
+        		}
+        	}
+
+            $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, '/', $sUrl);
+
+        $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;
+
+        		/* Match URL based imports like the following:
+        		 * @import 'http://[domain]/modules/[vendor]/[module]/template/css/view.css';
+        		 * Is mainly needed for CSS files which are gotten from LESS compiler.
+        		 */
+        		$sContent = preg_replace_callback(
+	                "'@import\s+[\'|\"]*\s*" . str_replace("/", "\/", BX_DOL_URL_ROOT) . "([a-zA-Z0-9\.\/_-]+)\s*[\'|\"]*\s*;'", function ($aMatches)  use($oTemplate, $sPath, $aIncluded) {
+	                	return $oTemplate->_compileCss(realpath(BX_DIRECTORY_PATH_ROOT . $aMatches[1]), $aIncluded);
+	                }, $sContent);
+
+				/* Match relative path based imports like the following:
+				 * @import url(../../../../../../base/profile/template/css/main.css);
+				 * Is mainly needed for default CSS files.
+				 */
+	            $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/Less.php');
+
+        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;
+
+            require_once(BX_DIRECTORY_PATH_PLUGINS . 'lessphp/Cache.php');
+        	$aFiles = array($mixed['path'] => $mixed['url']);
+        	$aOptions = array('cache_dir' => $this->_sCachePublicFolderPath);
+        	$sFile = Less_Cache::Get($aFiles, $aOptions, $this->_oConfigTemplate->aLessConfig);
+
+            return array('url' => $this->_sCachePublicFolderUrl . $sFile, 'path' => $this->_sCachePublicFolderPath . $sFile);
+        }
+
+        $oLess = new Less_Parser();
+        $oLess->ModifyVars($this->_oConfigTemplate->aLessConfig);
+        $oLess->parse($mixed);
+        return $oLess->getCss();
+    }
+
+    /**
+     * Minify CSS
+     *
+     * @param  string $s CSS string to minify
+     * @return string minified CSS string.
+     */
+    function _minifyCss($s)
+    {
+    	return BxDolMinify::getInstance()->minifyCss($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)
+                continue;                
+
+            if (!preg_match('/[\.-]min.(js|css)$/i', $aFile['path']) && $this->{'_b' . $sUpcaseType . 'Minify'}) // don't minify minified files
+                $sContent = $this->$sMethodMinify($sContent);
+            
+            $sResult .= $sContent;
+        }
+
+        $mixedWriteResult = false;
+        if(!empty($sResult) && ($rHandler = fopen($sCacheAbsolutePath, 'w')) !== false) {
+            $mixedWriteResult = fwrite($rHandler, $sResult);
+            fclose($rHandler);
+            @chmod ($sCacheAbsolutePath, BX_DOL_FILE_RIGHTS);
+        }
+
+        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));
+                $sFile = bx_ltrim_str($sFile, BX_DIRECTORY_PATH_ROOT);
+
+                $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_callback("'" . $aKeyWrappers['left'] . "([a-zA-Z0-9_-]+)" . $aKeyWrappers['right'] . "'", function($aMatches) use($oTemplate, $mixedKeyWrapperHtml) {
+        	return $oTemplate->parseSystemKey($aMatches[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 . BX_DOL_TEMPLATE_FOLDER_ROOT . $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)
+    {
+    	$sNameLess = str_replace('.css', '.less', $sName);
+
+    	$sResult = $this->_getAbsoluteLocation($sType, $this->_sFolderCss, $sNameLess);
+    	if(!empty($sResult))
+    		return $sResult;
+
+        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 = '')
+    {
+        bx_import('BxDolLanguages');
+        header("HTTP/1.0 403 Forbidden");
+        $this->displayMsg($sMsg ? $sMsg : _t('_Access denied'));
+    }
+    function displayNoData ($sMsg = '')
+    {
+        bx_import('BxDolLanguages');
+        header("HTTP/1.0 204 No Content");
+        $this->displayMsg($sMsg ? $sMsg : _t('_Empty'));
+    }
+    function displayErrorOccured ($sMsg = '')
+    {
+        bx_import('BxDolLanguages');
+        header("HTTP/1.0 500 Internal Server Error");
+        $this->displayMsg($sMsg ? $sMsg : _t('_error occured'));
+    }
+    function displayPageNotFound ($sMsg = '')
+    {
+        bx_import('BxDolLanguages');
+        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());
+    }
+}
+
+/** @} */

+ 1325 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/inc/utils.inc.php

@@ -0,0 +1,1325 @@
+<?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    TridentCore Trident 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)
+{
+    $oAccount = BxDolAccount::getInstance($iAccountId);
+
+    $oProfile = BxDolProfile::getInstance($iProfileId);
+
+    $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)
+{
+    $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
+    $oAccount = !$isDisableAlert ? BxDolAccount::getInstance($sRecipientEmail) : null;
+    $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) {
+        $oProfile = BxDolProfile::getInstance($iRecipientID);
+        if ($oProfile)
+            $aRecipientInfo = $oProfile->getInfo();
+    }
+
+    // get site vars
+    $sEmailNotify = !$isDisableAlert ? getParam('site_email_notify') : $sRecipientEmail;
+    $sSiteTitle = !$isDisableAlert ? getParam('site_title') : 'Trident ' . BX_DOL_VERSION;
+
+    // add unsubscribe link
+    if (!$isDisableAlert && 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();
+        $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)
+{
+    $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($sLang = '', $oTemplate = null)
+{
+    if (!$oTemplate)
+        $oTemplate = BxDolTemplate::getInstance();
+    $oTemplate->addCss(BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'flag-icon-css/css/|flag-icon.min.css');
+    $sFlag = BxDolLanguages::getInstance()->getLangFlag($sLang);
+    return '<span title="' . $sFlag . '" class="flag-icon flag-icon-' . $sFlag . '"></span>';
+}
+
+// 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 . ") ---\n" .
+        $sDesc . "\n" .
+        print_r($mWhat, true) . "\n\n\n";
+
+    $rFile = fopen(BX_DIRECTORY_PATH_LOGS . $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 = '-')
+{
+    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)
+{
+    $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, false))
+        return;
+
+    $aModule = false;
+    if ($mixedModule) {
+        if (is_array($mixedModule)) {
+            $aModule = $mixedModule;
+        } elseif (is_string($mixedModule)) {
+            $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, false))
+            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)) {
+        if(0 == strncmp($sClassName, 'BxTemplStudio', 13)) {
+            $sPath = BX_DIRECTORY_PATH_MODULES . BxDolStudioTemplate::getInstance()->getPath() . 'data/template/studio/scripts/' . $sClassName . '.php';
+        } else {
+            $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);
+    }
+}
+
+/**
+ * used in spl_autoload_register() function, so no need to call bx_import for system classes
+ */
+function bx_autoload($sClassName)
+{
+    if (0 == strncmp($sClassName, 'BxDol', 5) || 0 == strncmp($sClassName, 'BxBase', 6) || 0 == strncmp($sClassName, 'BxTempl', 7))
+        bx_import($sClassName);
+}
+
+/**
+ * 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];
+
+    if ($aModule)
+        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['"'] = '&quot;';
+        $aUnits["'"] = '&apos;';
+        $aUnits["<script>"] = '&lt;script&gt;';
+        $aUnits["</script>"] = '&lt;/script&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(bx_mb_strpos($sFileUrl, 'https') !== false) {
+	        curl_setopt($rConnect, CURLOPT_SSL_VERIFYPEER, 0);
+			curl_setopt($rConnect, CURLOPT_SSL_VERIFYHOST, 0);
+        }
+
+        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)
+{
+	$sAlgo = defined('BX_PWD_ALGO') ? BX_PWD_ALGO : '';
+
+    switch ($sAlgo) {
+    	case 'crypt':
+            return crypt($sPwd, BX_PWD_ALGO_SALT);
+
+        case 'sha1_crypt_salt':
+            return sha1(crypt($sPwd, BX_PWD_ALGO_SALT) . $sSalt);
+
+        case 'sha1_md5_salt':
+        default:
+            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;
+}
+
+function bx_rtrim_str ($sString, $sPrefix, $sReplace = '')
+{
+    if ($sReplace && substr($sString, -strlen($sReplace)) == $sReplace)
+        return $sString;
+    if (substr($sString, -strlen($sPrefix)) == $sPrefix)
+        return substr($sString, 0, -strlen($sPrefix)) . $sReplace;
+    return $sString;
+}
+
+function bx_convert_links($s)
+{
+	$oTemplate = BxDolTemplate::getInstance();
+
+	$bAddNofollow = getParam('sys_add_nofollow') == 'on';
+
+	$aRegExp = array(
+//		"/((https?|ftp|news):\/\/)?(www\.)?([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}))(\/[a-z0-9_\-\.~'\s]+)*(\/([a-z0-9_\-\.]*)(\?[a-z0-9+_\-\.%=&amp;]*)?)?(#[a-z][a-z0-9_]*)?/" => array('link' => 0, 'protocol' => 1),
+		"/(([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?/" => array('link' => 0, 'protocol' => 2)
+	);
+
+	foreach($aRegExp as $sRegExp => $aIndexes)
+		$s = preg_replace_callback($sRegExp, function($aMatches) use($oTemplate, $bAddNofollow, $aIndexes) {
+        	$aLinkAttrs = array(
+        		array('key' => 'target', 'value' => '_blank')
+        	);
+
+        	$sLink = $aMatches[$aIndexes['link']];
+	        if($bAddNofollow && strncmp(BX_DOL_URL_ROOT, $sLink, strlen(BX_DOL_URL_ROOT)) != 0)
+	        	$aLinkAttrs[] = array('key' => 'rel', 'value' => 'nofollow');
+
+        	return $oTemplate->parsePageByName('bx_a.html', array(
+        		'href' => (empty($aMatches[$aIndexes['protocol']]) ? 'http://' : '') . $sLink,
+        		'title' => $sLink,
+        		'bx_repeat:attrs' => $aLinkAttrs,
+        		'content' => $sLink,
+        	));
+        }, $s);
+
+	return $s;
+}
+
+/**
+ * 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 system DB version, for files version @see BX_DOL_VERSION, these versions must match
+ */
+function bx_get_ver ($bInvalidateCache = false)
+{
+    $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 the script 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 BX_DISABLE_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('BX_DISABLE_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 (the script 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:{$aUrl['scheme']}://{$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;
+}
+
+/**
+ * The function is sumilar to php readfile, but it send all required headers and can send file by chunks and suports file seek
+ * @param $sPath path to file to output to the browser
+ * @param $sFilename filename without path, ig file is saved from browser, then this name is used, not used(empty) by default
+ * @param $sMimeType file mime type, by default 'application/octet-stream'
+ * @param $iCacheAge file cache age, by default 0
+ * @param $sCachePrivacy cache privacy 'public' (default value) or 'private'
+ * @return true on success or false on error
+ */
+function bx_smart_readfile($sPath, $sFilename = '', $sMimeType = 'application/octet-stream', $iCacheAge = 0, $sCachePrivacy = 'public')
+{
+    if (!file_exists($sPath))
+        return  false;
+
+    $fp = @fopen($sPath, 'rb');
+
+    $size   = filesize($sPath);
+    $length = $size;
+    $start  = 0;
+    $end    = $size - 1;
+
+    header('Content-type: ' . $sMimeType);
+    header('Cache-Control: ' . $sCachePrivacy . ', must-revalidate, max-age=' . $iCacheAge);
+    header("Accept-Ranges: 0-$length");
+    if ($sFilename)
+        header('Content-Disposition: inline; filename=' . $sFilename);
+
+    if (isset($_SERVER['HTTP_RANGE'])) {
+
+        $c_start = $start;
+        $c_end   = $end;
+
+        list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
+        if (strpos($range, ',') !== false) {
+            header('HTTP/1.1 416 Requested Range Not Satisfiable');
+            header("Content-Range: bytes $start-$end/$size");
+            return false;
+        }
+        if ($range == '-') {
+            $c_start = $size - substr($range, 1);
+        }else{
+            $range  = explode('-', $range);
+            $c_start = $range[0];
+            $c_end   = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
+        }
+        $c_end = ($c_end > $end) ? $end : $c_end;
+        if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
+            header('HTTP/1.1 416 Requested Range Not Satisfiable');
+            header("Content-Range: bytes $start-$end/$size");
+            return false;
+        }
+        $start  = $c_start;
+        $end    = $c_end;
+        $length = $end - $start + 1;
+        fseek($fp, $start);
+        header('HTTP/1.1 206 Partial Content');
+    }
+    header("Content-Range: bytes $start-$end/$size");
+    header("Content-Length: ".$length);
+
+
+    $buffer = 1024 * 8;
+    while(!feof($fp) && ($p = ftell($fp)) <= $end) {
+
+        if ($p + $buffer > $end) {
+            $buffer = $end - $p + 1;
+        }
+        set_time_limit(0);
+        echo fread($fp, $buffer);
+        flush();
+    }
+
+    fclose($fp);
+
+    return true;
+}
+
+/** @} */

+ 12 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/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    TridentCore Trident Core
+ * @{
+ */
+
+define('BX_DOL_VERSION', '8.0.0-B2'); ///< system files version, for DB version @see bx_get_ver, DB and files version must match
+
+/** @} */

+ 333 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/general/classes/BxBaseModGeneralModule.php

@@ -0,0 +1,333 @@
+<?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    BaseGeneral Base classes for modules
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+bx_import('BxDolAcl');
+
+/**
+ * Base module class.
+ */
+class BxBaseModGeneralModule extends BxDolModule
+{
+    protected $_iProfileId;
+
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+        $this->_iProfileId = bx_get_logged_profile_id();
+    }
+
+    // ====== ACTIONS METHODS
+
+    public function actionRss ()
+    {
+        $aArgs = func_get_args();
+        $this->_rss($aArgs);
+    }
+
+    // ====== SERVICE METHODS
+
+    public function serviceModuleIcon ()
+    {
+        return isset($this->_oConfig->CNF['ICON']) ? $this->_oConfig->CNF['ICON'] : '';
+    }
+
+	public function serviceManageTools($sType = 'common')
+    {
+        $oGrid = BxDolGrid::getObjectInstance($this->_oConfig->getGridObject($sType));
+        if(!$oGrid)
+            return '';
+
+		$CNF = &$this->_oConfig->CNF;
+
+		$sMenu = '';
+		if(BxDolAcl::getInstance()->isMemberLevelInSet(192)) {
+			$oPermalink = BxDolPermalinks::getInstance();
+
+			$aMenuItems = array();
+			if(!empty($CNF['OBJECT_GRID_COMMON']) && !empty($CNF['T']['menu_item_manage_my']))
+				$aMenuItems[] = array('id' => 'manage-common', 'name' => 'manage-common', 'class' => '', 'link' => $oPermalink->permalink($CNF['URL_MANAGE_COMMON']), 'target' => '_self', 'title' => _t($CNF['T']['menu_item_manage_my']), 'active' => 1);
+			if(!empty($CNF['OBJECT_GRID_ADMINISTRATION']) && !empty($CNF['T']['menu_item_manage_all']))
+				$aMenuItems[] = array('id' => 'manage-administration', 'name' => 'manage-administration', 'class' => '', 'link' => $oPermalink->permalink($CNF['URL_MANAGE_ADMINISTRATION']), 'target' => '_self', 'title' => _t($CNF['T']['menu_item_manage_all']), 'active' => 1);
+
+			if(count($aMenuItems) > 1) {
+	            $oMenu = new BxTemplMenu(array(
+	            	'template' => 'menu_vertical.html', 
+	            	'menu_items' => $aMenuItems
+	            ), $this->_oTemplate);
+	            $oMenu->setSelected($this->_aModule['name'], 'manage-' . $sType);
+	            $sMenu = $oMenu->getCode();
+			}
+		}
+
+		if(!empty($CNF['OBJECT_MENU_SUBMENU'])) {
+			BxDolMenu::getObjectInstance($CNF['OBJECT_MENU_SUBMENU'])->setSelected($this->_aModule['name'], $CNF['URI_MANAGE_COMMON']);
+		}
+
+        $this->_oTemplate->addCss(array('manage_tools.css'));
+        $this->_oTemplate->addJs(array('manage_tools.js'));
+        $this->_oTemplate->addJsTranslation(array('_sys_grid_search'));
+        return array(
+        	'content' => $this->_oTemplate->getJsCode('manage_tools', array('sObjNameGrid' => $this->_oConfig->getGridObject($sType))) . $oGrid->getCode(),
+        	'menu' => $sMenu
+        );
+    }
+
+    public function serviceGetMenuAddonManageTools()
+    {
+    	return 0;
+    }
+    
+    public function serviceGetMenuAddonManageToolsProfileStats()
+    {
+    	return 0;
+    }
+
+    /**
+     * Add entry form
+     * @return HTML string
+     */
+    public function serviceEntityCreate ()
+    {
+        bx_import('FormsEntryHelper', $this->_aModule);
+        $sClass = $this->_aModule['class_prefix'] . 'FormsEntryHelper';
+        $oFormsHelper = new $sClass($this);
+        return $oFormsHelper->addDataForm();
+    }
+
+    public function serviceEntityEdit ($iContentId = 0)
+    {
+        return $this->_serviceEntityForm ('editDataForm', $iContentId);
+    }
+
+    public function serviceEntityDelete ($iContentId = 0)
+    {
+        return $this->_serviceEntityForm ('deleteDataForm', $iContentId);
+    }
+
+    public function serviceEntityInfo ($iContentId = 0)
+    {
+        return $this->_serviceEntityForm ('viewDataForm', $iContentId);
+    }
+
+    /**
+     * Delete content entry
+     * @param $iContentId content id 
+     * @return error message or empty string on success
+     */
+    public function serviceDeleteEntity ($iContentId, $sFuncDelete = 'deleteData')
+    {
+        bx_import('FormsEntryHelper', $this->_aModule);
+        $sClass = $this->_oConfig->getClassPrefix() . 'FormsEntryHelper';
+        $oFormsHelper = new $sClass($this);
+        return $oFormsHelper->$sFuncDelete($iContentId);
+    }
+
+    /**
+     * Entry actions block
+     */
+    public function serviceEntityActions ($iContentId = 0)
+    {
+        if (!$iContentId)
+            $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        if (!$iContentId)
+            return false;
+
+        $oMenu = BxTemplMenu::getObjectInstance($this->_oConfig->CNF['OBJECT_MENU_ACTIONS_VIEW_ENTRY']);
+        return $oMenu ? $oMenu->getCode() : false;
+    }
+
+    // ====== PERMISSION METHODS
+
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make "true === " checking.
+     */
+    public function checkAllowedBrowse ()
+    {
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
+     */
+    public function checkAllowedView ($aDataEntry, $isPerformAction = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        // moderator and owner always have access
+        if ($aDataEntry[$CNF['FIELD_AUTHOR']] == $this->_iProfileId || $this->_isModerator($isPerformAction))
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        // check ACL
+        $aCheck = checkActionModule($this->_iProfileId, 'view entry', $this->getName(), $isPerformAction);
+        if ($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+            return $aCheck[CHECK_ACTION_MESSAGE];
+
+        // check privacy
+        if (!empty($CNF['OBJECT_PRIVACY_VIEW'])) {
+            $oPrivacy = BxDolPrivacy::getObjectInstance($CNF['OBJECT_PRIVACY_VIEW']);
+            if ($oPrivacy && !$oPrivacy->check($aDataEntry[$CNF['FIELD_ID']]))
+                return _t('_sys_access_denied_to_private_content');
+        }
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
+     */
+    public function checkAllowedAdd ($isPerformAction = false)
+    {
+        // check ACL
+        $aCheck = checkActionModule($this->_iProfileId, 'create entry', $this->getName(), $isPerformAction);
+        if ($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+            return $aCheck[CHECK_ACTION_MESSAGE];
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
+     */
+    public function checkAllowedEdit ($aDataEntry, $isPerformAction = false)
+    {
+        // moderator and owner always have access
+        if ($aDataEntry[$this->_oConfig->CNF['FIELD_AUTHOR']] == $this->_iProfileId || $this->_isModerator($isPerformAction))
+            return CHECK_ACTION_RESULT_ALLOWED;
+        return _t('_sys_txt_access_denied');
+    }
+
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
+     */
+    public function checkAllowedDelete (&$aDataEntry, $isPerformAction = false)
+    {
+        // moderator always has access
+        if ($this->_isModerator($isPerformAction))
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        // check ACL
+        $aCheck = checkActionModule($this->_iProfileId, 'delete entry', $this->getName(), $isPerformAction);
+        if ($aDataEntry[$this->_oConfig->CNF['FIELD_AUTHOR']] == $this->_iProfileId && $aCheck[CHECK_ACTION_RESULT] === CHECK_ACTION_RESULT_ALLOWED)
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        return _t('_sys_txt_access_denied');
+    }
+
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
+     */
+    public function checkAllowedSetMembership (&$aDataEntry, $isPerformAction = false)
+    {
+        // admin always has access
+        if (isAdmin())
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        // check ACL
+        $aCheck = checkActionModule($this->_iProfileId, 'set acl level', 'system', $isPerformAction);
+        if ($aCheck[CHECK_ACTION_RESULT] !== CHECK_ACTION_RESULT_ALLOWED)
+            return $aCheck[CHECK_ACTION_MESSAGE];
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden. So make sure to make strict(===) checking.
+     */
+    public function checkAllowedEditAnyEntry ($isPerformAction = false)
+    {
+    	$aCheck = checkActionModule($this->_iProfileId, 'edit any entry', $this->getName(), $isPerformAction);
+    	if($aCheck[CHECK_ACTION_RESULT] === CHECK_ACTION_RESULT_ALLOWED)
+    		return CHECK_ACTION_RESULT_ALLOWED;
+
+    	return _t('_sys_txt_access_denied');
+    }
+
+    public function _serviceBrowse ($sMode, $aParams = false, $iDesignBox = BX_DB_PADDING_DEF, $bDisplayEmptyMsg = false, $bAjaxPaginate = true, $sClassSearchResult = 'SearchResult')
+    {
+        if (CHECK_ACTION_RESULT_ALLOWED !== ($sMsg = $this->checkAllowedBrowse()))
+            return MsgBox($sMsg);
+
+        bx_import($sClassSearchResult, $this->_aModule);
+        $sClass = $this->_aModule['class_prefix'] . $sClassSearchResult;
+        $o = new $sClass($sMode, $aParams);
+
+        $o->setDesignBoxTemplateId($iDesignBox);
+        $o->setDisplayEmptyMsg($bDisplayEmptyMsg);
+        $o->setAjaxPaginate($bAjaxPaginate);
+        $o->setUnitParams(array('context' => $sMode));
+
+        if ($o->isError)
+            return '';
+
+        if ($s = $o->processing())
+            return $s;
+        else
+            return '';
+    }
+
+    // ====== PROTECTED METHODS
+
+    protected function _isModerator ($isPerformAction = false)
+    {
+        return CHECK_ACTION_RESULT_ALLOWED === $this->checkAllowedEditAnyEntry ($isPerformAction);
+    }
+
+    protected function _serviceEntityForm ($sFormMethod, $iContentId = 0, $sDisplay = false)
+    {
+        if (!$iContentId)
+            $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        if (!$iContentId)
+            return false;
+
+        bx_import('FormsEntryHelper', $this->_aModule);
+        $sClass = $this->_aModule['class_prefix'] . 'FormsEntryHelper';
+        $oFormsHelper = new $sClass($this);
+        return $oFormsHelper->$sFormMethod((int)$iContentId, $sDisplay);
+    }
+
+    protected function _serviceTemplateFunc ($sFunc, $iContentId, $sFuncGetContent = 'getContentInfoById')
+    {
+        if (!$iContentId)
+            $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+        if (!$iContentId)
+            return false;
+
+        $aContentInfo = $this->_oDb->$sFuncGetContent($iContentId);
+        if (!$aContentInfo)
+            return false;
+
+        return $this->_oTemplate->$sFunc($aContentInfo);
+    }
+
+    protected function _rss ($aArgs, $sClass = 'SearchResult')
+    {
+        $sMode = array_shift($aArgs);
+
+        if (CHECK_ACTION_RESULT_ALLOWED !== ($sMsg = $this->checkAllowedBrowse())) {
+            $this->_oTemplate->displayAccessDenied ($sMsg);
+            exit;
+        }
+
+        $aParams = $this->_buildRssParams($sMode, $aArgs);
+
+        bx_import ($sClass, $this->_aModule);
+        $sClass = $this->_aModule['class_prefix'] . $sClass;
+        $o = new $sClass($sMode, $aParams);
+
+        if ($o->isError)
+            $this->_oTemplate->displayPageNotFound ();
+        else
+            $o->outputRSS();
+
+        exit;
+    }
+}
+
+/** @} */

+ 199 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/profile/classes/BxBaseModProfileGridAdministration.php

@@ -0,0 +1,199 @@
+<?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    BaseProfile Base classes for profile modules
+ * @ingroup     TridentModules
+ * 
+ * @{
+ */
+
+class BxBaseModProfileGridAdministration extends BxBaseModGeneralGridAdministration
+{
+	protected $_sFilter1Name;
+	protected $_sFilter1Value;
+	protected $_aFilter1Values;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $this->_sFilter1Name = 'filter1';
+        $this->_aFilter1Values = array(
+			'active' => $CNF['T']['filter_item_active'],
+            'pending' => $CNF['T']['filter_item_pending'],
+            'suspended' => $CNF['T']['filter_item_suspended'],
+		);
+
+    	$sFilter1 = bx_get($this->_sFilter1Name);
+        if(!empty($sFilter1)) {
+            $this->_sFilter1Value = bx_process_input($sFilter1);
+            $this->_aQueryAppend[$this->_sFilter1Name] = $this->_sFilter1Value;
+        }
+    }
+
+    public function performActionSetAclLevel()
+    {
+    	$oMenu = BxDolMenu::getObjectInstance('sys_set_acl_level');
+
+    	$aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds) || !$oMenu) {
+            $this->_echoResultJson(array());
+            return;
+        }
+
+        $aIdsResult = array();
+        foreach($aIds as $iId) {
+        	$aContentInfo = $this->_oModule->_oDb->getContentInfoById($iId);
+	    	if($this->_oModule->checkAllowedSetMembership($aContentInfo) !== CHECK_ACTION_RESULT_ALLOWED)
+	    		continue;
+
+        	$aIdsResult[] = $this->_getProfileId($iId);
+        }
+
+        if(empty($aIdsResult)) {
+            $this->_echoResultJson(array());
+            return;
+        }
+
+		if(count($aIdsResult) == 1)
+			$aIdsResult = $aIdsResult[0];
+
+		$sContent = $this->_oTemplate->parseHtmlByName('set_acl_popup.html', array(
+			'content' => $oMenu->getCode($aIdsResult)
+		));
+
+		$sContent = BxTemplFunctions::getInstance()->transBox($this->_oModule->_oConfig->getName() . 'set_acl_level_popup', $sContent);
+
+    	$this->_echoResultJson(array('popup' => $sContent), true);
+    }
+
+	public function performActionDeleteWithContent()
+    {
+    	$this->performActionDelete(array('with_content' => true));
+    }
+
+    protected function _getActionSetAclLevel($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+    	if($this->_sManageType == BX_DOL_MANAGE_TOOLS_ADMINISTRATION && $this->_oModule->checkAllowedSetMembership($aRow) !== CHECK_ACTION_RESULT_ALLOWED)
+			return '';
+
+		return $this->_getActionDefault($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);	
+    }
+
+	protected function _getActionDeleteWithContent($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+		if($this->_sManageType == BX_DOL_MANAGE_TOOLS_ADMINISTRATION && $this->_oModule->checkAllowedEditAnyEntry() !== CHECK_ACTION_RESULT_ALLOWED)
+			return '';
+
+    	return $this->_getActionDefault($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+    
+    protected function _switcherChecked2State($isChecked)
+    {
+        return $isChecked ? 'active' : 'suspended';
+    }
+
+    protected function _switcherState2Checked($mixedState)
+    {
+        return 'active' == $mixedState ? true : false;
+    }
+
+    protected function _enable ($mixedId, $isChecked)
+    {
+    	$oProfile = $this->_getProfileObject($mixedId);
+
+    	if($oProfile instanceof BxDolProfileUndefined)
+    		return false;
+
+		$iAction = BX_PROFILE_ACTION_MANUAL;
+    	return $isChecked ? $oProfile->activate($iAction) : $oProfile->suspend($iAction);
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        if(strpos($sFilter, $this->_sParamsDivider) !== false)
+            list($this->_sFilter1Value, $sFilter) = explode($this->_sParamsDivider, $sFilter);
+
+    	if(!empty($this->_sFilter1Value))
+        	$this->_aOptions['source'] .= $this->_oModule->_oDb->prepare(" AND `tp`.`status`=?", $this->_sFilter1Value);
+
+        return parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+    }
+
+    //--- Layout methods ---//
+	protected function _getFilterControls()
+    {
+        parent::_getFilterControls();
+
+        return  $this->_getFilterSelectOne($this->_sFilter1Name, $this->_sFilter1Value, $this->_aFilter1Values) . $this->_getSearchInput();
+    }
+
+    protected function _getCellFullname($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$oProfile = $this->_getProfileObject($aRow['id']);
+
+        $mixedValue = $this->_oTemplate->parseHtmlByName('name_link.html', array(
+            'href' => $oProfile->getUrl(),
+            'title' => $mixedValue,
+            'content' => $mixedValue
+        ));
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellLastOnline($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault(bx_time_js($mixedValue), $sKey, $aField, $aRow);
+    }
+
+	protected function _getCellAccount($mixedValue, $sKey, $aField, $aRow)
+    {
+    	$sManageAccountUrl = $this->_getManageAccountUrl($aRow[$sKey]);
+    	if(!empty($sManageAccountUrl)) {
+    		$mixedValue = $this->_oTemplate->parseHtmlByName('account_link.html', array(
+    			'href' => $sManageAccountUrl,
+    			'title' => _t($this->_oModule->_oConfig->CNF['T']['grid_txt_account_manager']),
+    			'content' => $mixedValue, 
+    		));
+    	}
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getProfileObject($iId)
+    {
+    	$oProfile = BxDolProfile::getInstanceByContentAndType((int)$iId, $this->_oModule->_oConfig->getName());
+        if (!$oProfile) 
+            $oProfile = BxDolProfileUndefined::getInstance();
+
+        return $oProfile;
+    }
+
+    protected function _getProfileId($iId)
+    {
+    	return $this->_getProfileObject($iId)->id();
+    }
+
+	protected function _doDelete($iId, $aParams = array())
+    {
+    	if($this->_oModule->checkMyself($iId))
+    		return false;
+
+    	if(isset($aParams['with_content']) && $aParams['with_content'] === true) {
+    		$oProfile = $this->_getProfileObject($iId);
+
+    		if($oProfile instanceof BxDolProfileUndefined)
+    			return false;
+
+	    	return $oProfile->delete($oProfile->id(), true);
+    	}
+
+    	return parent::_doDelete($iId, $aParams);
+    }
+}
+
+/** @} */

+ 81 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/profile/classes/BxBaseModProfilePageEntry.php

@@ -0,0 +1,81 @@
+<?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    BaseProfile Base classes for profile modules
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+/**
+ * Profile create/edit/delete pages.
+ */
+class BxBaseModProfilePageEntry extends BxBaseModGeneralPageEntry
+{
+    protected $_aProfileInfo;
+    protected $_oProfile;
+
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        // get profile info
+        $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
+        $iContentId = bx_process_input(bx_get('id'), BX_DATA_INT);
+
+        if ($iProfileId)
+            $this->_oProfile = BxDolProfile::getInstance($iProfileId);
+
+        if (!$this->_oProfile && $iContentId)
+            $this->_oProfile = BxDolProfile::getInstanceByContentAndType($iContentId, $this->MODULE);
+
+        if ($this->_oProfile) {
+            $this->_aProfileInfo = $this->_oProfile->getInfo();
+            $this->_aContentInfo = $this->_oModule->_oDb->getContentInfoById($this->_aProfileInfo['content_id']);
+        }
+
+        if (!$this->_aContentInfo || !$this->_oProfile)
+            return;
+
+        // select view profile submenu
+        $oMenuSubmenu = BxDolMenu::getObjectInstance('sys_site_submenu');
+        $oMenuSubmenu->setObjectSubmenu($CNF['OBJECT_MENU_SUBMENU_VIEW_ENTRY'], array (
+            'title' => $this->_oProfile->getDisplayName(),
+            'link' => $this->_oProfile->getUrl(),
+            'icon' => $CNF['ICON'],
+        ));
+
+        // add replaceable markers
+        $this->addMarkers($this->_aProfileInfo); // every content field can be used as marker
+        $this->addMarkers(array('profile_id' => $this->_oProfile->id())); // profile id field
+        $this->addMarkers(array('display_name' => $this->_oProfile->getDisplayName())); // profile display name
+        $this->addMarkers(array('profile_link' => $this->_oProfile->getUrl())); // profile link
+
+        // display message if profile isn't active
+        $aInformers = array ();
+        $oInformer = BxDolInformer::getInstance($this->_oTemplate);
+        if (bx_get_logged_profile_id() == $this->_oProfile->id() && !empty($CNF['INFORMERS']['status']) && $oInformer) {
+            $sStatus = $this->_aContentInfo['profile_status'];
+            if (isset($CNF['INFORMERS']['status']['map'][$sStatus]))
+                $aInformers[] = array ('name' => $CNF['INFORMERS']['status']['name'], 'msg' => _t($CNF['INFORMERS']['status']['map'][$sStatus]), 'type' => BX_INFORMER_ALERT);
+        }
+
+        // display message if it is possible to switch to this profile
+        $oProfile = $this->_aContentInfo ? BxDolProfile::getInstanceByContentTypeAccount($this->_aContentInfo['id'], $this->MODULE) : false;
+        if ($oProfile)
+            $oProfile->checkSwitchToProfile($this->_oTemplate);
+
+        // add informers
+        if ($aInformers && $oInformer) {
+            foreach ($aInformers as $a)
+                $oInformer->add($a['name'], $this->_replaceMarkers($a['msg']), $a['type']);
+        }
+    }
+
+}
+
+/** @} */

+ 53 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/text/classes/BxBaseModTextPageAuthor.php

@@ -0,0 +1,53 @@
+<?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    BaseText Base classes for text modules
+ * @ingroup     TridentModules
+ *
+ * @{
+ */
+
+/**
+ * Profile's entries page.
+ */
+class BxBaseModTextPageAuthor extends BxTemplPage
+{
+    protected $_aProfileInfo;
+    protected $_oProfile;
+
+    public function __construct($aObject, $oTemplate = false)
+    {
+        parent::__construct($aObject, $oTemplate);
+
+        // get profile info
+        $iProfileId = bx_process_input(bx_get('profile_id'), BX_DATA_INT);
+        if ($iProfileId) {
+            $this->_oProfile = BxDolProfile::getInstance($iProfileId);
+            $this->_aProfileInfo = $this->_oProfile ? $this->_oProfile->getInfo() : false;
+        }
+
+        if (!$this->_aProfileInfo || !$this->_oProfile)
+            return;
+
+        // select view profile submenu
+        $oMenuSubmenu = BxDolMenu::getObjectInstance('sys_site_submenu');
+        if ($oMenuSubmenu) {
+            $oMenuSubmenu->setObjectSubmenu('bx_persons_view_submenu', array (
+                'title' => $this->_oProfile->getDisplayName(),
+                'link' => $this->_oProfile->getUrl(),
+                'icon' => $this->_oProfile->getIconModule(),
+            ));
+        }
+
+        // add replaceable markers
+        $this->addMarkers($this->_aProfileInfo); // every profile field can be used as marker
+        $this->addMarkers(array('profile_id' => $this->_oProfile->id())); // profile id 
+        $this->addMarkers(array('display_name' => $this->_oProfile->getDisplayName())); // profile display name 
+        $this->addMarkers(array('profile_link' => $this->_oProfile->getUrl())); // profile link
+    }
+
+}
+
+/** @} */

+ 194 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/modules/base/text/template/css/main.css

@@ -0,0 +1,194 @@
+
+@import url(../../../general/template/css/main.css);
+
+/* unit */
+
+.bx-base-text-unit:first-child h2,
+.bx-base-text-unit:first-child h1 {
+    padding-top:0;
+}
+
+.bx-base-text-unit h2,
+.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 {
+    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;
+    background-size:cover;
+    background-repeat:no-repeat;
+    background-position:center center;
+    display:table;
+    width:100%;
+}
+
+.bx-base-text-unit-gallery h2 {
+    line-height:1.25em;
+    max-height:3.75em;
+    overflow:hidden; 
+    margin-bottom:0;
+}
+
+.bx-base-text-wrapper {
+    margin-bottom:0;
+}
+
+.bx-base-text-unit-ext .bx-base-text-unit-thumb {
+    float:left;
+}
+
+.bx-base-text-unit-strecher {
+    visibility: hidden;
+}
+
+
+/* flexbox */
+
+.bx-base-text-unit-gallery-wrapper {
+    display: -webkit-box;
+    display: -webkit-flex;
+    display: -ms-flexbox;
+    display: flex;
+
+    /* makes all items on a row equal height */
+    -webkit-box-align: stretch;
+    -webkit-align-items: stretch;
+    -ms-flex-align: stretch;
+    align-items: stretch;
+
+    /* wrap onto the next line rather than stretching or shrinking */
+    -webkit-flex-wrap: wrap;
+    -ms-flex-wrap: wrap;
+    flex-wrap: wrap;
+
+    /* tells flexbox to start items from the left */
+    -webkit-box-pack: start;
+    -ms-flex-pack: start;
+    plex-pack: start;
+    -webkit-justify-content: flex-start;
+    justify-content: flex-start;
+}
+
+.bx-base-text-unit-gallery-wrapper .bx-base-text-unit-gallery {
+    display: -webkit-box;
+    display: -webkit-flex;
+    display: -ms-flexbox;
+    display: flex;
+
+    -webkit-box-flex: 0;
+
+    -webkit-flex: 0 0 100%;
+    -ms-flex: 0 0 100%;
+    flex: 0 0 100%;
+
+    overflow:hidden;
+}
+
+@media (min-width:533px) and (max-width:720px) {
+    .bx-layout-column-half .bx-base-text-unit-gallery-wrapper .bx-base-text-unit-gallery,
+    .bx-layout-column-thin .bx-base-text-unit-gallery-wrapper .bx-base-text-unit-gallery {
+        -webkit-flex: 0 0 49.8%;
+        -ms-flex: 0 0 49.8%;
+        flex: 0 0 49.8%;
+    }
+}
+
+@media (min-width:533px) and (max-width:1024px) {
+    .bx-layout-column-wide .bx-base-text-unit-gallery-wrapper .bx-base-text-unit-gallery,
+    #sys_search_results .bx-base-text-unit-gallery-wrapper .bx-base-text-unit-gallery,
+    .bx-layout-column-fullwidth .bx-base-text-unit-gallery-wrapper .bx-base-text-unit-gallery {
+        -webkit-flex: 0 0 49.8%;
+        -ms-flex: 0 0 49.8%;
+        flex: 0 0 49.8%;
+    }
+}
+
+@media (min-width:1024px) {
+    .bx-layout-column-wide .bx-base-text-unit-gallery-wrapper .bx-base-text-unit-gallery {
+        -webkit-flex: 0 0 49.8%;
+        -ms-flex: 0 0 49.8%;
+        flex: 0 0 49.8%;
+    }
+    #sys_search_results .bx-base-text-unit-gallery-wrapper .bx-base-text-unit-gallery,
+    .bx-layout-column-fullwidth .bx-base-text-unit-gallery-wrapper .bx-base-text-unit-gallery {
+        -webkit-flex: 0 0 33.25%;
+        -ms-flex: 0 0 33.25%;
+        flex: 0 0 33.25%;
+    }
+}
+
+/* view page */
+
+.bx-base-text-entity-content p:first-child,
+.bx-base-text-entity-content h2: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 h2: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;
+}
+
+.bx-base-text-attachments .bx-base-text-attachment video,
+.bx-base-text-attachments .bx-base-text-attachment {
+    width:18.75rem;
+    height:12.5rem;
+}
+
+.bx-base-text-attachments {
+    margin:-1px;
+}
+.bx-base-text-attachment {
+    margin:1px;
+}
+
+.bx-base-text-entity-actions-all .bx-base-text-entity-share,
+.bx-base-text-entity-actions-all .bx-menu-btns-hor {
+    display:inline-block;
+    vertical-align:top;
+}
+
+.bx-base-text-comments-item {
+    float: left;
+}
+
+.bx-base-text-comments-item-counter-holder {
+    float: left;
+    font-size: 1.5rem;
+    line-height: 2.2rem;
+}

+ 325 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/plugins_public/codemirror/codemirror.css

@@ -0,0 +1,325 @@
+/* BASICS */
+
+.CodeMirror {
+  /* Set height, width, borders, and global font properties here */
+  font-family: monospace;
+  height: 300px;
+  color: black;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+  padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+  padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+  border-right: 1px solid #ddd;
+  background-color: #f7f7f7;
+  white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+  padding: 0 3px 0 5px;
+  min-width: 20px;
+  text-align: right;
+  color: #999;
+  white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror div.CodeMirror-cursor {
+  border-left: 1px solid black;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+  border-left: 1px solid silver;
+}
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursor {
+  width: auto;
+  border: 0;
+  background: #7e7;
+}
+.CodeMirror.cm-fat-cursor div.CodeMirror-cursors {
+  z-index: 1;
+}
+
+.cm-animate-fat-cursor {
+  width: auto;
+  border: 0;
+  -webkit-animation: blink 1.06s steps(1) infinite;
+  -moz-animation: blink 1.06s steps(1) infinite;
+  animation: blink 1.06s steps(1) infinite;
+}
+@-moz-keyframes blink {
+  0% { background: #7e7; }
+  50% { background: none; }
+  100% { background: #7e7; }
+}
+@-webkit-keyframes blink {
+  0% { background: #7e7; }
+  50% { background: none; }
+  100% { background: #7e7; }
+}
+@keyframes blink {
+  0% { background: #7e7; }
+  50% { background: none; }
+  100% { background: #7e7; }
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+div.CodeMirror-overwrite div.CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-ruler {
+  border-left: 1px solid #ccc;
+  position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+   the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+  position: relative;
+  overflow: hidden;
+  background: white;
+}
+
+.CodeMirror-scroll {
+  overflow: scroll !important; /* Things will break if this is overridden */
+  /* 30px is the magic margin used to hide the element's real scrollbars */
+  /* See overflow: hidden in .CodeMirror */
+  margin-bottom: -30px; margin-right: -30px;
+  padding-bottom: 30px;
+  height: 100%;
+  outline: none; /* Prevent dragging from highlighting the element */
+  position: relative;
+}
+.CodeMirror-sizer {
+  position: relative;
+  border-right: 30px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+   before actuall scrolling happens, thus preventing shaking and
+   flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+  position: absolute;
+  z-index: 6;
+  display: none;
+}
+.CodeMirror-vscrollbar {
+  right: 0; top: 0;
+  overflow-x: hidden;
+  overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+  bottom: 0; left: 0;
+  overflow-y: hidden;
+  overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+  right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+  left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+  position: absolute; left: 0; top: 0;
+  z-index: 3;
+}
+.CodeMirror-gutter {
+  white-space: normal;
+  height: 100%;
+  display: inline-block;
+  margin-bottom: -30px;
+  /* Hack to make IE7 behave */
+  *zoom:1;
+  *display:inline;
+}
+.CodeMirror-gutter-wrapper {
+  position: absolute;
+  z-index: 4;
+  height: 100%;
+}
+.CodeMirror-gutter-elt {
+  position: absolute;
+  cursor: default;
+  z-index: 4;
+}
+.CodeMirror-gutter-wrapper {
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  user-select: none;
+}
+
+.CodeMirror-lines {
+  cursor: text;
+  min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+  /* Reset some styles that the rest of the page might have set */
+  -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+  border-width: 0;
+  background: transparent;
+  font-family: inherit;
+  font-size: inherit;
+  margin: 0;
+  white-space: pre;
+  word-wrap: normal;
+  line-height: inherit;
+  color: inherit;
+  z-index: 2;
+  position: relative;
+  overflow: visible;
+  -webkit-tap-highlight-color: transparent;
+}
+.CodeMirror-wrap pre {
+  word-wrap: break-word;
+  white-space: pre-wrap;
+  word-break: normal;
+}
+
+.CodeMirror-linebackground {
+  position: absolute;
+  left: 0; right: 0; top: 0; bottom: 0;
+  z-index: 0;
+}
+
+.CodeMirror-linewidget {
+  position: relative;
+  z-index: 2;
+  overflow: auto;
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-code {
+  outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+  -moz-box-sizing: content-box;
+  box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+  position: absolute;
+  width: 100%;
+  height: 0;
+  overflow: hidden;
+  visibility: hidden;
+}
+.CodeMirror-measure pre { position: static; }
+
+.CodeMirror div.CodeMirror-cursor {
+  position: absolute;
+  border-right: none;
+  width: 0;
+}
+
+div.CodeMirror-cursors {
+  visibility: hidden;
+  position: relative;
+  z-index: 3;
+}
+.CodeMirror-focused div.CodeMirror-cursors {
+  visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror ::selection { background: #d7d4f0; }
+.CodeMirror ::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+  background: #ffa;
+  background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+  /* Hide the cursor when printing */
+  .CodeMirror div.CodeMirror-cursors {
+    visibility: hidden;
+  }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }

Fișier diff suprimat deoarece este prea mare
+ 16 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/plugins_public/codemirror/codemirror.min.js


+ 615 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/classes/BxDolStudioInstallerUtils.php

@@ -0,0 +1,615 @@
+<?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    TridentStudio Trident Studio
+ * @{
+ */
+
+bx_import('BxDolLanguages');
+
+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;
+    }
+
+	public static function getNamePerformModulesUpgrade()
+    {
+    	return 'sys_upgrade_modules_transient';
+    }
+
+    /*
+     * 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)));
+    }
+
+	/*
+     * Is used to perform modules upgrade inside Transient Cron Job.
+     */
+	public function servicePerformModulesUpgrade($bEmailNotify)
+    {
+    	return $this->performModulesUpgrade(true, $bEmailNotify);
+    }
+
+    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();
+
+        $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))
+			$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 performModulesUpgrade($bDirectly = true, $bEmailNotify = true)
+    {
+    	if(!defined('BX_DOL_CRON_EXECUTE') && !$bDirectly)
+    		return $this->addTransientJob(self::getNamePerformModulesUpgrade(), 'perform_modules_upgrade', array($bEmailNotify));   	
+
+        $aFailed = array();
+		$aUpdates = $this->checkUpdates();
+		if(empty($aUpdates) || !is_array($aUpdates))
+			return true;
+
+	    foreach($aUpdates as $aUpdate) {
+	    	$mixedResult = $this->downloadUpdatePublic($aUpdate['name']);
+	        if($mixedResult !== true) {
+	        	$aFailed[$aUpdate['name']] = $mixedResult;
+				continue;
+			}
+		}
+
+		$aSuccess = array();
+		$aUpdates = $this->getUpdates();
+		if(empty($aUpdates) || !is_array($aUpdates))
+			return true;
+
+		foreach($aUpdates as $aUpdate) {
+			$aResult = $this->perform($aUpdate['dir'], 'update');
+			if((int)$aResult['code'] != 0) {
+				$aFailed[$aUpdate['module_name']] = $aResult['message'];
+				continue;
+			}
+
+			$aSuccess[$aUpdate['module_name']] = $aUpdate['version_to'];
+		}
+
+    	if($bEmailNotify && !empty($aSuccess))
+    		$this->emailNotifyModulesUpgrade('success', $aSuccess);
+
+        if($bEmailNotify && !empty($aFailed))
+        	$this->emailNotifyModulesUpgrade('failed', $aFailed);
+
+		return empty($aFailed);
+    }
+
+    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')));
+
+    	$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)
+    {
+		$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)
+    {
+		$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)
+    {
+    	$sFilePath = BX_DIRECTORY_PATH_TMP . $aItem['name'];
+    	if(file_exists($sFilePath))
+        	@unlink($sFilePath);
+
+		$iUmaskSave = umask(0);
+
+        if(!$rHandler = fopen($sFilePath, 'w')) {
+        	umask($iUmaskSave);
+            return _t('_adm_str_err_cannot_write');
+        }
+
+        $sContent = urldecode($aItem['content']);
+        if(!fwrite($rHandler, $sContent, strlen($sContent))) {
+        	umask($iUmaskSave);
+            return _t('_adm_str_err_cannot_write');
+        }
+
+        fclose($rHandler);
+        
+        umask($iUmaskSave);
+        return true;
+    }
+
+    protected function performUnarchive($sFilePath, &$sPackagePath)
+    {
+    	if(!class_exists('ZipArchive'))
+            return _t('_adm_str_err_zip_not_available');
+
+		$iUmaskSave = umask(0);
+
+        $oZip = new ZipArchive();
+        if($oZip->open($sFilePath) !== true) {
+        	umask($iUmaskSave);
+            return _t('_adm_str_err_cannot_unzip_package');
+        }
+
+        $sPackageFolder = '';
+        if($oZip->numFiles > 0)
+        	$sPackageFolder = $oZip->getNameIndex(0);
+
+        if(empty($sPackageFolder)) {
+        	umask($iUmaskSave);
+        	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)) {
+        	umask($iUmaskSave);
+            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');
+	
+	        $oFile = new BxDolFtp($_SERVER['HTTP_HOST'], $sLogin, $sPassword, $sPath);
+	
+	        if(!$oFile->connect())
+	            return _t('_adm_str_err_cannot_connect_to_ftp');
+	
+	        if(!$oFile->isTrident())
+	            return _t('_adm_str_err_destination_not_valid');
+        }
+        else
+        	$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()
+    {
+        $aModules = BxDolModuleQuery::getInstance()->getModules();
+
+        $aInstalledInfo = array();
+        foreach($aModules as $aModule)
+            $aInstalledInfo[$aModule['path']] = $aModule;
+
+        return $aInstalledInfo;
+    }
+
+    private function addTransientJob($sName, $sAction, $aParams)
+    {
+		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),
+		));
+    }
+
+    private function emailNotifyModulesUpgrade($sResult, $aData)
+    {
+		$oModuleQuery = BxDolModuleQuery::getInstance();
+
+    	$sConclusion = '';
+    	if(!empty($aData))
+			foreach($aData as $sModule => $sMessage) {
+				$aModule = $oModuleQuery->getModuleByName($sModule);
+
+				$sConclusion .= _t('_sys_et_txt_body_modules_upgrade_' . $sResult, $aModule['title'], $sMessage);
+			}
+
+		sendMailTemplateSystem('t_UpgradeModules' . ucfirst($sResult), array (
+			'conclusion' => $sConclusion,
+		));
+    }
+}
+
+/** @} */

+ 473 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/classes/BxDolStudioLanguagesUtils.php

@@ -0,0 +1,473 @@
+<?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    TridentStudio Trident Studio
+ * @{
+ */
+
+bx_import('BxDolLanguages');
+
+class BxDolStudioLanguagesUtils extends BxDolLanguages implements iBxDolSingleton
+{
+    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->oDb = new BxDolStudioLanguagesUtilsQuery();
+    }
+
+    /**
+     * 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 BxDolStudioLanguagesUtils();
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    function getLanguageInfo($sName)
+    {
+        $aLanguage = array();
+        $this->oDb->getLanguagesBy(array('type' => 'by_name', 'value' => $sName), $aLanguage, false);
+
+        if(!empty($aLanguage))
+            $aLanguage['icon'] = genFlag($aLanguage['name'], BxDolStudioTemplate::getInstance());
+
+        return $aLanguage;
+    }
+
+    function getLanguagesInfo($bIdAsKey = false, $bActiveOnly = false)
+    {
+        $aLanguages = array();
+        $this->oDb->getLanguagesBy(array('type' => 'all'), $aLanguages, false);
+        if(!is_array($aLanguages) || empty($aLanguages))
+            return $aLanguages;
+
+        $oTemplate  = BxDolStudioTemplate::getInstance();
+        foreach($aLanguages as $iKey => $aLanguage)
+            $aLanguages[$iKey]['icon'] = genFlag($aLanguage['name'], $oTemplate);
+
+        return $aLanguages;
+    }
+
+    function readLanguage($sPath, $sType = 'restore')
+    {
+        if(!file_exists($sPath))
+            return array();
+
+        $oXmlParser = BxDolXmlParser::getInstance();
+        $sXmlContent = file_get_contents($sPath);
+
+        $aResult = array();
+        $aResult['name'] = $oXmlParser->getAttribute($sXmlContent, 'resources', 'name');
+
+        $aResult['title'] = $oXmlParser->getAttribute($sXmlContent, 'resources', 'title');
+        if($aResult['title'] === false)
+			unset($aResult['title']);
+
+        $aResult['category'] = $oXmlParser->getAttribute($sXmlContent, 'resources', 'category');
+		if($aResult['category'] === false)
+			unset($aResult['category']);
+
+        switch($sType) {
+            case 'restore':
+                $aResult['strings'] = $oXmlParser->getValues($sXmlContent, 'string');
+                break;
+
+            case 'update':
+                $aResult['strings_del'] = $oXmlParser->getValues($sXmlContent, 'string_del');
+                if(empty($aResult['strings_del']) || !is_array($aResult['strings_del']))
+                	unset($aResult['strings_del']);
+
+                $aResult['strings_add'] = $oXmlParser->getValues($sXmlContent, 'string_add');
+                if(empty($aResult['strings_add']) || !is_array($aResult['strings_add']))
+                	unset($aResult['strings_add']);
+
+                $aResult['strings_upd'] = $oXmlParser->getValues($sXmlContent, 'string_upd');
+                if(empty($aResult['strings_upd']) || !is_array($aResult['strings_upd']))
+                	unset($aResult['strings_upd']);
+
+                break;
+        }
+
+        return $aResult;
+    }
+
+    function compileLanguage($mixedLang = 0, $bForce = false)
+    {
+        $sType = 'all';
+        if(!empty($mixedLang)) {
+            if(is_string($mixedLang) && !is_numeric($mixedLang))
+                $sType = 'all_by_name';
+            else if(is_numeric($mixedLang) || is_int($mixedLang)) {
+                $sType = 'all_by_id';
+                $mixedLang = (int)$mixedLang;
+            }
+        }
+
+        $aLanguages = array();
+        $iLanguages = $this->oDb->getLanguagesBy(array('type' => $sType, 'value' => $mixedLang), $aLanguages);
+
+        if($iLanguages == 0)
+            return false;
+
+        $oFile = BxDolFile::getInstance();
+
+        $sNewLine = "\r\n";
+        foreach($aLanguages as $aLanguage) {
+            if(!$bForce && (int)$aLanguage['enabled'] != 1)
+                continue;
+
+			$aKeys = array();
+            $this->oDb->getKeysBy(array('type' => 'by_language_id_key_key', 'value' => $aLanguage['id']), $aKeys);
+
+            if($aLanguage['name'] != BX_DOL_LANGUAGE_DEFAULT && getParam('lang_subst_from_en') == 'on') {
+				$aKeysAll = array();
+				$this->oDb->getKeysBy(array('type' => 'by_language_name_key_key', 'value' => BX_DOL_LANGUAGE_DEFAULT), $aKeysAll);
+
+	            $aKeys = array_merge($aKeysAll, $aKeys);
+            }
+
+            $sLanguageFile = "lang-" . $aLanguage['name'] . ".php";
+            $oFile->delete('cache/' . $sLanguageFile);
+
+            $rHandle = fopen( BX_DIRECTORY_PATH_CACHE . $sLanguageFile, 'w');
+            if($rHandle === false)
+                return false;
+
+            $sContent = "<?php{$sNewLine}\$GLOBALS['LANG'] = array(";
+            foreach($aKeys as $aKey) {
+                list($sKey, $sString) = str_replace(array("\\", "'"), array("\\\\", "\\'"), array($aKey['key'], $aKey['string']));
+                $sContent .= "{$sNewLine}\t'$sKey' => '$sString',";
+            }
+
+            $sContent = trim($sContent, ',');
+
+            if(fwrite($rHandle, $sContent."{$sNewLine});?>") === false)
+                return false;
+
+            if(fclose($rHandle) === false)
+                return false;
+
+            @chmod( BX_DIRECTORY_PATH_CACHE . "lang-{$aLanguage['name']}.php", 0666);
+        }
+
+        $this->init();
+        BxDolCacheUtilities::getInstance()->clear('template');
+
+        return true;
+    }
+
+    function installLanguage($aLanguageModule, $bRecompile = true)
+    {
+        $aLanguage = array();
+        $this->oDb->getLanguagesBy(array('type' => 'by_name', 'value' => $aLanguageModule['uri']), $aLanguage, false);
+        if(empty($aLanguage) || !is_array($aLanguage))
+            return false;
+
+        $sPath = BX_DIRECTORY_PATH_MODULES . $aLanguageModule['path'] . 'data/langs/';
+        if(!($rHandler = opendir($sPath)))
+            return false;
+
+		$oModuleQuery = BxDolModuleQuery::getInstance();
+
+        $bResult = false;
+        while(($sFileName = readdir($rHandler)) !== false) {
+        	if($sFileName == '.' || $sFileName == '..')
+				continue;
+
+			$aModule = $oModuleQuery->getModuleByName($sFileName);
+            if(empty($aModule) || !is_array($aModule))
+            	continue;
+
+			$bResult |= $this->_restoreLanguageByModule($aLanguage, $sPath, $aModule);
+        }
+        closedir($rHandler);
+
+        $bResult |= $this->_restoreLanguageByModule($aLanguage, $sPath, $aLanguageModule);
+        return $bResult && ($bRecompile ? $this->compileLanguage($aLanguage['id']) : true);
+    }
+
+    function restoreLanguage($mixedLang = 0, $mixedModule = 0, $bRecompile = true)
+    {
+        $aLanguages = array();
+        if(!empty($mixedLang)) {
+            $sType = is_string($mixedLang) && !is_numeric($mixedLang) ? 'by_name' : 'by_id';
+            $this->oDb->getLanguagesBy(array('type' => $sType, 'value' => $mixedLang), $aLanguages[0], false);
+        } 
+        else
+			$this->oDb->getLanguagesBy(array('type' => 'all'), $aLanguages, false);
+
+        $bResult = true;
+        foreach($aLanguages as $aLanguage) {
+            if(empty($aLanguage) || !is_array($aLanguage))
+                continue;
+
+            $oModuleQuery = BxDolModuleQuery::getInstance();
+
+            $aLanguageModule = $oModuleQuery->getModuleByUri($aLanguage['name']);
+            if(empty($aLanguageModule) || !is_array($aLanguageModule))
+                continue;
+
+            $sPath = BX_DIRECTORY_PATH_MODULES . $aLanguageModule['path'] . 'data/langs/';
+
+            if(!empty($mixedModule))
+                $bResult &= $this->_restoreLanguageByModule($aLanguage, $sPath, $mixedModule);
+            else {
+            	$aModules = $oModuleQuery->getModules();
+            	foreach($aModules as $aModule)
+            		$bResult &= $this->_restoreLanguageByModule($aLanguage, $sPath, $aModule);
+            }
+
+            if($bRecompile)
+                $bResult &= $this->compileLanguage($aLanguage['id']);
+        }
+
+        return $bResult;
+    }
+
+    function removeLanguageById($mixedLang = 0, $bRecompile = true)
+    {
+        $aLanguages = array();
+        if(!empty($mixedLang)) {
+            $sType = is_string($mixedLang) && !is_numeric($mixedLang) ? 'by_name' : 'by_id';
+            $this->oDb->getLanguagesBy(array('type' => $sType, 'value' => $mixedLang), $aLanguages[0], false);
+        } else
+            $this->oDb->getLanguagesBy(array('type' => 'all'), $aLanguages, false);
+
+        $bResult = true;
+        foreach($aLanguages as $aLanguage) {
+            if(empty($aLanguage) || !is_array($aLanguage))
+                continue;
+
+            $bResult &= (int)$this->oDb->deleteStringsBy(array('type' => 'by_lang', 'language_id' => $aLanguage['id'])) >= 0;
+
+            if($bRecompile)
+                $bResult &= $this->compileLanguage($aLanguage['id']);
+        }
+
+        return $bResult;
+    }
+
+    function removeLanguageByModule($mixedModule, $bRecompile = true)
+    {
+        $aModule = $mixedModule;
+        if(!is_array($mixedModule)) {
+            $sMethod = is_string($mixedModule) && !is_numeric($mixedModule) ? 'getModuleByUri' : 'getModuleById';
+            $aModule = BxDolModuleQuery::getInstance()->$sMethod($mixedModule);
+        }
+
+        if(empty($aModule) || !is_array($aModule) || !$aModule['uri'] || !$aModule['lang_category'])
+            return false;
+
+        $aCategory = array();
+        $this->oDb->getCategoriesBy(array('type' => 'by_name', 'value' => $aModule['lang_category']), $aCategory, false);
+        if(empty($aCategory) || !is_array($aCategory))
+            return false;
+
+        $bResult = (int)$this->oDb->deleteKeysBy(array('type' => 'by_cat_id', 'value' => $aCategory['id'])) >= 0;
+        return $bResult && ($bRecompile ? $this->compileLanguage() : true);
+    }
+
+    function getLanguageString($sKey, $iLangId = 0)
+    {
+        if($iLangId == 0) {
+            $aStrings = array();
+            $this->oDb->getStringsBy(array('type' => 'all_by_key_key_language_id', 'value' => $sKey), $aStrings, false);
+
+            return $aStrings;
+        }
+
+        $aString = array();
+        $this->oDb->getStringsBy(array('type' => 'by_key_language_id', 'key' => $sKey, 'language_id' => $iLangId), $aString, false);
+
+        return $aString;
+    }
+
+    function addLanguageString($sKey, $mixedString, $iLangId = 0, $iCategoryId = 0, $bRecompile = true)
+    {
+        $iLangId = (int)$iLangId;
+
+        $iCategoryId = (int)$iCategoryId;
+        if($iCategoryId == 0)
+            $iCategoryId = BX_DOL_LANGUAGE_CATEGORY_CUSTOM;
+
+        $sKey = bx_process_input($sKey);
+        $mixedString = bx_process_input($mixedString);
+
+        $aLanguages = array();
+        $iLanguages = $this->oDb->getLanguagesBy(($iLangId == 0 ? array('type' => 'all') : array('type' => 'all_by_id', 'value' => $iLangId)), $aLanguages);
+
+        if(($iKeyId = $this->oDb->addKey($iCategoryId, $sKey)) === false)
+            return false;
+
+        foreach($aLanguages as $aLanguage) {
+            $sString = '';
+            if(is_string($mixedString))
+                $sString = $mixedString;
+            else if(is_array($mixedString))
+                $sString = $mixedString[$aLanguage['id']];
+
+            if(!$this->oDb->addString($iKeyId, $aLanguage['id'], $sString))
+                return false;
+
+            if($bRecompile)
+                $this->compileLanguage($aLanguage['id']);
+        }
+
+        return $iKeyId;
+    }
+
+    function updateLanguageString($sKey, $mixedString, $iLangId = 0, $iCategoryId = 0, $bRecompile = true)
+    {
+        $sKey = bx_process_input($sKey);
+        $mixedString = bx_process_input($mixedString);
+        $iLangId = (int)$iLangId;
+        $iCategoryId = (int)$iCategoryId;
+        if(empty($iCategoryId))
+        	$iCategoryId = BX_DOL_LANGUAGE_CATEGORY_CUSTOM;
+
+        $aKey = array();
+        $this->oDb->getKeysBy(array('type' => 'by_name', 'value' => $sKey), $aKey);
+        if(empty($aKey))
+            return $this->addLanguageString($sKey, $mixedString, $iLangId, $iCategoryId, $bRecompile);
+
+		if((int)$aKey['category_id'] != $iCategoryId)
+			$this->oDb->updateKeys(array('IDCategory' => $iCategoryId), array('ID' => $aKey['id']));
+
+        return $this->updateLanguageStringById($aKey['id'], $mixedString, $iLangId, $bRecompile);
+    }
+
+    function updateLanguageStringById($iKeyId, $mixedString, $iLangId = 0, $bRecompile = true)
+    {
+        $iKeyId = (int)$iKeyId;
+        $mixedString = bx_process_input($mixedString);
+        $iLangId = (int)$iLangId;
+
+        $aLanguages = array();
+        $iLanguages = $this->oDb->getLanguagesBy(($iLangId == 0 ? array('type' => 'all') : array('type' => 'all_by_id', 'value' => $iLangId)), $aLanguages);
+
+        foreach($aLanguages as $aLanguage) {
+            $sString = '';
+            if(is_string($mixedString))
+                $sString = $mixedString;
+            else if(is_array($mixedString))
+                $sString = $mixedString[$aLanguage['id']];
+
+            if($this->oDb->updateString($iKeyId, $aLanguage['id'], $sString) && $bRecompile)
+                $this->compileLanguage($aLanguage['id']);
+        }
+
+        return true;
+    }
+
+    function deleteLanguageString($sKey, $iLangId = 0, $bRecompile = true)
+    {
+        $iLangId = (int)$iLangId;
+        $sKey = bx_process_input($sKey);
+
+        $aKey = array();
+        $this->oDb->getKeysBy(array('type' => 'by_name', 'value' => $sKey), $aKey);
+        if(empty($aKey))
+            return false;
+
+        return $this->deleteLanguageStringById($aKey['id'], $iLangId, $bRecompile);
+    }
+
+    function deleteLanguageStringById($iKeyId, $iLangId = 0, $bRecompile = true)
+    {
+        $iKeyId = (int)$iKeyId;
+        $iLangId = (int)$iLangId;
+
+        $aLanguages = array();
+        $iLanguages = $this->oDb->getLanguagesBy(($iLangId == 0 ? array('type' => 'all') : array('type' => 'all_by_id', 'value' => $iLangId)), $aLanguages);
+
+        foreach($aLanguages as $aLanguage) {
+            if(!$this->oDb->deleteString($iKeyId, $aLanguage['id']))
+                continue;
+
+            if($bRecompile)
+                $this->compileLanguage($aLanguage['id']);
+        }
+
+        return $this->oDb->deleteKey($iKeyId);
+    }
+
+    function addLanguageCategory($sName)
+    {
+        return $this->oDb->addCategory($sName);
+    }
+
+    function deleteLanguageCategory($sName)
+    {
+        return $this->oDb->deleteCategory($sName);
+    }
+
+    protected function _restoreLanguageByModule($aLanguage, $sPath, $mixedModule)
+    {
+        $oXmlParser = BxDolXmlParser::getInstance();
+
+        $aModule = $mixedModule;
+        if(!is_array($mixedModule)) {
+            $sMethod = is_string($mixedModule) && !is_numeric($mixedModule) ? 'getModuleByUri' : 'getModuleById';
+            $aModule = BxDolModuleQuery::getInstance()->$sMethod($mixedModule);
+        }
+
+        if(empty($aModule) || !is_array($aModule) || !$aModule['name'] || !$aModule['lang_category'])
+            return true;
+
+		$sPath = $sPath . $aModule['name'] . '/' . $aLanguage['name'] . '.xml';
+        if(!file_exists($sPath) && isset($aModule['path'])) {
+            $sPath = BX_DIRECTORY_PATH_MODULES . $aModule['path'] . 'install/langs/' . $aLanguage['name'] . '.xml';
+            if(!file_exists($sPath))
+                return true;
+        }
+
+        $aLanguageInfo = $this->readLanguage($sPath);
+        if(empty($aLanguageInfo['name']) || empty($aLanguageInfo['strings']) || $aLanguageInfo['name'] != $aLanguage['name'])
+            return false;
+
+        $aCategory = array();
+        $this->oDb->getCategoriesBy(array('type' => 'by_name', 'value' => $aModule['lang_category']), $aCategory, false);
+
+        $iCategoryId = 0;
+        if(empty($aCategory) || !is_array($aCategory)) {
+        	$iCategoryId = $this->addLanguageCategory($aModule['lang_category']);
+        	if(empty($iCategoryId))
+        		return false;
+        }
+        else
+        	$iCategoryId = $aCategory['id'];
+
+		$bDeleteStringsByKey = !$this->oDb->deleteStringsBy(array('type' => 'by_cat_and_lang', 'category_id' => $iCategoryId, 'language_id' => $aLanguage['id']));
+
+        $bResult = true;
+        foreach($aLanguageInfo['strings'] as $sKey => $sValue)
+            if($sKey != '') {
+            	if($bDeleteStringsByKey)
+            		$this->oDb->deleteStringsBy(array('type' => 'by_key_and_lang', 'key' => $sKey, 'language_id' => $aLanguage['id']));
+
+                $bResult &= $this->addLanguageString($sKey, $sValue, $aLanguage['id'], $iCategoryId, false) > 0;
+            }
+
+        return $bResult;
+    }
+}
+
+/** @} */

+ 102 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/classes/BxDolStudioPolyglotKeys.php

@@ -0,0 +1,102 @@
+<?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    TridentStudio Trident Studio
+ * @{
+ */
+
+define('BX_DOL_STUDIO_PK_CATEGORY_SYSTEM', 1);
+define('BX_DOL_STUDIO_PK_CATEGORY_CUSTOM', 2);
+
+define('BX_DOL_STUDIO_PK_PREVIEW', 100);
+
+class BxDolStudioPolyglotKeys extends BxTemplStudioGrid
+{
+    protected $iPreviewLength;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->oDb = new BxDolStudioPolyglotQuery();
+        $this->iPreviewLength = BX_DOL_STUDIO_PK_PREVIEW;
+    }
+
+    protected function add(&$oForm)
+    {
+        $sKey = $oForm->getCleanValue('key');
+        $iCategoryId = (int)$oForm->getCleanValue('category_id');
+        $aLanguages = explode(',', $oForm->getCleanValue('languages'));
+
+        if(empty($sKey) || empty($iCategoryId))
+            return _t('_adm_pgt_err_create_key_empty_fields');
+
+        $aStrings = array();
+        foreach($aLanguages as $iLanguageId)
+            $aStrings[$iLanguageId] = $oForm->getCleanValue('language_' . $iLanguageId);
+
+        $mixedResult = BxDolStudioLanguagesUtils::getInstance()->addLanguageString($sKey, $aStrings, 0, $iCategoryId);
+        if($mixedResult === false)
+            return _t('_adm_pgt_err_create_key');
+
+        return $mixedResult;
+    }
+    protected function edit(&$oForm)
+    {
+        $iId = $oForm->getCleanValue('id');
+        $aLanguages = explode(',', $oForm->getCleanValue('languages'));
+
+        if(empty($iId) || empty($aLanguages))
+            return _t('_adm_pgt_err_save');
+
+        $aStrings = array();
+        foreach($aLanguages as $iLanguageId)
+            $aStrings[$iLanguageId] = $oForm->getCleanValue('language_' . $iLanguageId);
+
+        if(!BxDolStudioLanguagesUtils::getInstance()->updateLanguageStringById($iId, $aStrings))
+            return _t('_adm_pgt_err_save');
+
+        return $iId;
+    }
+
+    protected function _getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage)
+    {
+        $iModule = 0;
+        if(strpos($sFilter, $this->sParamsDivider) !== false)
+            list($iModule, $sFilter) = explode($this->sParamsDivider, $sFilter);
+
+        $aLanguage = array();
+        $iLanguage = $this->oDb->getLanguagesBy(array('type' => 'default'), $aLanguage);
+
+        if($iLanguage <= 0 || empty($aLanguage)) {
+            $aLanguages = array();
+            $iLanguages = $this->oDb->getLanguagesBy(array('type' => 'active'), $aLanguages);
+            if($iLanguages <= 0 || empty($aLanguages))
+                return array();
+
+            $aLanguage = $aLanguages[0];
+        }
+
+        $this->_aOptions['source'] = sprintf($this->_aOptions['source'], $aLanguage['id']);
+        if((int)$iModule != 0)
+            $this->_aOptions['source'] .= $this->oDb->prepare(" AND `tlk`.`IDCategory`=?", $iModule);
+
+        $aResults = parent::_getDataSql($sFilter, $sOrderField, $sOrderDir, $iStart, $iPerPage);
+
+        foreach($aResults as $iIndex => $aResult) {
+            $aKey = $this->oDb->getKeyFullInfo($aResult['id'], $this->iPreviewLength);
+            if(empty($aKey))
+                continue;
+
+            $aResults[$iIndex]['module'] = $aKey['module'];
+            $aResults[$iIndex]['string'] = $aKey['strings'][$aLanguage['name']]['preview'];
+            $aResults[$iIndex]['languages'] = implode(', ', array_keys($aKey['strings']));
+        }
+
+        return $aResults;
+    }
+}
+
+/** @} */

+ 646 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/classes/BxDolStudioToolsAudit.php

@@ -0,0 +1,646 @@
+<?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    TridentStudio Trident 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 $sMinPhpVer;
+    protected $aPhpSettings;
+    protected $iPhpErrorReporting;
+
+    protected $sMinMysqlVer;
+    protected $aMysqlOptimizationSettings;
+
+    protected $aOptimizationSettings;
+
+    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->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' => 'without', 'val' => 'shell_exec,eval,assert,phpinfo,getenv,ini_set,mail,fsockopen,chmod,parse_ini_file'),
+            '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'),
+            'php module: fileinfo' => array('op' => 'module', 'val' => 'fileinfo'),
+            'php module: zip' => array('op' => 'module', 'val' => 'zip'),
+            'php module: openssl' => array('op' => 'module', 'val' => 'openssl'),
+            'php module: exif' => array('op' => 'module', 'val' => 'exif'),
+        );
+
+        $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->aOptimizationSettings = 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 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.3.0', '>=') && version_compare($sPhpVer, '5.4.0', '<'))
+            $aVer = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_version_is_outdated', $sLatestPhpVersion), '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()
+    {
+        $sFfmpegPath = defined('BX_SYSTEM_FFMPEG') ? BX_SYSTEM_FFMPEG : BX_DIRECTORY_PATH_PLUGINS . 'ffmpeg/ffmpeg.exe';
+
+        $sEmailToCkeckMailSending = class_exists('BxDolDb') && BxDolDb::getInstance() ? BxDolDb::getInstance()->getParam('site_email') : '';
+
+        $oUpgrader = new BxDolUpgrader();
+        if (!($sLatestVer = $oUpgrader->getLatestVersionNumber()))
+            $sLatestVer = 'undefined';
+
+        $sVer = bx_get_ver();
+        $aMessage = array('type' => BX_DOL_AUDIT_OK);
+        if (!version_compare($sVer, $sLatestVer, '>='))
+            $aMessage = array('type' => BX_DOL_AUDIT_WARN, 'msg' => _t('_sys_audit_msg_version_is_outdated', $sLatestVer));
+
+        $s = $this->getBlock(_t('_sys_audit_version_script'), $sVer, $this->getMsgHTML(_t('_sys_audit_version_script'), $aMessage));
+
+        $s .= $this->getBlock(_t('_sys_audit_permissions'), '', _t('_sys_audit_msg_permissions'));
+
+        $s .= $this->getBlock('ffmpeg', '', _t('_sys_audit_msg_ffmpeg', `{$sFfmpegPath} 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->optimizationScript();
+    }
+
+    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 optimizationScript()
+    {
+        $s = '';
+        foreach ($this->aOptimizationSettings as $sName => $a) {
+
+            $sVal = ('always_on' == $a['enabled'] || getParam($a['enabled'])) ? 'On' : 'Off';
+            if ($a['cache_engine'])
+                $sVal .= _t('_sys_audit_msg_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_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_optimization_warn'));
+            else
+                $aMessage = array('type' => BX_DOL_AUDIT_OK);
+
+            $s .= $this->getBlock($sName, $sVal, $this->getMsgHTML($sName, $aMessage));
+        }
+
+        echo $this->getSection('Trident', '', $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 'without':
+                $aFuncsDisabled = explode(',', $mixedVal);
+                $aFuncsMustBeEnabled = explode(',', $a['val']);
+                $a = array_intersect($aFuncsDisabled, $aFuncsMustBeEnabled);
+                $bResult = !$a;
+                break;
+            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;
+    }
+}
+
+/** @} */

+ 398 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioDesigner.php

@@ -0,0 +1,398 @@
+<?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    TridentView Trident Studio Representation classes
+ * @ingroup     TridentStudio
+ * @{
+ */
+
+class BxBaseStudioDesigner extends BxDolStudioDesigner
+{
+    protected $sLogoFormId = 'adm-dsg-logo-form';
+    protected $sLogoIframeId = 'adm-dsg-logo-iframe';
+    protected $sIconFormId = 'adm-dsg-icon-form';
+    protected $sIconIframeId = 'adm-dsg-icon-iframe';
+    protected $sCoverFormId = 'adm-dsg-cover-form';
+    protected $sCoverIframeId = 'adm-dsg-cover-iframe';
+    protected $sCoverEditorId = 'adm-dsg-cover-editor';
+
+    function __construct($sPage = '')
+    {
+        parent::__construct($sPage);
+    }
+    function getPageCss()
+    {
+        return array_merge(parent::getPageCss(), array('forms.css', 'designer.css'));
+    }
+    function getPageJs()
+    {
+        return array_merge(parent::getPageJs(), array('settings.js', 'designer.js'));
+    }
+    function getPageJsObject()
+    {
+        return 'oBxDolStudioDesigner';
+    }
+    function getPageMenu($aMenu = array(), $aMarkers = array())
+    {
+        $sJsObject = $this->getPageJsObject();
+
+        $aMenu = array();
+        $aMenuItems = array(
+            BX_DOL_STUDIO_DSG_TYPE_GENERAL => array('icon' => 'globe'),
+            BX_DOL_STUDIO_DSG_TYPE_LOGO => array('icon' => 'pencil'),
+            BX_DOL_STUDIO_DSG_TYPE_ICON => array('icon' => 'picture-o'),
+            BX_DOL_STUDIO_DSG_TYPE_COVER => array('icon' => 'file-image-o'),
+            BX_DOL_STUDIO_DSG_TYPE_SETTINGS => array('icon' => 'cogs'),
+        );
+        foreach($aMenuItems as $sMenuItem => $aItem)
+            $aMenu[] = array(
+                'name' => $sMenuItem,
+                'icon' => $aItem['icon'],
+                'link' => BX_DOL_URL_STUDIO . 'designer.php?page=' . $sMenuItem,
+                'title' => _t('_adm_lmi_cpt_' . $sMenuItem),
+                'selected' => $sMenuItem == $this->sPage
+            );
+
+        return parent::getPageMenu($aMenu);
+    }
+    function getPageCode($bHidden = false)
+    {
+        $sMethod = 'get' . ucfirst($this->sPage);
+        if(!method_exists($this, $sMethod))
+            return '';
+
+        return $this->$sMethod();
+    }
+
+    protected function getGeneral()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $sResult = '';
+
+        $sTemplate = getParam('template');
+        $aTemplates = get_templates_array(true, false);
+
+        $aTmplVarsTemplates = array ();
+        foreach($aTemplates as $sUri => $aTemplate) {
+        	$sIcon = $this->getModuleIcon($aTemplate, 'store');
+	        $bIcon = strpos($sIcon, '.') === false;
+
+            $aTmplVarsTemplates[] = array(
+                'uri' => $sUri,
+                'title' => htmlspecialchars_adv($aTemplate['title']),
+                'version' => htmlspecialchars_adv($aTemplate['version']),
+                'vendor' => htmlspecialchars_adv($aTemplate['vendor']),
+            	'bx_if:icon' => array (
+	                'condition' => $bIcon,
+	                'content' => array('icon' => $sIcon),
+	            ),
+                'bx_if:image' => array (
+	                'condition' => !$bIcon,
+	                'content' => array('icon_url' => $sIcon),
+	            ),
+                'bx_if:default' => array (
+                    'condition' => $sUri == $sTemplate,
+                    'content' => array (),
+                ),
+                'bx_if:make_default' => array (
+                    'condition' => $sUri != $sTemplate,
+                    'content' => array(
+                        'js_object' => $sJsObject,
+                        'uri' => $sUri
+                    ),
+                )
+            );
+        }
+
+        $sContent  = $sResult ? MsgBox($sResult, 10) : '';
+        $sContent .= $oTemplate->parseHtmlByName('templates.html', array(
+            'bx_repeat:templates' => $aTmplVarsTemplates,
+        ));
+
+        return $oTemplate->parseHtmlByName('designer.html', array(
+            'js_object' => $this->getPageJsObject(),
+            'content' => $sContent,
+        ));
+    }
+
+    protected function getLogo()
+    {
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => $this->sLogoFormId,
+                'name' => $this->sLogoFormId,
+                'action' => BX_DOL_URL_STUDIO . 'designer.php',
+                'method' => 'post',
+                'enctype' => 'multipart/form-data',
+                'target' => $this->sLogoIframeId
+            ),
+            'params' => array(
+                'db' => array(
+                    'table' => '',
+                    'key' => '',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'save'
+                ),
+            ),
+            'inputs' => array(
+                'page' => array(
+                    'type' => 'hidden',
+                    'name' => 'page',
+                    'value' => $this->sPage
+                ),
+                'image' => array(
+                    'type' => 'image_uploader',
+                    'name' => 'image',
+                    'caption' => _t('_adm_dsg_txt_upload_image'),
+                    'caption_preview' => _t('_adm_dsg_txt_upload_image_preview'),
+                    'ajax_action_delete' => $this->getPageJsObject() . '.deleteLogo()',
+                    'storage_object' => 'sys_images_custom',
+                    'transcoder_object' => 'sys_custom_images',
+                    'transcoder_image_width' => getParam('sys_site_logo_width'),
+                    'transcoder_image_height' => getParam('sys_site_logo_height'),
+                    'value' => (int)getParam('sys_site_logo'),
+                ),
+                'width' => array(
+                    'type' => 'text',
+                    'name' => 'width',
+                    'caption' => _t('_adm_stg_cpt_option_sys_site_logo_width'),
+                    'value' => getParam('sys_site_logo_width'),
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'height' => array(
+                    'type' => 'text',
+                    'name' => 'height',
+                    'caption' => _t('_adm_stg_cpt_option_sys_site_logo_height'),
+                    'value' => getParam('sys_site_logo_height'),
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'alt' => array(
+                    'type' => 'text',
+                    'name' => 'alt',
+                    'caption' => _t('_adm_dsg_txt_alt_text'),
+                    'info' => _t('_adm_dsg_dsc_alt_text'),
+                    'value' => getParam('sys_site_logo_alt'),
+                    'checker' => array(
+                        'func' => '',
+                        'params' => array(),
+                        'error' => ''
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'save' => array(
+                    'type' => 'submit',
+                    'name' => 'save',
+                    'value' => _t('_adm_btn_designer_submit'),
+                )
+            )
+        );
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            echo $this->submitLogo($oForm);
+            exit;
+        }
+
+        return $oTemplate->parseHtmlByName('designer.html', array(
+            'js_object' => $this->getPageJsObject(),
+            'content' => $this->getBlockCode(array(
+				'items' => $oTemplate->parseHtmlByName('dsr_logo.html', array('logo_iframe_id' => $this->sLogoIframeId, 'form' => $oForm->getCode())),
+			))
+        ));
+    }
+
+    protected function getIcon()
+    {
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $sPreview = "";
+        $aTmplVars = array('bx_repeat:images' => array());
+
+        if(($iId = (int)getParam('sys_site_icon')) != 0) {
+            $aTranscoders = array(
+                BX_DOL_TRANSCODER_OBJ_ICON_APPLE => '_adm_dsg_txt_icon_apple',
+                BX_DOL_TRANSCODER_OBJ_ICON_FACEBOOK => '_adm_dsg_txt_icon_facebook',
+                BX_DOL_TRANSCODER_OBJ_ICON_FAVICON => '_adm_dsg_txt_icon_favicon'
+            );
+
+            foreach($aTranscoders as $sTranscoder => $sTitle) {
+                $oTranscoder = BxDolTranscoderImage::getObjectInstance($sTranscoder);
+
+                $sImageUrl = $oTranscoder->getFileUrl($iId);
+                if($sImageUrl === false) {
+                    setParam('sys_site_icon', 0);
+                    break;
+                }
+
+                $aTmplVars['bx_repeat:images'][] = array(
+                    'caption' => _t($sTitle),
+                    'url' => $sImageUrl
+                );
+            }
+
+            $sPreview = $oTemplate->parseHtmlByName('dsr_icon_preview.html', $aTmplVars);
+        }
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => $this->sIconFormId,
+                'name' => $this->sIconFormId,
+                'action' => BX_DOL_URL_STUDIO . 'designer.php',
+                'method' => 'post',
+                'enctype' => 'multipart/form-data',
+                'target' => $this->sIconIframeId
+            ),
+            'params' => array(
+                'db' => array(
+                    'table' => '',
+                    'key' => '',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'save'
+                ),
+            ),
+            'inputs' => array(
+                'page' => array(
+                    'type' => 'hidden',
+                    'name' => 'page',
+                    'value' => $this->sPage
+                ),
+                'preview' => array(
+                    'type' => 'custom',
+                    'name' => 'preview',
+                    'content' => $sPreview
+                ),
+                'image' => array(
+                    'type' => 'file',
+                    'name' => 'image',
+                    'caption' => _t('_adm_dsg_txt_upload_icon')
+                ),
+                'save' => array(
+                    'type' => 'submit',
+                    'name' => 'save',
+                    'value' => _t('_adm_btn_designer_submit'),
+                )
+            )
+        );
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            echo $this->submitIcon($oForm);
+            exit;
+        }
+
+        return $oTemplate->parseHtmlByName('designer.html', array(
+            'js_object' => $this->getPageJsObject(),
+            'content' => $this->getBlockCode(array(
+				'items' => $oTemplate->parseHtmlByName('dsr_icon.html', array('icon_iframe_id' => $this->sIconIframeId, 'form' => $oForm->getCode())),
+			))
+        ));
+    }
+
+    protected function getCover()
+    {
+    	$oTemplate = BxDolStudioTemplate::getInstance();
+
+		$aForm = array(
+			'form_attrs' => array(
+				'id' => $this->sCoverFormId,
+				'name' => $this->sCoverFormId,
+				'action' => BX_DOL_URL_STUDIO . 'designer.php',
+				'method' => 'post',
+				'enctype' => 'multipart/form-data',
+				'target' => $this->sCoverIframeId
+			),
+			'params' => array(
+				'db' => array(
+					'table' => '',
+					'key' => '',
+					'uri' => '',
+					'uri_title' => '',
+					'submit_name' => 'save'
+				),
+			),
+			'inputs' => array(
+				'page' => array(
+					'type' => 'hidden',
+					'name' => 'page',
+					'value' => $this->sPage
+				),
+				'enabled' => array(
+					'type' => 'checkbox',
+					'name' => 'enabled',
+					'caption' => _t('_adm_dsg_txt_cover_enabled'),
+					'value' => 'on',
+					'checked' => getParam('sys_site_cover_enabled') == 'on',
+					'db' => array (
+                        'pass' => 'Xss',
+                    ),
+				),
+				'code' => array(
+					'type' => 'textarea',
+					'name' => 'code',
+					'caption' => '',
+					'value' => getParam('sys_site_cover_code'),
+					'db' => array (
+                        'pass' => 'XssHtml',
+                    ),
+				),
+				'save' => array(
+					'type' => 'submit',
+					'name' => 'save',
+					'value' => _t('_adm_btn_designer_submit'),
+				)
+			)
+		);
+
+		$oForm = new BxTemplStudioFormView($aForm, $oTemplate);
+		$oForm->initChecker();
+
+		if($oForm->isSubmittedAndValid()) {
+			echo $this->submitCover($oForm);
+			exit;
+		}
+
+        $oTemplate->addJs(array('codemirror/codemirror.min.js'));
+        $oTemplate->addCss(BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'codemirror/|codemirror.css');
+
+		return $oTemplate->parseHtmlByName('designer.html', array(
+			'js_object' => $this->getPageJsObject(),
+			'content' => $this->getBlockCode(array(
+				'items' => $oTemplate->parseHtmlByName('dsr_cover.html', array(
+					'warning' => MsgBox(_t('_adm_dsg_dsc_cover_warning')),
+					'cover_iframe_id' => $this->sCoverIframeId, 
+					'form' => $oForm->getCode()
+				)),
+			))
+		));
+    }
+
+    protected function getSettings()
+    {
+        $oPage = new BxTemplStudioSettings(BX_DOL_STUDIO_STG_TYPE_SYSTEM, BX_DOL_STUDIO_STG_CATEGORY_TEMPLATES);
+
+        return BxDolStudioTemplate::getInstance()->parseHtmlByName('designer.html', array(
+            'js_object' => $this->getPageJsObject(),
+            'content' => $oPage->getPageCode()
+        ));
+    }
+}
+
+/** @} */

+ 1358 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioFormsField.php

@@ -0,0 +1,1358 @@
+<?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    TridentView Trident Studio Representation classes
+ * @ingroup     TridentStudio
+ * @{
+ */
+
+class BxBaseStudioFormsField extends BxDolStudioFormsField
+{
+    protected $aForm;
+    protected $sTypeTitlePrefix = '_adm_form_txt_field_type_';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $sJsObject = $this->getJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        bx_import('BxTemplStudioFormView');
+
+        $this->aForm = array(
+            'form_attrs' => array(
+                'id' => '',
+                'action' => '',
+                'method' => BX_DOL_STUDIO_METHOD_DEFAULT
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_form_inputs',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'type' => array(
+                    'type' => 'custom',
+                    'name' => 'type',
+                    'content' => '',
+                    'attrs' => array(
+                        'id' => 'bx-form-field-type',
+                        'class' => 'bx-form-field-type',
+                        'onchange' => $sJsObject . '.onSelectType()'
+                    ),
+                ),
+                'reset' => array (
+                    'type' => 'reset',
+                    'name' => 'close',
+                    'value' => _t('_adm_form_btn_field_cancel'),
+                    'attrs' => array(
+                        'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                        'class' => 'bx-def-margin-sec-left',
+                    ),
+                )
+            )
+        );
+
+        $aTypes = array();
+        foreach($this->aTypes as $sType)
+            $aTypes[$sType] = _t($this->sTypeTitlePrefix . $sType);
+
+        asort($aTypes);
+
+        $aMenu = array();
+        foreach($aTypes as $sName => $sTitle)
+            $aMenu[] = array(
+                'name' => $sName,
+                'icon' => 'ui-' . $sName . '.png',
+                'onclick' => $sJsObject . ".onSelectType('" . $sName . "', this)",
+                'title' => $sTitle
+            );
+
+        $oMenu = new BxTemplStudioMenu(array('template' => 'menu_vertical.html', 'menu_items' => $aMenu));
+        $this->aForm['inputs']['type']['content'] = $oMenu->getCode();
+    }
+
+    function getJsObject()
+    {
+        return BX_DOL_STUDIO_FORMS_FIELDS_JS_OBJECT;
+    }
+
+    function getCode($sAction, $sObject)
+    {
+        $sFunction = 'getCode' . $this->getClassName($sAction);
+        if(method_exists($this, $sFunction))
+            return $this->$sFunction($sAction, $sObject);
+
+        return false;
+    }
+
+    protected function getCodeAdd($sAction, $sObject)
+    {
+        $aForm = $this->getFormAdd($sAction, $sObject);
+        $oForm = new BxTemplStudioFormView($aForm);
+
+        if($oForm->isSubmitted() && isset($oForm->aInputs['required']))
+            $this->updateCheckerFields($oForm);
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+            //--- Process field name.
+            $sInputName = $oForm->getCleanValue('name');
+            if(empty($sInputName)) {
+            	$sInputObject = $oForm->getCleanValue('object');
+
+	            $sLanguage = BxDolStudioLanguagesUtils::getInstance()->getCurrentLangName(false);
+	            $sInputCaption = BxDolForm::getSubmittedValue('caption-' . $sLanguage, $aForm['form_attrs']['method']);
+	
+	            $sInputName = $this->getFieldName($sInputObject, $sInputCaption);
+	            BxDolForm::setSubmittedValue('name', $sInputName, $oForm->aFormAttrs['method']);
+            }
+
+            $this->onSubmitField($oForm);
+            if(($iId = $oForm->insert()) === false)
+                return false;
+
+            $this->alterAdd($sInputName);
+            return true;
+        } else
+            return BxTemplStudioFunctions::getInstance()->popupBox('adm-form-field-add-' . $this->aParams['display'] . '-popup', _t('_adm_form_txt_field_add_popup'), BxDolStudioTemplate::getInstance()->parseHtmlByName('form_add_field.html', array(
+                'form_id' => $aForm['form_attrs']['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $sObject,
+                'action' => $sAction
+            )));
+    }
+
+    protected function getCodeEdit($sAction, $sObject)
+    {
+        $aForm = $this->getFormEdit($sAction, $sObject);
+        $oForm = new BxTemplStudioFormView($aForm);
+
+        if($oForm->isSubmitted() && isset($oForm->aInputs['required']))
+            $this->updateCheckerFields($oForm);
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+        	$sInputNameOld = $this->aField['name'];
+            $sInputNameNew = $oForm->getCleanValue('name');
+
+            $this->onSubmitField($oForm);
+            if($oForm->update((int)$this->aField['id']) === false)
+                return false;
+
+			if(strcmp($sInputNameOld, $sInputNameNew) != 0)
+				$this->alterChange($sInputNameOld, $sInputNameNew);
+            return true;
+        } else
+            return BxTemplStudioFunctions::getInstance()->popupBox('adm-form-field-edit-' . $this->aField['type'] . '-popup', _t('_adm_form_txt_field_edit_popup', _t($this->aField['caption'])), BxDolStudioTemplate::getInstance()->parseHtmlByName('form_add_field.html', array(
+                'form_id' => $aForm['form_attrs']['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $sObject,
+                'action' => $sAction
+            )));
+    }
+
+    protected function getFormAdd($sAction, $sObject)
+    {
+        $aForm = $this->aForm;
+        $aForm['form_attrs']['id'] = 'adm-form-field-add-' . $this->aParams['display'];
+        $aForm['form_attrs']['action'] = BX_DOL_URL_ROOT . 'grid.php?o=' . $sObject . '&a=' . $sAction . '&object=' . $this->aParams['object'] . '&display=' . $this->aParams['display'];
+
+        if(isset($aForm['inputs']['object']))
+            $aForm['inputs']['object']['value'] = $this->aParams['object'];
+
+        return $aForm;
+    }
+
+    protected function getFormEdit($sAction, $sObject)
+    {
+        $aForm = $this->aForm;
+        $aForm['form_attrs']['id'] = 'adm-form-field-edit-' . $this->aParams['display'];
+        $aForm['form_attrs']['action'] = BX_DOL_URL_ROOT . 'grid.php?o=' . $sObject . '&a=' . $sAction . '&object=' . $this->aParams['object'] . '&display=' . $this->aParams['display'] . '&di_id=' . (int)$this->aField['di_id'];
+
+        foreach($aForm['inputs'] as $sKey => $aInput)
+            if(!in_array($sKey, array('type_display', 'controls')))
+                switch($sKey) {
+                    case 'value':
+                        $aForm['inputs'][$sKey]['value'] = $this->aField[$sKey];
+
+                        if(in_array($aForm['inputs']['type']['value'], array('select', 'select_multiple', 'radio_set', 'checkbox_set'))) {
+                            $sList = trim($this->getFieldValues($this->aField), BX_DATA_LISTS_KEY_PREFIX . ' ');
+
+                            $bUseForSets = false;
+                            if(in_array($aForm['inputs']['type']['value'], array('select_multiple', 'checkbox_set'))) {
+                                $aList = array();
+                                $this->oDb->getLists(array('type' => 'by_key', 'value' => $sList), $aList, false);
+
+                                $bUseForSets = (int)$aList['use_for_sets'] == 1;
+                            }
+
+                            $aPreValues = BxDolForm::getDataItems($sList, $bUseForSets);
+                            foreach($aPreValues as $mixedValue => $sTitle)
+                                 $aForm['inputs'][$sKey]['values'][] = array('key' => $mixedValue, 'value' => $sTitle);
+                        }
+                        break;
+                    case 'values':
+                        if(!empty($this->aField[$sKey]))
+                            $aForm['inputs'][$sKey]['value'] = $this->getFieldValues($this->aField);
+
+                        if(in_array($aForm['inputs']['type']['value'], array('select', 'select_multiple', 'radio_set', 'checkbox_set'))) {
+                            $aForm['inputs'][$sKey]['type'] = 'value';
+                            foreach($aForm['inputs'][$sKey]['values'] as $aValue)
+                                if($aValue['key'] == $aForm['inputs'][$sKey]['value']) {
+                                    $aForm['inputs'][$sKey]['value'] = BxDolStudioTemplate::getInstance()->parseHtmlByName('bx_a.html', array(
+                                        'href' => BX_DOL_URL_STUDIO . 'builder_forms.php?page=pre_values&list=' . trim($aValue['key'], BX_DATA_LISTS_KEY_PREFIX . ' '),
+                                        'title' =>  _t('_adm_form_txt_field_values_manage'),
+                                        'bx_repeat:attrs' => array(),
+                                        'content' => $aValue['value']
+                                    ));
+                                    break;
+                                }
+                            unset($aForm['inputs'][$sKey]['values'], $aForm['inputs'][$sKey]['db']);
+                        }
+                        break;
+                    case 'checked':
+                        $aForm['inputs'][$sKey]['checked'] = (int)$this->aField[$sKey];
+                        break;
+                    case 'collapsed':
+                        $aForm['inputs'][$sKey]['checked'] = (int)$this->aField[$sKey];
+                        break;
+                    case 'required':
+                        $aForm['inputs'][$sKey]['checked'] = (int)$this->aField[$sKey];
+                        if($aForm['inputs'][$sKey]['checked'] == 1 && isset($aForm['inputs']['checker_func']))
+                            unset($aForm['inputs']['checker_func']['tr_attrs']['style'], $aForm['inputs']['checker_error']['tr_attrs']['style']);
+                        break;
+                    case 'checker_func':
+                        $aForm['inputs'][$sKey]['value'] = strtolower($this->aField[$sKey]);
+                        switch($aForm['inputs'][$sKey]['value']) {
+                            case 'length':
+                                unset($aForm['inputs']['checker_params_length_min']['tr_attrs']['style'], $aForm['inputs']['checker_params_length_max']['tr_attrs']['style']);
+                                break;
+                            case 'preg':
+                                unset($aForm['inputs']['checker_params_preg']['tr_attrs']['style']);
+                                break;
+                        }
+                        break;
+                    case 'checker_params_length_min':
+                        $aParams = unserialize($this->aField['checker_params']);
+                        $aForm['inputs'][$sKey]['value'] = isset($aParams['min']) ? (int)$aParams['min'] : 0;
+                        break;
+                    case 'checker_params_length_max':
+                        $aParams = unserialize($this->aField['checker_params']);
+                        $aForm['inputs'][$sKey]['value'] = isset($aParams['max']) ? (int)$aParams['max'] : 0;
+                        break;
+                    case 'checker_params_preg':
+                        $aParams = unserialize($this->aField['checker_params']);
+                        $aForm['inputs'][$sKey]['value'] = isset($aParams['preg']) ? $aParams['preg'] : '';
+                        break;
+                    case 'attrs_min':
+                        $aParams = unserialize($this->aField['attrs']);
+                        $aForm['inputs'][$sKey]['value'] = isset($aParams['min']) ? (int)$aParams['min'] : 0;
+                        break;
+                    case 'attrs_max':
+                        $aParams = unserialize($this->aField['attrs']);
+                        $aForm['inputs'][$sKey]['value'] = isset($aParams['max']) ? (int)$aParams['max'] : 100;
+                        break;
+                    case 'attrs_step':
+                        $aParams = unserialize($this->aField['attrs']);
+                        $aForm['inputs'][$sKey]['value'] = isset($aParams['step']) ? (int)$aParams['step'] : 1;
+                        break;
+                    case 'attrs_src':
+                        $aParams = unserialize($this->aField['attrs']);
+                        $aForm['inputs'][$sKey]['value'] = isset($aParams['src']) ? $aParams['src'] : '';
+                        break;
+                    default:
+                        $aForm['inputs'][$sKey]['value'] = $this->aField[$sKey];
+                }
+
+        if(array_key_exists($aForm['inputs']['type']['value'], $this->aTypesRelated)) {
+           $aForm['inputs']['type'] = $this->getFieldTypesSelector('type', $aForm['inputs']['type']['value'], true);
+           unset($aForm['inputs']['type_display']);
+        }
+
+        $aForm['inputs']['controls'][0]['value'] = _t('_adm_form_btn_field_save');
+
+        return $aForm;
+    }
+
+    protected function getFieldTypes($sRelatedTo = '')
+    {
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aTypesList = $sRelatedTo != '' && isset($this->aTypesRelated[$sRelatedTo]) ? $this->aTypesRelated[$sRelatedTo]['types'] : $this->aTypes;
+
+        $aTypes = array();
+        foreach($aTypesList as $sType)
+            $aTypes[$sType] = _t($this->sTypeTitlePrefix . $sType);
+
+        asort($aTypes);
+
+        $aResult = array();
+        foreach($aTypes as $sName => $sTitle)
+            $aResult[] = array(
+                'key' => $sName,
+                'value' => $sTitle,
+                'style' => 'background-image:url(' . $oTemplate->getIconUrl('ui-' . $sName . '.png') . ')'
+            );
+
+        return $aResult;
+    }
+
+    protected function getFieldTypesSelector($sName, $sValue, $bRelated = false)
+    {
+        $bRelated = $bRelated && $sValue != '' && isset($this->aTypesRelated[$sValue]);
+
+        $aField = array(
+            'type' => 'select',
+            'name' => $sName,
+            'caption' => _t('_adm_form_txt_field_type_display'),
+            'info' => '',
+            'value' => $sValue,
+            'values' => $this->getFieldTypes($bRelated ? $sValue : ''),
+            'required' => '',
+            'attrs' => array(
+                'id' => 'bx-form-field-type',
+                'class' => 'bx-form-field-type',
+                'disabled' => 'disabled'
+            )
+        );
+
+        if($bRelated) {
+            $aField['db'] = array('pass' => 'Xss');
+            if((int)$this->aTypesRelated[$sValue]['reload_on_change'] == 1)
+                $aField['attrs']['onchange'] = $this->getJsObject() . '.onChangeType(' . $this->aField['di_id'] . ')';
+            unset($aField['attrs']['disabled']);
+        }
+
+        return $aField;
+    }
+
+    protected function getFieldName($sObject, $mixedCaption)
+    {
+        if($mixedCaption === false)
+            return $this->genFieldName($sObject);
+
+        $sName = get_mb_replace('/([^\d^\w]+)/u', '', $mixedCaption);
+        if(empty($sName))
+            return $this->genFieldName($sObject);
+
+        $sName = get_mb_replace('/([^\d^\w^\s]+)/u', '', $mixedCaption);
+        $sName = $this->getSystemName(trim($sName));
+        if($this->oDb->isInput($sObject, $sName))
+            $sName = $this->genFieldName($sObject, $sName);
+
+        return $sName;
+    }
+
+    protected function getFieldValues($aField)
+    {
+        $mixedResult = null;
+
+        switch($aField['type']) {
+            case 'select':
+            case 'select_multiple':
+            case 'checkbox_set':
+            case 'radio_set':
+                $mixedResult = $this->aField['values'];
+                break;
+            case 'input_set':
+                $mixedResult = $this->aField['values'];
+                break;
+        }
+
+        return $mixedResult;
+    }
+
+    protected function getCheckerFields()
+    {
+        $aResult = array(
+            'checker_func' => array(
+                'type' => 'select',
+                'name' => 'checker_func',
+                'caption' => _t('_adm_form_txt_field_checker_func'),
+                'info' => '',
+                'value' => '',
+                'values' => array(
+                    array('key' => '', 'value' => _t('_adm_form_txt_field_checker_empty'))
+                ),
+                'required' => '1',
+                'attrs' => array(
+                    'id' => 'bx-form-field-type',
+                    'onchange' => $this->getJsObject() . ".onSelectChecker(this)"
+                ),
+                'tr_attrs' => array(
+                    'style' => 'display:none'
+                ),
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+                'checker' => array (
+                    'func' => 'avail',
+                    'params' => array(),
+                    'error' => _t('_adm_form_err_field_checker_func'),
+                ),
+            ),
+            'checker_params' => array(
+                'type' => 'hidden',
+                'name' => 'checker_params',
+                'value' => '',
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+            ),
+            'checker_params_length_min' => array(
+                'type' => 'text',
+                'name' => 'checker_params_length_min',
+                'caption' => _t('_adm_form_txt_field_checker_params_length_min'),
+                'info' => '',
+                'value' => '',
+                'required' => '1',
+                'tr_attrs' => array(
+                    'style' => 'display:none'
+                ),
+                'db' => array (
+                    'pass' => 'Int',
+                ),
+                'checker' => array (
+                    'func' => 'preg',
+                    'params' => array('/^[0-9]+$/'),
+                    'error' => _t('_adm_form_err_field_checker_params_length_min'),
+                ),
+            ),
+            'checker_params_length_max' => array(
+                'type' => 'text',
+                'name' => 'checker_params_length_max',
+                'caption' => _t('_adm_form_txt_field_checker_params_length_max'),
+                'info' => '',
+                'value' => '',
+                'required' => '1',
+                'tr_attrs' => array(
+                    'style' => 'display:none'
+                ),
+                'db' => array (
+                    'pass' => 'Int',
+                ),
+                'checker' => array (
+                    'func' => 'preg',
+                    'params' => array('/^[0-9]+$/'),
+                    'error' => _t('_adm_form_err_field_checker_params_length_max'),
+                ),
+            ),
+            'checker_params_preg' => array(
+                'type' => 'text',
+                'name' => 'checker_params_preg',
+                'caption' => _t('_adm_form_txt_field_checker_params_preg'),
+                'info' => _t('_adm_form_dsc_field_checker_params_preg'),
+                'value' => '',
+                'required' => '1',
+                'tr_attrs' => array(
+                    'style' => 'display:none'
+                ),
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+                'checker' => array (
+                    'func' => 'avail',
+                    'params' => array(),
+                    'error' => _t('_adm_form_err_field_checker_params_preg'),
+                ),
+            ),
+            'checker_error' => array(
+                'type' => 'text_translatable',
+                'name' => 'checker_error',
+                'caption' => _t('_adm_form_txt_field_checker_error'),
+                'info' => '',
+                'value' => '_sys_form_txt_field_checker_error',
+                'required' => '1',
+                'tr_attrs' => array(
+                    'style' => 'display:none'
+                ),
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+                'checker' => array (
+                    'func' => 'avail',
+                    'params' => array(),
+                    'error' => _t('_adm_form_err_field_checker_error'),
+                ),
+            ),
+        );
+
+        foreach($this->aCheckFunctions as $sCheckFunction)
+            $aResult['checker_func']['values'][] = array('key' => $sCheckFunction, 'value' => _t('_adm_form_txt_field_checker_' . $sCheckFunction));
+
+        return $aResult;
+    }
+
+    protected function updateCheckerFields(&$oForm)
+    {
+            if((int)$oForm->getCleanValue('required') == 0)
+                $this->unsetCheckerFields($oForm);
+            else {
+                unset($oForm->aInputs['checker_func']['tr_attrs']['style'], $oForm->aInputs['checker_error']['tr_attrs']['style']);
+                switch($oForm->getCleanValue('checker_func')) {
+                    case 'length':
+                        unset($oForm->aInputs['checker_params_length_min']['tr_attrs']['style'], $oForm->aInputs['checker_params_length_max']['tr_attrs']['style']);
+                        $this->unsetCheckerFields($oForm, 'preg');
+                        break;
+                    case 'preg':
+                        unset($oForm->aInputs['checker_params_preg']['tr_attrs']['style']);
+                        $this->unsetCheckerFields($oForm, 'length');
+                        break;
+                    default:
+                        $this->unsetCheckerFields($oForm, 'params');
+                }
+            }
+    }
+
+    protected function unsetCheckerFields(&$oForm, $sCheckerFunc = 'all')
+    {
+        switch($sCheckerFunc) {
+            case 'length':
+                unset(
+                    $oForm->aInputs['checker_params_length_min'],
+                    $oForm->aInputs['checker_params_length_max']
+                );
+                break;
+            case 'preg':
+                unset($oForm->aInputs['checker_params_preg']);
+                break;
+            case 'params':
+                unset(
+                    $oForm->aInputs['checker_params_length_min'],
+                    $oForm->aInputs['checker_params_length_max'],
+                    $oForm->aInputs['checker_params_preg']
+                );
+                break;
+            case 'all':
+                unset(
+                    $oForm->aInputs['checker_func'],
+                    $oForm->aInputs['checker_params_length_min'],
+                    $oForm->aInputs['checker_params_length_max'],
+                    $oForm->aInputs['checker_params_preg'],
+                    $oForm->aInputs['checker_error']
+                );
+        }
+    }
+
+    protected function genFieldName($sObject, $sPrefix = 'field')
+    {
+        $aFields = array();
+        $this->oDb->getInputs(array('type' => 'by_object_name_filter', 'object' => $sObject, 'name_filter' => $sPrefix . '%'), $aFields, false);
+
+        for($iIndex = 1; true; $iIndex++)
+            if(!in_array($sPrefix . $iIndex, $aFields))
+                break;
+
+        return $sPrefix . $iIndex;
+    }
+
+    protected function onSubmitField(&$oForm)
+    {
+        //--- Process field values.
+        if(isset($oForm->aInputs['values']['db'])) {
+            $sValues = $oForm->getCleanValue('values');
+            if(is_string($sValues) && strpos($sValues, BX_DATA_LISTS_KEY_PREFIX) === false)
+                BxDolForm::setSubmittedValue('values', serialize(explode("\n", $sValues)), $oForm->aFormAttrs['method']);
+        }
+
+        //--- Process field 'html' flag.
+        if(isset($oForm->aInputs['html'])) {
+            $iHtml = (int)$oForm->getCleanValue('html');
+            BxDolForm::setSubmittedValue('db_pass', $iHtml == 0 ? 'Xss' : 'XssHtml', $oForm->aFormAttrs['method']);
+        }
+
+        //--- Process field checker.
+        $aCheckerParams = array();
+        if(isset($oForm->aInputs['checker_params_length_min'], $oForm->aInputs['checker_params_length_max'])) {
+            $aCheckerParams['min'] = $oForm->getCleanValue('checker_params_length_min');
+            $aCheckerParams['max'] = $oForm->getCleanValue('checker_params_length_max');
+        }
+        if(isset($oForm->aInputs['checker_params_preg']))
+            $aCheckerParams['preg'] = $oForm->getCleanValue('checker_params_preg');
+
+        unset($oForm->aInputs['checker_params_length_min'], $oForm->aInputs['checker_params_length_max'], $oForm->aInputs['checker_params_preg']);
+        BxDolForm::setSubmittedValue('checker_params', !empty($aCheckerParams) ? serialize($aCheckerParams) : '', $oForm->aFormAttrs['method']);
+
+        //--- Process field attrs.
+        $aAttrs = array();
+        if(isset($oForm->aInputs['attrs_min'], $oForm->aInputs['attrs_max'], $oForm->aInputs['attrs_step'])) {
+            $aAttrs['min'] = $oForm->getCleanValue('attrs_min');
+            $aAttrs['max'] = $oForm->getCleanValue('attrs_max');
+            $aAttrs['step'] = $oForm->getCleanValue('attrs_step');
+        } else if(isset($oForm->aInputs['attrs_src']))
+            $aAttrs['src'] = $oForm->getCleanValue('attrs_src');
+
+        unset($oForm->aInputs['attrs_min'], $oForm->aInputs['attrs_max'], $oForm->aInputs['attrs_step'], $oForm->aInputs['attrs_src']);
+        BxDolForm::setSubmittedValue('attrs', serialize($aAttrs), $oForm->aFormAttrs['method']);
+    }
+}
+
+class BxBaseStudioFormsFieldBlockHeader extends BxBaseStudioFormsField
+{
+    protected $sType = 'block_header';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aForm = array(
+            'form_attrs' => array(
+                'id' => '',
+                'action' => '',
+                'method' => 'post'
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_form_inputs',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'module' => array(
+                    'type' => 'hidden',
+                    'name' => 'module',
+                    'caption' => _t('_adm_form_txt_field_module'),
+                    'value' => 'custom',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'object' => array(
+                    'type' => 'hidden',
+                    'name' => 'object',
+                    'caption' => _t('_adm_form_txt_field_object'),
+                    'value' => '',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'name' => array(
+                    'type' => 'hidden',
+                    'name' => 'name',
+                	'caption' => _t('_adm_form_txt_field_name'),
+                    'value' => '',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    )
+                ),
+                'type' => array(
+                    'type' => 'hidden',
+                    'name' => 'type',
+                    'value' => $this->sType,
+                    'db' => array (
+                        'pass' => 'Xss',
+                    )
+                ),
+                'db_pass' => array(
+                    'type' => 'hidden',
+                    'name' => 'db_pass',
+                    'value' => $this->sDbPass,
+                    'db' => array (
+                        'pass' => 'Xss',
+                    )
+                ),
+                'type_display' => $this->getFieldTypesSelector('type_display', $this->sType),
+                'caption_system' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'caption_system',
+                    'caption' => _t('_adm_form_txt_field_caption_system'),
+                    'info' => _t('_adm_form_dsc_field_caption_system'),
+                    'value' => '_sys_form_txt_field',
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'LengthTranslatable',
+                        'params' => array(3,100, 'caption_system'),
+                        'error' => _t('_adm_form_err_field_caption_system'),
+                    ),
+                ),
+                'caption' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'caption',
+                    'caption' => _t('_adm_form_txt_field_caption'),
+                    'info' => _t('_adm_form_dsc_field_caption_block_header'),
+                    'value' => '_sys_form_txt_field',
+                    'required' => '',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    )
+                ),
+                'collapsed' => array(
+                    'type' => 'checkbox',
+                    'name' => 'collapsed',
+                    'caption' => _t('_adm_form_txt_field_collapsed'),
+                    'info' => _t('_adm_form_dsc_field_collapsed'),
+                    'value' => '1',
+                    'required' => '0',
+                    'db' => array (
+                        'pass' => 'Int',
+                    )
+                ),
+                'controls' => array(
+                    'name' => 'controls',
+                    'type' => 'input_set',
+                    array(
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_adm_form_btn_field_add'),
+                    ),
+                    array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_adm_form_btn_field_cancel'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    )
+                )
+            )
+        );
+    }
+}
+
+class BxBaseStudioFormsFieldValue extends BxBaseStudioFormsFieldBlockHeader
+{
+    protected $sType = 'value';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aForm['inputs']['caption']['info'] = _t('_adm_form_dsc_field_caption');
+
+        $aFields = array(
+            'value' => array(
+                'type' => 'text',
+                'name' => 'value',
+                'caption' => _t('_adm_form_txt_field_value_custom_text'),
+                'info' => '',
+                'value' => '',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Xss',
+                )
+            ),
+        );
+        $this->aForm['inputs'] = $this->addInArray($this->aForm['inputs'], 'caption', $aFields);
+
+        unset(
+            $this->aForm['inputs']['collapsed']
+        );
+    }
+}
+
+class BxBaseStudioFormsFieldText extends BxBaseStudioFormsFieldBlockHeader
+{
+    protected $sType = 'text';
+    protected $aCheckFunctions = array('avail', 'length', 'preg', 'email');
+    protected $sDbPass = 'Xss';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aParams['table_alter'] = true;
+        $this->aParams['table_field_type'] = 'varchar(255)';
+
+        $this->aForm['inputs']['caption']['info'] = _t('_adm_form_dsc_field_caption');
+
+        $aFields = array(
+            'value' => array(
+                'type' => 'text',
+                'name' => 'value',
+                'caption' => _t('_adm_form_txt_field_value_default'),
+                'info' => '',
+                'value' => '',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Xss',
+                )
+            ),
+            'info' => array(
+                'type' => 'textarea_translatable',
+                'name' => 'info',
+                'caption' => _t('_adm_form_txt_field_info'),
+                'info' => _t('_adm_form_dsc_field_info'),
+                'value' => '_sys_form_txt_field',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Xss',
+                )
+            ),
+            'required' => array(
+                'type' => 'switcher',
+                'name' => 'required',
+                'caption' => _t('_adm_form_txt_field_required'),
+                'info' => _t('_adm_form_dsc_field_required'),
+                'value' => '1',
+                'required' => '0',
+                'attrs' => array(
+                    'id' => 'bx-form-field-required',
+                    'onchange' => $this->getJsObject() . ".onCheckRequired(this)"
+                ),
+                'db' => array (
+                    'pass' => 'Int',
+                )
+            )
+        );
+
+        $aFields = array_merge($aFields, $this->getCheckerFields());
+
+        $this->aForm['inputs'] = $this->addInArray($this->aForm['inputs'], 'caption', $aFields);
+        unset($this->aForm['inputs']['collapsed']);
+    }
+}
+
+class BxBaseStudioFormsFieldPassword extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'password';
+    protected $aCheckFunctions = array('avail', 'length', 'preg');
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+    }
+}
+
+class BxBaseStudioFormsFieldTextarea extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'textarea';
+    protected $aCheckFunctions = array('avail', 'length', 'preg');
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aParams['table_field_type'] = 'text';
+
+        $this->aForm['inputs']['value']['type'] = $this->sType;
+
+        $aFields = array(
+            'html' => array(
+                'type' => 'select',
+                'name' => 'html',
+                'caption' => _t('_adm_form_txt_field_html'),
+                'info' => _t('_adm_form_dsc_field_html'),
+                'value' => '0',
+                'values' => array(
+                    array('key' => '0', 'value' => _t('_adm_form_txt_field_html_none')),
+                    array('key' => '1', 'value' => _t('_adm_form_txt_field_html_standard')),
+                    array('key' => '2', 'value' => _t('_adm_form_txt_field_html_full')),
+                    array('key' => '3', 'value' => _t('_adm_form_txt_field_html_mini')),
+                ),
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Int',
+                )
+            ),
+        );
+
+        $this->aForm['inputs'] = $this->addInArray($this->aForm['inputs'], 'info', $aFields);
+    }
+}
+
+class BxBaseStudioFormsFieldDatepicker extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'datepicker';
+    protected $aCheckFunctions = array('date');
+    protected $sDbPass = 'Date';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aParams['table_field_type'] = 'int(11)';
+
+        $this->aForm['inputs']['value']['type'] = $this->sType;
+        $this->aForm['inputs']['value']['db']['pass'] = 'Date';
+    }
+}
+
+class BxBaseStudioFormsFieldDatetime extends BxBaseStudioFormsFieldDatepicker
+{
+    protected $sType = 'datetime';
+    protected $aCheckFunctions = array('date_time');
+    protected $sDbPass = 'DateTime';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aForm['inputs']['value']['db']['pass'] = 'DateTime';
+    }
+}
+
+class BxBaseStudioFormsFieldCheckbox extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'checkbox';
+    protected $aCheckFunctions = array('avail', 'length', 'preg');
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aForm['inputs']['value']['type'] = 'hidden';
+        $this->aForm['inputs']['value']['caption'] = _t('_adm_form_txt_field_value_checkbox');
+        $this->aForm['inputs']['value']['value'] = '1';
+        $this->aForm['inputs']['value']['info'] = _t('_adm_form_dsc_field_value_checkbox');
+
+        $aFields = array(
+            'checked' => array(
+                'type' => 'checkbox',
+                'name' => 'checked',
+                'caption' => _t('_adm_form_txt_field_checked'),
+                'value' => '1',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Int',
+                )
+            )
+        );
+        $this->aForm['inputs'] = $this->addInArray($this->aForm['inputs'], 'value', $aFields);
+    }
+}
+
+class BxBaseStudioFormsFieldSwitcher extends BxBaseStudioFormsFieldCheckbox
+{
+    protected $sType = 'switcher';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aForm['inputs']['checked']['caption'] = _t('_adm_form_txt_field_checked_switcher');
+    }
+}
+
+class BxBaseStudioFormsFieldFile extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'file';
+    protected $aCheckFunctions = array('avail', 'length', 'preg');
+    protected $sDbPass = '';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        unset($this->aForm['inputs']['value']);
+    }
+}
+
+class BxBaseStudioFormsFieldFiles extends BxBaseStudioFormsFieldFile
+{
+    protected $sType = 'files';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+    }
+}
+
+class BxBaseStudioFormsFieldNumber extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'number';
+    protected $aCheckFunctions = array('avail', 'length', 'preg');
+    protected $sDbPass = 'Int';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aParams['table_field_type'] = 'int(11)';
+
+        $this->aForm['inputs']['value']['db']['pass'] = 'Int';
+        $this->aForm['inputs']['value']['checker'] = array (
+            'func' => 'preg',
+            'params' => array('/^\d*?$/'),
+            'error' => _t('_adm_form_err_field_value_number'),
+        );
+    }
+}
+
+class BxBaseStudioFormsFieldSlider extends BxBaseStudioFormsFieldNumber
+{
+    protected $sType = 'slider';
+    protected $aCheckFunctions = array('avail', 'length');
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $aFields = array(
+            'attrs' => array(
+                'type' => 'hidden',
+                'name' => 'attrs',
+                'value' => '',
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+            ),
+            'attrs_min' => array(
+                'type' => 'text',
+                'name' => 'attrs_min',
+                'caption' => _t('_adm_form_txt_field_attrs_min'),
+                'info' => _t('_adm_form_dsc_field_attrs_min'),
+                'value' => '1',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Int',
+                )
+            ),
+            'attrs_max' => array(
+                'type' => 'text',
+                'name' => 'attrs_max',
+                'caption' => _t('_adm_form_txt_field_attrs_max'),
+                'info' => _t('_adm_form_dsc_field_attrs_max'),
+                'value' => '100',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Int',
+                )
+            ),
+            'attrs_step' => array(
+                'type' => 'text',
+                'name' => 'attrs_step',
+                'caption' => _t('_adm_form_txt_field_attrs_step'),
+                'info' => _t('_adm_form_dsc_field_attrs_step'),
+                'value' => '1',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Int',
+                )
+            ),
+        );
+        $this->aForm['inputs'] = $this->addInArray($this->aForm['inputs'], 'value', $aFields);
+    }
+}
+
+class BxBaseStudioFormsFieldDoublerange extends BxBaseStudioFormsFieldSlider
+{
+    protected $sType = 'doublerange';
+    protected $aCheckFunctions = array('avail', 'length');
+    protected $sDbPass = 'Xss';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aParams['table_field_type'] = 'varchar(255)';
+
+        $this->aForm['inputs']['value']['db']['pass'] = 'Xss';
+        $this->aForm['inputs']['value']['checker'] = array (
+            'func' => 'preg',
+            'params' => array('/^(\d+-\d+)?$/'),
+            'error' => _t('_adm_form_err_field_value_doublerange'),
+        );
+    }
+}
+
+class BxBaseStudioFormsFieldHidden extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'hidden';
+    protected $aCheckFunctions = array('avail', 'length', 'preg', 'date', 'date_time', 'email');
+    protected $sDbPass = '';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        unset(
+            $this->aForm['inputs']['caption'],
+            $this->aForm['inputs']['info'],
+            $this->aForm['inputs']['required'],
+            $this->aForm['inputs']['checker_func'],
+            $this->aForm['inputs']['checker_params'],
+            $this->aForm['inputs']['checker_params_length_min'],
+            $this->aForm['inputs']['checker_params_length_max'],
+            $this->aForm['inputs']['checker_params_preg'],
+            $this->aForm['inputs']['checker_error']
+        );
+    }
+}
+
+class BxBaseStudioFormsFieldButton extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'button';
+    protected $sDbPass = '';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aParams['table_alter'] = false;
+
+        $this->aForm['inputs']['value']['type'] = 'text_translatable';
+        $this->aForm['inputs']['value']['caption'] = _t('_adm_form_txt_field_value_button');
+        $this->aForm['inputs']['value']['info'] = _t('_adm_form_dsc_field_value_button');
+        $this->aForm['inputs']['value']['value'] = '_sys_form_txt_field';
+
+        unset(
+            $this->aForm['inputs']['caption'],
+            $this->aForm['inputs']['info'],
+            $this->aForm['inputs']['required'],
+            $this->aForm['inputs']['checker_func'],
+            $this->aForm['inputs']['checker_params'],
+            $this->aForm['inputs']['checker_params_length_min'],
+            $this->aForm['inputs']['checker_params_length_max'],
+            $this->aForm['inputs']['checker_params_preg'],
+            $this->aForm['inputs']['checker_error']
+        );
+    }
+}
+
+class BxBaseStudioFormsFieldReset extends BxBaseStudioFormsFieldButton
+{
+    protected $sType = 'reset';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aForm['inputs']['value']['info'] = _t('_adm_form_dsc_field_value_reset');
+    }
+}
+
+class BxBaseStudioFormsFieldSubmit extends BxBaseStudioFormsFieldButton
+{
+    protected $sType = 'submit';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aForm['inputs']['value']['info'] = _t('_adm_form_dsc_field_value_submit');
+    }
+}
+
+class BxBaseStudioFormsFieldImage extends BxBaseStudioFormsFieldButton
+{
+    protected $sType = 'image';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $aFields = array(
+            'attrs' => array(
+                'type' => 'hidden',
+                'name' => 'attrs',
+                'value' => '',
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+            ),
+            'attrs_src' => array(
+                'type' => 'text',
+                'name' => 'attrs_src',
+                'caption' => _t('_adm_form_txt_field_attrs_src'),
+                'info' => _t('_adm_form_dsc_field_attrs_src'),
+                'value' => '',
+                'required' => '0',
+                'db' => array (
+                    'pass' => 'Xss',
+                )
+            ),
+        );
+        $this->aForm['inputs'] = $this->addInArray($this->aForm['inputs'], 'value', $aFields);
+
+        unset(
+            $this->aForm['inputs']['value']
+        );
+    }
+}
+
+class BxBaseStudioFormsFieldSelect extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'select';
+    protected $aCheckFunctions = array('avail', 'length', 'preg');
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aForm['inputs']['value']['type'] = 'select';
+        $this->aForm['inputs']['value']['values'] = array(
+            array('key' => '', 'value' => _t('_adm_form_txt_field_value_select_value'))
+        );
+        $this->aForm['inputs']['value']['attrs'] = array(
+            'id' => 'adm-form-field-add-value'
+        );
+
+        $aFields = array(
+            'values' => array(
+                'type' => 'select',
+                'name' => 'values',
+                'caption' => _t('_adm_form_txt_field_values'),
+                'info' => '',
+                'value' => '',
+                'values' => array(
+                    array('key' => '', 'value' => _t('_adm_form_txt_field_values_select_list'))
+                ),
+                'required' => '1',
+                'attrs' => array(
+                    'onChange' => $this->getJsObject() . ".onChangeValues(0, this)"
+                ),
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+                'checker' => array (
+                    'func' => 'avail',
+                    'params' => array(),
+                    'error' => _t('_adm_form_err_field_values'),
+                ),
+            )
+        );
+
+        $aLists = array();
+        $this->oDb->getLists(array('type' => 'all'), $aLists, false);
+        foreach($aLists as $aList)
+            $aFields['values']['values'][] = array('key' => BX_DATA_LISTS_KEY_PREFIX . $aList['key'], 'value' => _t($aList['title']));
+
+        $this->aForm['inputs'] = $this->addInArray($this->aForm['inputs'], 'caption', $aFields);
+    }
+}
+
+class BxBaseStudioFormsFieldRadioSet extends BxBaseStudioFormsFieldSelect
+{
+    protected $sType = 'radio_set';
+    protected $aCheckFunctions = array('avail', 'length', 'preg');
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+    }
+}
+
+class BxBaseStudioFormsFieldSelectMultiple extends BxBaseStudioFormsFieldSelect
+{
+    protected $sType = 'select_multiple';
+    protected $aCheckFunctions = array('avail', 'length', 'preg');
+    protected $sDbPass = 'Set';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aParams['table_field_type'] = 'int(11)';
+
+        $this->aForm['inputs']['value']['type'] = 'select_multiple';
+        $this->aForm['inputs']['value']['values'] = array();
+        $this->aForm['inputs']['value']['db']['pass'] = 'Set';
+        $this->aForm['inputs']['values']['values'] = array(
+            array('key' => '', 'value' => _t('_adm_form_txt_field_values_select_list'))
+        );
+        $this->aForm['inputs']['values']['attrs']['onChange'] = $this->getJsObject() . ".onChangeValues(1, this)";
+
+        $aLists = array();
+        $this->oDb->getLists(array('type' => 'all_for_sets'), $aLists, false);
+        foreach($aLists as $aList)
+            $this->aForm['inputs']['values']['values'][] = array('key' => BX_DATA_LISTS_KEY_PREFIX . $aList['key'], 'value' => _t($aList['title']));
+    }
+}
+
+class BxBaseStudioFormsFieldCheckboxSet extends BxBaseStudioFormsFieldSelectMultiple
+{
+    protected $sType = 'checkbox_set';
+    protected $aCheckFunctions = array('avail', 'length', 'preg');
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+    }
+}
+
+class BxBaseStudioFormsFieldCustom extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'custom';
+    protected $sDbPass = '';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        unset(
+            $this->aForm['inputs']['required'],
+            $this->aForm['inputs']['checker_func'],
+            $this->aForm['inputs']['checker_params'],
+            $this->aForm['inputs']['checker_params_length_min'],
+            $this->aForm['inputs']['checker_params_length_max'],
+            $this->aForm['inputs']['checker_params_preg'],
+            $this->aForm['inputs']['checker_error']
+        );
+    }
+}
+
+class BxBaseStudioFormsFieldInputSet extends BxBaseStudioFormsFieldCustom
+{
+    protected $sType = 'input_set';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aParams['table_alter'] = false;
+
+        $aFields = array(
+            'values' => array(
+                'type' => 'value',
+                'name' => 'values',
+                'caption' => _t('_adm_form_txt_field_values'),
+                'value' => ''
+            )
+        );
+        $this->aForm['inputs'] = $this->addInArray($this->aForm['inputs'], 'value', $aFields);
+
+        unset(
+            $this->aForm['inputs']['value']
+        );
+    }
+}
+
+class BxBaseStudioFormsFieldCaptcha extends BxBaseStudioFormsFieldText
+{
+    protected $sType = 'captcha';
+    protected $aCheckFunctions = array('captcha');
+    protected $sDbPass = '';
+
+    function __construct($aParams = array(), $aField = array())
+    {
+        parent::__construct($aParams, $aField);
+
+        $this->aParams['table_alter'] = false;
+
+        $this->aForm['inputs']['required'] = array(
+            'type' => 'hidden',
+            'name' => 'required',
+            'value' => '1',
+            'db' => array (
+                'pass' => 'Int',
+            )
+        );
+
+        $this->aForm['inputs']['checker_func']['type'] = 'hidden';
+        $this->aForm['inputs']['checker_func']['value'] = 'captcha';
+        $this->aForm['inputs']['checker_error']['info'] = _t('_adm_form_dsc_field_checker_error_captcha');
+
+        unset(
+            $this->aForm['inputs']['value'],
+            $this->aForm['inputs']['checker_func']['tr_attrs']['style'],
+            $this->aForm['inputs']['checker_error']['tr_attrs']['style']
+        );
+    }
+}
+
+/** @} */

+ 353 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioFormsPreLists.php

@@ -0,0 +1,353 @@
+<?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    TridentView Trident Studio Representation classes
+ * @ingroup     TridentStudio
+ * @{
+ */
+
+class BxBaseStudioFormsPreLists extends BxDolStudioFormsPreLists
+{
+    protected $sUrlViewValues;
+
+    function __construct($aOptions, $oTemplate = false)
+    {
+        parent::__construct($aOptions, $oTemplate);
+
+        $this->_aOptions['actions_single']['edit']['attr']['title'] = _t('_adm_form_btn_pre_lists_edit');
+        $this->_aOptions['actions_single']['delete']['attr']['title'] = _t('_adm_form_btn_pre_lists_delete');
+
+        $this->sUrlViewValues = BX_DOL_URL_STUDIO . 'builder_forms.php?page=pre_values&module=%s&list=%s';
+    }
+
+    public function performActionAdd()
+    {
+        $sAction = 'add';
+
+        bx_import('BxDolStudioUtils');
+        bx_import('BxTemplStudioFormView');
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-form-pre-list-create',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction,
+                'method' => BX_DOL_STUDIO_METHOD_DEFAULT
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_form_pre_lists',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'module' => array(
+                    'type' => 'hidden',
+                    'name' => 'module',
+                    'value' => BX_DOL_STUDIO_MODULE_CUSTOM,
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'use_for_sets' => array(
+                    'type' => 'hidden',
+                    'name' => 'use_for_sets',
+                    'value' => 1,
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'title' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title',
+                    'caption' => _t('_adm_form_txt_pre_lists_title'),
+                    'info' => _t('_adm_form_dsc_pre_lists_title'),
+                    'value' => '_adm_form_txt_pre_lists',
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'LengthTranslatable',
+                        'params' => array(3,100, 'title'),
+                        'error' => _t('_adm_form_err_pre_lists_title'),
+                    ),
+                ),
+                'controls' => array(
+                    'name' => 'controls',
+                    'type' => 'input_set',
+                    array(
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_adm_form_btn_pre_lists_add'),
+                    ),
+                    array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_adm_form_btn_pre_lists_cancel'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    )
+                )
+            )
+        );
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $oLanguage = BxDolStudioLanguagesUtils::getInstance();
+            $sLanguage = $oLanguage->getCurrentLangName(false);
+
+            $sKey = BxDolForm::getSubmittedValue('title-' . $sLanguage, $aForm['form_attrs']['method']);
+            $sKey = uriGenerate($sKey, 'sys_form_pre_lists', 'key', 'key');
+
+            $iId = (int)$oForm->insert(array('key' => $sKey));
+            if($iId != 0)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $iId);
+            else
+                $aRes = array('msg' => _t('_adm_form_err_pre_lists_create'));
+
+            $this->_echoResultJson($aRes, true);
+        } 
+        else {
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-form-pre-list-create-popup', _t('_adm_form_txt_pre_lists_create_popup'), $this->_oTemplate->parseHtmlByName('form_add_list.html', array(
+                'form_id' => $aForm['form_attrs']['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()
+    {
+        $sAction = 'edit';
+
+        $aList = $this->_getItem('getLists');
+        if($aList === false) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        bx_import('BxTemplStudioFormView');
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-form-list-edit',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction,
+                'method' => BX_DOL_STUDIO_METHOD_DEFAULT
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_form_pre_lists',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'id' => array(
+                    'type' => 'hidden',
+                    'name' => 'id',
+                    'value' => $aList['id'],
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'title' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title',
+                    'caption' => _t('_adm_form_txt_pre_lists_title'),
+                    'info' => _t('_adm_form_dsc_pre_lists_title'),
+                    'value' => $aList['title'],
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'LengthTranslatable',
+                        'params' => array(3,100, 'title'),
+                        'error' => _t('_adm_form_err_pre_lists_title'),
+                    ),
+                ),
+                'controls' => array(
+                    'name' => 'controls',
+                    'type' => 'input_set',
+                    array(
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_adm_form_btn_pre_lists_save'),
+                    ),
+                    array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_adm_form_btn_pre_lists_cancel'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    )
+                )
+            )
+        );
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            if($oForm->update($aList['id']) !== false)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $aList['id']);
+            else
+                $aRes = array('msg' => _t('_adm_form_err_pre_lists_edit'));
+
+            $this->_echoResultJson($aRes, true);
+        }
+        else {
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-form-pre-list-edit-popup', _t('_adm_form_txt_pre_lists_edit_popup', _t($aList['title'])), $this->_oTemplate->parseHtmlByName('form_add_list.html', array(
+                'form_id' => $aForm['form_attrs']['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            )));
+
+            $this->_echoResultJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))), true);
+        }
+    }
+
+    public function performActionDelete()
+    {
+        $sAction = 'delete';
+
+        $iAffected = 0;
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $aIdsAffected = array ();
+        foreach($aIds as $iId) {
+            $aList = array();
+            $this->oDb->getLists(array('type' => 'by_id', 'value' => (int)$iId), $aList, false);
+            if(!is_array($aList) || empty($aList))
+                continue;
+
+            if(!$this->_canDelete($aList))
+                continue;
+
+            if(!$this->oDb->deleteValues(array('type' => 'by_key', 'value' => $aList['key'])) || (int)$this->_delete($iId) <= 0)
+                continue;
+
+            BxDolStudioLanguagesUtils::getInstance()->deleteLanguageString($aList['title']);
+
+            $aIdsAffected[] = $iId;
+            $iAffected++;
+        }
+
+        $this->_echoResultJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('msg' => _t('_adm_from_err_pre_lists_delete')));
+    }
+
+    function getJsObject()
+    {
+        return 'oBxDolStudioFormsPreLists';
+    }
+
+    function getCode($isDisplayHeader = true)
+    {
+        return $this->_oTemplate->parseHtmlByName('forms_lists.html', array(
+            'content' => parent::getCode($isDisplayHeader),
+            'js_object' => $this->getJsObject(),
+            'grid_object' => $this->_sObject,
+            'params_divider' => $this->sParamsDivider
+        ));
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oTemplate->addJs(array('jquery.form.min.js', 'forms_lists.js'));
+
+        $oForm = new BxTemplStudioFormView(array());
+        $oForm->addCssJs();
+    }
+
+    protected function _getCellValues ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $mixedValue = $this->_oTemplate->parseHtmlByName('bx_a.html', array(
+            'href' => sprintf($this->sUrlViewValues, $aRow['module'], $aRow['key']),
+            'title' => _t('_adm_form_txt_pre_lists_manage_values'),
+            'bx_repeat:attrs' => array(),
+            'content' => _t('_adm_form_txt_pre_lists_n_values', $aRow['values_count'])
+        ));
+
+        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 _getCellUseForSets ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $aChanger = array(
+            '0' => '_adm_form_txt_pre_lists_no',
+            '1' => '_adm_form_txt_pre_lists_yes'
+        );
+        $mixedValue = $this->_limitMaxLength(_t($aChanger[$mixedValue]), $sKey, $aField, $aRow, $this->_isDisplayPopupOnTextOverflow);
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getActionDelete ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if(!$this->_canDelete($aRow))
+            return '';
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+    protected function _getFilterControls ()
+    {
+        $sContent = parent::_getFilterControls();
+
+        $oForm = new BxTemplStudioFormView(array());
+
+        $aInputModules = array(
+            'type' => 'select',
+            'name' => 'module',
+            'attrs' => array(
+                'id' => 'bx-grid-module-' . $this->_sObject,
+                'onChange' => 'javascript:' . $this->getJsObject() . '.onChangeFilter()'
+            ),
+            'value' => '',
+            'values' => $this->getModules()
+        );
+
+        $aCounter = array();
+        $this->oDb->getLists(array('type' => 'counter_by_modules'), $aCounter, false);
+        foreach($aInputModules['values'] as $sKey => $sValue)
+                $aInputModules['values'][$sKey] = $aInputModules['values'][$sKey] . " (" . (isset($aCounter[$sKey]) ? $aCounter[$sKey] : "0") . ")";
+
+        $aInputModules['values'] = array_merge(array('' => _t('_adm_form_txt_all_modules')), $aInputModules['values']);
+
+        return  $oForm->genRow($aInputModules) . $sContent;
+    }
+
+    protected function _canDelete($aList)
+    {
+    	bx_import('BxDolStudioUtils');
+        return $aList['module'] == BX_DOL_STUDIO_MODULE_CUSTOM;
+    }
+}
+
+/** @} */

+ 148 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioNavigationImport.php

@@ -0,0 +1,148 @@
+<?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    TridentView Trident Studio Representation classes
+ * @ingroup     TridentStudio
+ * @{
+ */
+
+class BxBaseStudioNavigationImport extends BxDolStudioNavigationImport
+{
+    function __construct($aOptions, $oTemplate = false)
+    {
+        parent::__construct($aOptions, $oTemplate);
+
+        $this->_aOptions['actions_single']['import']['attr']['title'] = _t('_adm_nav_btn_items_import');
+    }
+
+    public function performActionImport()
+    {
+    	bx_import('BxDolStudioUtils');
+
+        $iAffected = 0;
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $aIdsAffected = array ();
+        foreach($aIds as $iId) {
+            $aItem = array();
+            $iItem = $this->oDb->getItems(array('type' => 'by_id', 'value' => (int)$iId), $aItem);
+            if($iItem != 1 || empty($aItem))
+                continue;
+
+            $mixedIcon = 0;
+            if(is_numeric($aItem['icon']) && (int)$aItem['icon'] != 0) {
+                $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+
+                if(($mixedIcon = $oStorage->storeFileFromStorage(array('id' => (int)$aItem['icon']), false, 0)) === false) {
+                    $this->_echoResultJson(array('msg' => _t('_adm_nav_err_items_icon_copy') . $oStorage->getErrorString()), true);
+                    return;
+                }
+
+                $oStorage->afterUploadCleanup($mixedIcon, 0);
+            }
+
+            $iIdImported = (int)$aItem['id'];
+            $sTitleKey = $aItem['title'];
+
+            unset($aItem['id']);
+            $aItem['set_name'] = $this->sSet;
+            $aItem['module'] = BX_DOL_STUDIO_MODULE_CUSTOM;
+            $aItem['title'] .= '_' . time();
+            $aItem['icon'] = $mixedIcon != 0 ? $mixedIcon : '';
+            $aItem['active'] = 1;
+            $aItem['order'] = $this->oDb->getItemOrderMax($this->sSet) + 1;
+
+            if(($iIdAdded = (int)$this->oDb->addItem($aItem)) == 0)
+                continue;
+
+            BxDolStudioLanguagesUtils::getInstance()->addLanguageString($aItem['title'], _t($sTitleKey));
+
+            $aIdsImported[] = $iIdImported;
+            $aIdsAdded[] = $iIdAdded;
+            $iAffected++;
+        }
+
+        $aResult = array('msg' => _t('_adm_nav_err_items_import'));
+        if($iAffected) {
+            $oGrid = BxDolGrid::getObjectInstance('sys_studio_nav_items');
+            if($oGrid !== false)
+                $aResult = array(
+                    'parent_grid' => $oGrid->getCode(false),
+                    'parent_blink' => $aIdsAdded,
+                    'disable' => $aIdsImported,
+                    'eval' => $this->getJsObject() . '.onImport(oData)'
+                );
+        }
+        $this->_echoResultJson($aResult);
+    }
+
+    function getJsObject()
+    {
+        return 'oBxDolStudioNavigationImport';
+    }
+
+    function getCode($isDisplayHeader = true)
+    {
+        return $this->_oTemplate->parseHtmlByName('nav_import.html', array(
+            'content' => parent::getCode($isDisplayHeader),
+            'js_object' => $this->getJsObject(),
+            'grid_object' => $this->_sObject,
+            'params_divider' => $this->sParamsDivider
+        ));
+    }
+
+    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 _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 _getActionDone ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        $a['attr']['onclick'] = "$('.bx-popup-applied:visible').dolPopupHide()";
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getFilterControls ()
+    {
+        parent::_getFilterControls();
+
+        $oForm = new BxTemplStudioFormView(array());
+
+        $aInputSets = array(
+            'type' => 'select',
+            'name' => 'set',
+            'attrs' => array(
+                'id' => 'bx-grid-set-' . $this->_sObject,
+                'onChange' => 'javascript:' . $this->getJsObject() . '.onChangeFilter()'
+            ),
+            'value' => '',
+            'values' => array()
+        );
+
+        $aSets = $aCounter = array();
+        $this->oDb->getSets(array('type' => 'all', 'except' => $this->sSet), $aSets, false);
+        $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) . $this->getModulesSelectAll('getItems') . $this->getSearchInput();
+    }
+}
+
+/** @} */

+ 795 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioNavigationItems.php

@@ -0,0 +1,795 @@
+<?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    TridentView Trident Studio Representation classes
+ * @ingroup     TridentStudio
+ * @{
+ */
+
+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()
+    {
+        $oGrid = BxDolGrid::getObjectInstance('sys_studio_nav_import');
+        if(!$oGrid)
+            return '';
+
+        $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'])) {
+                $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']);
+            }
+
+            $oPermalinks = BxDolPermalinks::getInstance();
+
+            $sLink = $oForm->getCleanValue('link');
+            $sLink = $oPermalinks->unpermalink($sLink);
+            BxDolForm::setSubmittedValue('link', $sLink, $oForm->aFormAttrs['method']);
+
+            $sName = $oPermalinks->getPageNameFromLink($sLink);
+            if($sName == '') {
+                $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)) {
+            	$sLink = $oForm->getCleanValue('link');
+            	if(empty($sLink))
+                	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']);
+            }
+
+            bx_import('BxDolStudioUtils');
+            $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 {
+            $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)) {
+                $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) {
+                $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']);
+
+            $sLink = $oForm->getCleanValue('link');
+            $sLink = BxDolPermalinks::getInstance()->unpermalink($sLink);
+            BxDolForm::setSubmittedValue('link', $sLink, $oForm->aFormAttrs['method']);
+
+            $sSubmenu = $oForm->getCleanValue('submenu_object');
+            if(!empty($sSubmenu)) {
+            	$sLink = $oForm->getCleanValue('link');
+            	if(empty($sLink))
+                	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']);
+            $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;
+        }
+
+		bx_import('BxDolStudioUtils');
+        $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']);
+
+        $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 {
+            $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)
+            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 = '')
+    {
+        $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'));
+
+        $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
+            $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);
+
+        $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())
+    {
+		bx_import('BxTemplStudioFormView');
+
+        $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) {
+                        $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
+                )
+            )
+        ));
+    }
+}
+
+/** @} */

+ 399 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioNavigationMenus.php

@@ -0,0 +1,399 @@
+<?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    TridentView Trident Studio Representation classes
+ * @ingroup     TridentStudio
+ * @{
+ */
+
+class BxBaseStudioNavigationMenus extends BxDolStudioNavigationMenus
+{
+    private $sCreateNew = 'sys_create_new';
+    protected $sUrlViewItems;
+
+    function __construct($aOptions, $oTemplate = false)
+    {
+        parent::__construct($aOptions, $oTemplate);
+
+        $this->_aOptions['actions_single']['edit']['attr']['title'] = _t('_adm_nav_btn_menus_edit');
+        $this->_aOptions['actions_single']['delete']['attr']['title'] = _t('_adm_nav_btn_menus_delete');
+
+        $this->sUrlViewItems = BX_DOL_URL_STUDIO . 'builder_menu.php?page=items&module=%s&set=%s';
+    }
+
+    public function performActionAdd()
+    {
+        $sAction = 'add';
+
+        $oForm = $this->_getFormObject($sAction);
+        if($oForm->isSubmitted() && isset($oForm->aInputs['set_name']))
+            $this->updateSetFields($oForm);
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+        	bx_import('BxDolStudioUtils');
+
+            $oLanguage = BxDolStudioLanguagesUtils::getInstance();
+            $sLanguage = $oLanguage->getCurrentLangName(false);
+
+            $sObject = BxDolForm::getSubmittedValue('title-' . $sLanguage, $oForm->aFormAttrs['method']);
+            $sObject = uriGenerate($sObject, 'sys_objects_menu', 'object', 'object');
+
+            //--- New Set Creation
+            if($oForm->getCleanValue('set_name') == $this->sCreateNew) {
+                $sSetTitleValue = $oForm->getCleanValue('set_title');
+                $sSetName = uriGenerate($sSetTitleValue, 'sys_menu_sets', 'set_name', 'set');
+                $sSetTitleKey = '_adm_nav_txt_set_' . $sSetName;
+
+                if($this->oDb->addSet(array('set_name' => $sSetName, 'module' => BX_DOL_STUDIO_MODULE_CUSTOM, 'title' => $sSetTitleKey, 'deletable' => 1))) {
+                    $oLanguage->addLanguageString($sSetTitleKey, $sSetTitleValue);
+
+                    BxDolForm::setSubmittedValue('set_name', $sSetName, $oForm->aFormAttrs['method']);
+                }
+            }
+            unset($oForm->aInputs['set_title']);
+
+            $iId = (int)$oForm->insert(array('object' => $sObject, 'module' => BX_DOL_STUDIO_MODULE_CUSTOM, 'deletable' => 1, 'active' => 1));
+            if($iId != 0)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $iId);
+            else
+                $aRes = array('msg' => _t('_adm_nav_err_menus_create'));
+
+            $this->_echoResultJson($aRes, true);
+        }
+        else {
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-nav-menu-create-popup', _t('_adm_nav_txt_menus_create_popup'), $this->_oTemplate->parseHtmlByName('nav_add_menu.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()
+    {
+        $sAction = 'edit';
+
+        $aMenu = $this->_getItem('getMenus');
+        if($aMenu === false) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $oForm = $this->_getFormObject($sAction, $aMenu);
+        if($oForm->isSubmitted() && isset($oForm->aInputs['set_name']))
+            $this->updateSetFields($oForm);
+
+        $oForm->initChecker();
+        if($oForm->isSubmittedAndValid()) {
+			bx_import('BxDolStudioUtils');
+        	
+            //--- New Set Creation
+            if($oForm->getCleanValue('set_name') == $this->sCreateNew) {
+                $sSetTitleValue = $oForm->getCleanValue('set_title');
+                $sSetName = 'custom_' . $this->getSystemName($sSetTitleValue);
+                $sSetTitleKey = '_adm_nav_txt_set_' . $sSetName;
+
+                if($this->oDb->addSet(array('set_name' => $sSetName, 'module' => BX_DOL_STUDIO_MODULE_CUSTOM, 'title' => $sSetTitleKey, 'deletable' => 1))) {
+                    BxDolStudioLanguagesUtils::getInstance()->addLanguageString($sSetTitleKey, $sSetTitleValue);
+
+                    BxDolForm::setSubmittedValue('set_name', $sSetName, $oForm->aFormAttrs['method']);
+                }
+            }
+            unset($oForm->aInputs['set_title']);
+
+            if($oForm->update($aMenu['id']) !== false)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $aMenu['id']);
+            else
+                $aRes = array('msg' => _t('_adm_nav_err_menus_edit'));
+
+            $this->_echoResultJson($aRes, true);
+        }
+        else {
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-nav-menu-edit-popup', _t('_adm_nav_txt_menus_edit_popup', _t($aMenu['title'])), $this->_oTemplate->parseHtmlByName('nav_add_menu.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 performActionDelete()
+    {
+        $iAffected = 0;
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $aIdsAffected = array ();
+        foreach($aIds as $iId) {
+            $aMenu = array();
+            $iMenu = $this->oDb->getMenus(array('type' => 'by_id', 'value' => (int)$iId), $aMenu);
+            if($iMenu != 1 || empty($aMenu))
+                continue;
+
+            if((int)$aMenu['deletable'] != 1)
+                continue;
+
+            if((int)$this->_delete($iId) <= 0)
+                continue;
+
+            BxDolStudioLanguagesUtils::getInstance()->deleteLanguageString($aMenu['title']);
+
+            $aIdsAffected[] = $iId;
+            $iAffected++;
+        }
+
+        $this->_echoResultJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('msg' => _t('_adm_nav_err_menus_delete')));
+    }
+
+    function getJsObject()
+    {
+        return 'oBxDolStudioNavigationMenus';
+    }
+
+    function getCode($isDisplayHeader = true)
+    {
+        return $this->_oTemplate->parseHtmlByName('nav_menus.html', array(
+            'content' => parent::getCode($isDisplayHeader),
+            'js_object' => $this->getJsObject(),
+            'grid_object' => $this->_sObject,
+            'params_divider' => $this->sParamsDivider
+        ));
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oTemplate->addJs(array('jquery.form.min.js', 'navigation_menus.js'));
+
+        $oForm = new BxTemplStudioFormView(array());
+        $oForm->addCssJs();
+    }
+
+    protected function _getCellTitle ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $aValue = $this->_limitMaxLength(_t($aRow['title']), $sKey, $aField, $aRow, $this->_isDisplayPopupOnTextOverflow, false);
+
+        $mixedValue = $this->_oTemplate->parseHtmlByName('bx_a.html', array(
+            'href' => sprintf($this->sUrlViewItems, $aRow['module'], $aRow['set_name']),
+            'title' => _t('_adm_nav_txt_manage_items'),
+            'bx_repeat:attrs' => array(),
+            'content' => $aValue[0]
+        )) . (isset($aValue[1]) ? $aValue[1] : '');
+
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellItems ($mixedValue, $sKey, $aField, $aRow)
+    {
+        if(!empty($aRow['set_name'])) {
+            $aSets = array();
+            $this->oDb->getSets(array('type' => 'by_name', 'value' => $aRow['set_name']), $aSets, false);
+
+            $mixedValue = $this->_oTemplate->parseHtmlByName('bx_a.html', array(
+                'href' => sprintf($this->sUrlViewItems, $aRow['module'], $aRow['set_name']),
+                'title' => _t('_adm_nav_txt_manage_items'),
+                'bx_repeat:attrs' => array(),
+                'content' => _t('_adm_nav_txt_n_items', $aSets['items_count'])
+            ));
+        }
+
+        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 _getActionDelete ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if ($sType == 'single' && (int)$aRow['deletable'] != 1)
+            return '';
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getFilterControls ()
+    {
+        parent::_getFilterControls();
+
+        return  $this->getModulesSelectAll('getMenus') . $this->getSearchInput();
+    }
+
+    protected function _getFormObject($sAction, $aMenu = array())
+    {
+    	bx_import('BxTemplStudioFormView');
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-nav-menu-',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction,
+                'method' => BX_DOL_STUDIO_METHOD_DEFAULT
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_objects_menu',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'id' => array(
+                    'type' => 'hidden',
+                    'name' => 'id',
+                    'value' => isset($aMenu['id']) ? (int)$aMenu['id'] : 0,
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'title' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title',
+                    'caption' => _t('_adm_nav_txt_menus_title'),
+                    'info' => _t('_adm_nav_dsc_menus_title'),
+                    'value' => isset($aMenu['title']) ? $aMenu['title'] : '_adm_nav_txt_menu',
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'LengthTranslatable',
+                        'params' => array(3, 100, 'title'),
+                        'error' => _t('_adm_nav_err_menus_title'),
+                    ),
+                ),
+                'set_name' => array(
+                    'type' => 'select',
+                    'name' => 'set_name',
+                    'caption' => _t('_adm_nav_txt_menus_set_name'),
+                    'info' => _t('_adm_nav_dsc_menus_set_name'),
+                    'value' => isset($aMenu['set_name']) ? $aMenu['set_name'] : '',
+                    'values' => array(
+                        array('key' => '', 'value' => _t('_adm_nav_txt_menus_set_name_select')),
+                        array('key' => $this->sCreateNew, 'value' => _t('_adm_nav_txt_menus_set_name_new'))
+                    ),
+                    'required' => '1',
+                    'attrs' => array(
+                        'id' => 'bx-form-field-set-name',
+                        'onchange' => $this->getJsObject() . ".onSelectSet(this)"
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'avail',
+                        'params' => array(),
+                        'error' => _t('_adm_nav_err_menus_set_name'),
+                    ),
+                ),
+                'set_title' => array(
+                    'type' => 'text',
+                    'name' => 'set_title',
+                    'caption' => _t('_adm_nav_txt_menus_set_title'),
+                    'info' => _t('_adm_nav_dsc_menus_set_title'),
+                    'value' => '',
+                    'required' => '1',
+                    'tr_attrs' => array(
+                        'style' => 'display:none'
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'Avail',
+                        'params' => array(),
+                        'error' => _t('_adm_nav_err_menus_set_title'),
+                    ),
+                ),
+                'template_id' => array(
+                    'type' => 'select',
+                    'name' => 'template_id',
+                    'caption' => _t('_adm_nav_txt_menus_style'),
+                    'info' => _t('_adm_nav_dsc_menus_style'),
+                    'value' => isset($aMenu['template_id']) ? $aMenu['template_id'] : '',
+                    'values' => array(
+                        array('key' => '', 'value' => _t('_adm_nav_txt_menus_style_select'))
+                    ),
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'avail',
+                        'params' => array(),
+                        'error' => _t('_adm_nav_err_menus_style'),
+                    ),
+                ),
+                'controls' => array(
+                    'name' => 'controls',
+                    'type' => 'input_set',
+                    array(
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_adm_nav_btn_menus_add'),
+                    ),
+                    array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_adm_nav_btn_menus_cancel'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    )
+                )
+            )
+        );
+
+        $aSets = array();
+        $this->oDb->getSets(array('type' => 'all'), $aSets, false);
+        foreach($aSets as $sSet)
+            $aForm['inputs']['set_name']['values'][] = array('key' => $sSet['name'], 'value' => _t($sSet['title']));
+
+        $aTemplates = array();
+        $this->oDb->getTemplates(array('type' => 'all'), $aTemplates, false);
+        foreach($aTemplates as $aTemplate)
+            $aForm['inputs']['template_id']['values'][] = array('key' => $aTemplate['id'], 'value' => _t($aTemplate['title']));
+
+        switch($sAction){
+            case 'add':
+                unset($aForm['inputs']['id']);
+
+                $aForm['form_attrs']['id'] .= 'create';
+                break;
+
+            case 'edit':
+                $aForm['form_attrs']['id'] .= 'edit';
+                $aForm['inputs']['set_title']['checker']['func'] = 'UniqueSet';
+                $aForm['inputs']['controls'][0]['value'] = _t('_adm_nav_btn_menus_save');
+                break;
+        }
+
+        return new BxTemplStudioFormView($aForm);
+    }
+
+    protected function updateSetFields(&$oForm)
+    {
+        if($oForm->getCleanValue('set_name') != $this->sCreateNew)
+            unset($oForm->aInputs['set_title']['checker']);
+        else
+            unset($oForm->aInputs['set_title']['tr_attrs']['style']);
+    }
+}
+
+/** @} */

+ 342 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioNavigationSets.php

@@ -0,0 +1,342 @@
+<?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    TridentView Trident Studio Representation classes
+ * @ingroup     TridentStudio
+ * @{
+ */
+
+class BxBaseStudioNavigationSets extends BxDolStudioNavigationSets
+{
+    protected $sUrlViewItems;
+
+    function __construct($aOptions, $oTemplate = false)
+    {
+        parent::__construct($aOptions, $oTemplate);
+
+        $this->_aOptions['actions_single']['edit']['attr']['title'] = _t('_adm_nav_btn_sets_edit');
+        $this->_aOptions['actions_single']['delete']['attr']['title'] = _t('_adm_nav_btn_sets_delete');
+
+        $this->sUrlViewItems = BX_DOL_URL_STUDIO . 'builder_menu.php?page=items&module=%s&set=%s';
+    }
+
+    public function performActionAdd()
+    {
+        $sAction = 'add';
+
+        bx_import('BxTemplStudioFormView');
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-nav-set-create',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction,
+                'method' => BX_DOL_STUDIO_METHOD_DEFAULT
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_menu_sets',
+                    'key' => 'set_name',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'title' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title',
+                    'caption' => _t('_adm_nav_txt_sets_title'),
+                    'info' => _t('_adm_nav_dsc_sets_title'),
+                    'value' => '_adm_nav_txt_set',
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'LengthTranslatable',
+                        'params' => array(3, 100, 'title'),
+                        'error' => _t('_adm_nav_err_sets_title'),
+                    ),
+                ),
+                'deletable' => array(
+                    'type' => 'hidden',
+                    'name' => 'deletable',
+                    'value' => 1,
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'controls' => array(
+                    'name' => 'controls',
+                    'type' => 'input_set',
+                    array(
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_adm_nav_btn_sets_add'),
+                    ),
+                    array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_adm_nav_btn_sets_cancel'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    )
+                )
+            )
+        );
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+        	bx_import('BxDolStudioUtils');
+
+            $sLanguage = BxDolStudioLanguagesUtils::getInstance()->getCurrentLangName(false);
+
+            $sName = BxDolForm::getSubmittedValue('title-' . $sLanguage, $aForm['form_attrs']['method']);
+            $sName = uriGenerate($sName, 'sys_menu_sets', 'set_name', 'set');
+
+            if($oForm->insert(array('set_name' => $sName, 'module' => BX_DOL_STUDIO_MODULE_CUSTOM)) !== false)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $sName);
+            else
+                $aRes = array('msg' => _t('_adm_nav_err_sets_create'));
+
+            $this->_echoResultJson($aRes, true);
+        }
+        else {
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-nav-set-create-popup', _t('_adm_nav_txt_sets_create_popup'), $this->_oTemplate->parseHtmlByName('nav_add_set.html', array(
+                'form_id' => $aForm['form_attrs']['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()
+    {
+        $sAction = 'edit';
+
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $sId = bx_get('set_name');
+            if(!$sId) {
+                $this->_echoResultJson(array());
+                exit;
+            }
+
+            $aIds = array($sId);
+        }
+
+        $sId = bx_process_input($aIds[0]);
+
+        $aSet = array();
+        $iSet = $this->oDb->getSets(array('type' => 'by_name', 'value' => $sId), $aSet);
+        if($iSet != 1 || empty($aSet)) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        bx_import('BxTemplStudioFormView');
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-nav-set-edit',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction,
+                'method' => BX_DOL_STUDIO_METHOD_DEFAULT
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_menu_sets',
+                    'key' => 'set_name',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'set_name' => array(
+                    'type' => 'hidden',
+                    'name' => 'set_name',
+                    'value' => $sId,
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'title' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title',
+                    'caption' => _t('_adm_nav_txt_sets_title'),
+                    'info' => _t('_adm_nav_dsc_sets_title'),
+                    'value' => $aSet['title'],
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'LengthTranslatable',
+                        'params' => array(3, 100, 'title'),
+                        'error' => _t('_adm_nav_err_sets_title'),
+                    ),
+                ),
+                'controls' => array(
+                    'name' => 'controls',
+                    'type' => 'input_set',
+                    array(
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_adm_nav_btn_menus_save'),
+                    ),
+                    array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_adm_nav_btn_menus_cancel'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    )
+                )
+            )
+        );
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            if($oForm->update($sId) !== false)
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $sId);
+            else
+                $aRes = array('msg' => _t('_adm_nav_err_sets_edit'));
+
+            $this->_echoResultJson($aRes, true);
+        }
+        else {
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-nav-set-edit-popup', _t('_adm_nav_txt_sets_edit_popup', _t($aSet['title'])), $this->_oTemplate->parseHtmlByName('nav_add_set.html', array(
+                'form_id' => $aForm['form_attrs']['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            )));
+
+            $this->_echoResultJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))), true);
+        }
+    }
+
+    public function performActionDelete()
+    {
+        $iAffected = 0;
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $oGridItems = BxDolGrid::getObjectInstance('sys_studio_nav_items');
+        if(!$oGridItems) {
+            $this->_echoResultJson(array());
+            exit;
+        }
+
+        $aIdsAffected = array ();
+        foreach($aIds as $sId) {
+            $sId = bx_process_input($sId);
+
+            $aSet = array();
+            $this->oDb->getSets(array('type' => 'by_name', 'value' => $sId), $aSet, false);
+            if(!is_array($aSet) || empty($aSet))
+                continue;
+
+            $aMenus = array();
+            $this->oDb->getMenus(array('type' => 'by_set_name', 'value' => $sId), $aMenus, false);
+            if(is_array($aMenus) && count($aMenus) > 0) {
+                $this->_echoResultJson(array('msg' => _t('_adm_nav_err_sets_delete_used')));
+                exit;
+            }
+
+            if((int)$this->_delete($sId) <= 0)
+                continue;
+
+            $aItems = array();
+            $this->oDb->getItems(array('type' => 'by_set_name', 'value' => $aSet['name']), $aItems, false);
+            if(is_array($aItems) && !empty($aItems))
+                foreach($aItems as $aItem)
+                    $oGridItems->deleteByItem($aItem);
+
+            BxDolStudioLanguagesUtils::getInstance()->deleteLanguageString($aSet['title']);
+
+            $aIdsAffected[] = $sId;
+            $iAffected++;
+        }
+
+        $this->_echoResultJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('msg' => _t('_adm_nav_err_sets_delete')));
+    }
+
+    function getJsObject()
+    {
+        return 'oBxDolStudioNavigationSets';
+    }
+
+    function getCode($isDisplayHeader = true)
+    {
+        return $this->_oTemplate->parseHtmlByName('nav_sets.html', array(
+            'content' => parent::getCode($isDisplayHeader),
+            'js_object' => $this->getJsObject(),
+            'grid_object' => $this->_sObject,
+            'params_divider' => $this->sParamsDivider
+        ));
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oTemplate->addJs(array('jquery.form.min.js', 'navigation_sets.js'));
+
+        $oForm = new BxTemplStudioFormView(array());
+        $oForm->addCssJs();
+    }
+
+    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 _getCellItems ($mixedValue, $sKey, $aField, $aRow)
+    {
+        $aSets = array();
+        $this->oDb->getSets(array('type' => 'by_name', 'value' => $aRow['set_name']), $aSets, false);
+
+        $mixedValue = $this->_oTemplate->parseHtmlByName('bx_a.html', array(
+            'href' => sprintf($this->sUrlViewItems, $aRow['module'], $aRow['set_name']),
+            'title' => _t('_adm_nav_txt_manage_items'),
+            'bx_repeat:attrs' => array(),
+            'content' => _t('_adm_nav_txt_n_items', $aSets['items_count'])
+        ));
+
+        return parent::_getCellDefault ($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getActionDelete ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if ($sType == 'single' && (int)$aRow['deletable'] != 1)
+            return '';
+
+        return  parent::_getActionDefault($sType, $sKey, $a, false, $isDisabled, $aRow);
+    }
+
+    protected function _getFilterControls ()
+    {
+        parent::_getFilterControls();
+
+        return  $this->getModulesSelectAll('getSets') . $this->getSearchInput();
+    }
+}
+
+/** @} */

+ 340 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/studio/template/scripts/BxBaseStudioPolyglotKeys.php

@@ -0,0 +1,340 @@
+<?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    TridentView Trident Studio Representation classes
+ * @ingroup     TridentStudio
+ * @{
+ */
+
+class BxBaseStudioPolyglotKeys extends BxDolStudioPolyglotKeys
+{
+    function __construct($aOptions, $oTemplate = false)
+    {
+        parent::__construct($aOptions, $oTemplate);
+
+        $this->_aOptions['actions_single']['edit']['attr']['title'] = _t('_adm_pgt_btn_edit_title');
+        $this->_aOptions['actions_single']['delete']['attr']['title'] = _t('_adm_pgt_btn_delete_title');
+    }
+
+    public function performActionAdd()
+    {
+        $sAction = 'add';
+
+        $aLanguages = array();
+        $iLanguages = $this->oDb->getLanguagesBy(array('type' => 'all_key_id'), $aLanguages);
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-lang-new-key-form',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction,
+                'method' => 'post'
+            ),
+            'params' => array(
+                'db' => array(
+                    'table' => 'sys_localization_keys',
+                    'key' => 'ID',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array(
+                'key' => array(
+                    'type' => 'text',
+                    'name' => 'key',
+                    'caption' => _t('_adm_pgt_txt_nkp_key_name'),
+                    'value' => '',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'category_id' => array(
+                    'type' => 'select',
+                    'name' => 'category_id',
+                    'caption' => _t('_adm_pgt_txt_nkp_module'),
+                    'values' => $this->oDb->getCategories(),
+                    'value' => BX_DOL_STUDIO_PK_CATEGORY_CUSTOM,
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+
+            )
+        );
+
+        foreach($aLanguages as $aLanguage) {
+            $sName = 'language_' . $aLanguage['id'];
+            $aForm['inputs'][$sName] = array(
+                'type' => 'textarea',
+                'name' => $sName,
+                'caption' => $aLanguage['title'],
+                'value' => '',
+                'db' => array (
+                    'pass' => 'XssHtml',
+                ),
+            );
+        }
+
+        $aForm['inputs'] = array_merge($aForm['inputs'], array(
+            'languages' => array(
+                'type' => 'hidden',
+                'name' => 'languages',
+                'value' => implode(',', array_keys($aLanguages)),
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+            ),
+            'controls' => array(
+                'name' => 'controls',
+                'type' => 'input_set',
+                array(
+                    'type' => 'submit',
+                    'name' => 'do_submit',
+                    'value' => _t('_adm_pgt_btn_nkp_create'),
+                ),
+                array (
+                    'type' => 'reset',
+                    'name' => 'close',
+                    'value' => _t('_adm_pgt_btn_nkp_close'),
+                    'attrs' => array(
+                        'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                        'class' => 'bx-def-margin-sec-left',
+                    ),
+                )
+            )
+        ));
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $mixedResult = $this->add($oForm);
+
+            if(is_int($mixedResult))
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $mixedResult);
+            else
+                $aRes = array('msg' => $mixedResult);
+
+            $this->_echoResultJson($aRes, true);
+        }
+        else {
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-lang-new-key-popup', _t('_adm_pgt_txt_nkp_add_popup'), $this->_oTemplate->parseHtmlByName('pgt_new_key.html', array(
+                'form_id' => $aForm['form_attrs']['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()
+    {
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aIds = bx_get('ids');
+        if(!$aIds || !is_array($aIds)) {
+            $iId = (int)bx_get('id');
+            if(!$iId) {
+                $this->_echoResultJson(array());
+                exit;
+            }
+
+            $aIds = array($iId);
+        }
+
+        $iId = $aIds[0];
+        $sAction = 'edit';
+
+        $aLanguages = array();
+        $iLanguages = $this->oDb->getLanguagesBy(array('type' => 'all_key_id'), $aLanguages);
+
+        $aKey = $this->oDb->getKeyFullInfo($iId);
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-lang-edit-key-form',
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=' . $this->_sObject . '&a=' . $sAction,
+                'method' => 'post'
+            ),
+            'params' => array(
+                'db' => array(
+                    'table' => 'sys_localization_keys',
+                    'key' => 'ID',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array(
+                'id' => array(
+                    'type' => 'hidden',
+                    'name' => 'id',
+                    'value' => $iId,
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+            )
+        );
+
+        foreach($aLanguages as $aLanguage) {
+            $sName = 'language_' . $aLanguage['id'];
+            $aForm['inputs'][$sName] = array(
+                'type' => 'textarea',
+                'name' => $sName,
+                'caption' => $aLanguage['title'],
+                'value' => isset($aKey['strings'][$aLanguage['name']]) ? $aKey['strings'][$aLanguage['name']]['string'] : '',
+                'db' => array (
+                    'pass' => 'XssHtml',
+                ),
+            );
+        }
+
+        $aForm['inputs'] = array_merge($aForm['inputs'], array(
+            'languages' => array(
+                'type' => 'hidden',
+                'name' => 'languages',
+                'value' => implode(',', array_keys($aLanguages)),
+                'db' => array (
+                    'pass' => 'Xss',
+                ),
+            ),
+            'controls' => array(
+                'name' => 'controls',
+                'type' => 'input_set',
+                array(
+                    'type' => 'submit',
+                    'name' => 'do_submit',
+                    'value' => _t('_adm_pgt_btn_nkp_save'),
+                ),
+                array (
+                    'type' => 'reset',
+                    'name' => 'close',
+                    'value' => _t('_adm_pgt_btn_nkp_close'),
+                    'attrs' => array(
+                        'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                        'class' => 'bx-def-margin-sec-left',
+                    ),
+                )
+            )
+        ));
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $mixedResult = $this->edit($oForm);
+
+            if(is_int($mixedResult))
+                $aRes = array('grid' => $this->getCode(false), 'blink' => $mixedResult);
+            else
+                $aRes = array('msg' => $mixedResult);
+
+            $this->_echoResultJson($aRes, true);
+        }
+        else {
+            $sContent = BxTemplStudioFunctions::getInstance()->popupBox('adm-lang-edit-key-popup', _t('_adm_pgt_txt_nkp_edit_popup', $aKey['key']), $this->_oTemplate->parseHtmlByName('pgt_new_key.html', array(
+                'form_id' => $aForm['form_attrs']['id'],
+                'form' => $oForm->getCode(true),
+                'object' => $this->_sObject,
+                'action' => $sAction
+            )));
+
+            $this->_echoResultJson(array('popup' => array('html' => $sContent, 'options' => array('closeOnOuterClick' => false))), 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(!BxDolStudioLanguagesUtils::getInstance()->deleteLanguageStringById($iId, 0, false))
+                continue;
+
+            $aIdsAffected[] = $iId;
+            $iAffected++;
+        }
+
+        $this->_echoResultJson($iAffected ? array('grid' => $this->getCode(false), 'blink' => $aIdsAffected) : array('msg' => _t('_adm_pgt_err_save')));
+    }
+
+    function getJsObject()
+    {
+        return 'oBxDolStudioPolyglotKeys';
+    }
+
+    function getCode($isDisplayHeader = true)
+    {
+        return $this->_oTemplate->parseHtmlByName('pgt_keys.html', array(
+            'content' => parent::getCode($isDisplayHeader),
+            'js_object' => $this->getJsObject(),
+            'grid_object' => $this->_sObject
+        ));
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+        $this->_oTemplate->addJs(array('jquery.form.min.js', 'polyglot_keys.js'));
+
+        $oForm = new BxTemplStudioFormView(array());
+        $oForm->addCssJs();
+    }
+
+    protected function _getFilterControls ()
+    {
+        parent::_getFilterControls();
+
+        $sContent = "";
+
+        $oForm = new BxTemplStudioFormView(array());
+
+        $aInputModules = array(
+            'type' => 'select',
+            'name' => 'module',
+            'attrs' => array(
+                'id' => 'bx-grid-module-' . $this->_sObject,
+                'onChange' => 'javascript:' . $this->getJsObject() . '.onChangeFilter()'
+            ),
+            'values' => array(
+                'id-' . BX_DOL_LANGUAGE_CATEGORY_SYSTEM => '',
+                'id-' . BX_DOL_LANGUAGE_CATEGORY_CUSTOM => ''
+            )
+        );
+
+        $aCategories = $aCounter = array();
+        $this->oDb->getCategoriesBy(array('type' => 'all'), $aCategories, false);
+        $this->oDb->getKeysBy(array('type' => 'counter_by_category'), $aCounter, false);
+        foreach($aCategories as $aCategory)
+            $aInputModules['values']['id-' . $aCategory['id']] = $aCategory['name'] . " (" . (isset($aCounter[$aCategory['id']]) ? $aCounter[$aCategory['id']] : "0") . ")";;
+
+        $aInputModules['values'] = array_merge(array('id-0' => _t('_adm_pgt_txt_all_modules')), $aInputModules['values']);
+
+        $sContent .= $oForm->genRow($aInputModules);
+
+        $aInputSearch = array(
+            'type' => 'text',
+            'name' => 'keyword',
+            'attrs' => array(
+                'id' => 'bx-grid-search-' . $this->_sObject,
+                'onKeyup' => 'javascript:$(this).off(); ' . $this->getJsObject() . '.onChangeFilter()'
+            )
+        );
+
+        $sContent .= $oForm->genRow($aInputSearch);
+
+        return $sContent;
+    }
+}
+
+/** @} */

+ 105 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/template/css/media-phone.css

@@ -0,0 +1,105 @@
+@media (max-width:720px) {
+
+html {
+    font-size:85%;
+}
+
+.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:100% !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 */
+
+/* make popup fullscreen */
+
+.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;
+    top:0 !important;
+    left:0 !important;
+    bottom:0 !important;
+    right:0 !important;
+    width:100%;
+	width:100vw;
+    height:100%;
+    height:100vh;
+    overflow:auto;
+}
+
+.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-responsive.bx-popup-image-wrapper:not(.foo) .bx-popup-border,
+.bx-popup-responsive.bx-popup-trans-wrapper:not(.foo) .bx-popup-border,
+.bx-popup-responsive.bx-popup-box-wrapper:not(.foo) .bx-popup-border {
+    border:none;
+}
+
+.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;
+}
+
+.bx-popup-width {
+    width:100%;
+}
+
+/* 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 */

+ 525 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/template/css/menu.css

@@ -0,0 +1,525 @@
+/* --------------- 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-ver li .flag-icon {
+    vertical-align:baseline;
+}
+
+.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-homepage-menu-sys_homepage {
+    text-align:center;
+}
+
+#bx-menu-main-container {
+    height:40px;
+    height:2.5rem;
+    white-space: nowrap;
+    overflow:hidden;    
+}
+
+#bx-menu-main-container-scroller {
+    height:60px;
+    height:4rem;
+    overflow:auto;
+    text-align:left;
+    /* -webkit-overflow-scrolling:touch; */
+}
+
+#bx-menu-main-submenu,
+#bx-menu-main {
+    display:inline-block;
+    line-height:2.5rem;
+    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 li .sys-icon-a,
+.bx-menu-main-submenu li a {
+    display:inline-block;
+    text-decoration:none;
+    font-size:1.2rem;
+    line-height:2.5rem;
+    vertical-align:top;
+}
+.bx-menu-main li a {
+    font-size:0;
+    height:2rem;
+}
+.bx-menu-main li .sys-icon,
+.bx-menu-main li .sys-icon-a {
+    min-width:1.5rem;
+    font-size:1.5rem;
+    text-align:center;
+}
+.bx-menu-main li .sys-icon:first-child {
+    vertical-align:baseline;
+}
+.bx-menu-main li .sys-icon-a {
+    margin-top:0.5rem;
+}
+.bx-menu-main li img {
+    width:2rem;
+    height:40px;
+    height:2.5rem;
+    vertical-align:middle;
+    background-repeat:no-repeat;
+    background-position:center center;
+}
+
+.bx-menu-main-submenu li.bx-menu-tab-active a,
+.bx-menu-main li span {
+    color:#000;
+}
+
+#bx-menu-main li a span {
+    max-width:16rem;
+    overflow:hidden;
+    text-overflow:ellipsis;
+    white-space:nowrap;
+}
+
+/* --------------- 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 .sys-icon-a,
+ul.bx-menu-toolbar li a img,
+ul.bx-menu-toolbar li a span {
+    vertical-align:top;
+}
+ul.bx-menu-toolbar li a .sys-icon-a {
+    margin-top:0.5rem;
+}
+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-a,
+ul.bx-menu-toolbar li .sys-icon {
+    display:inline-block;
+    vertical-align:middle;
+    font-size:32px;
+    font-size:2rem;
+    text-align:center;
+    width:2rem;
+    height:2rem;
+}
+
+
+/* --------------- floating blocks menu */
+
+.bx-sliding-menu-close,
+.bx-sliding-menu-close:hover {
+    position:absolute;
+    top:0;
+    right:0;
+    text-decoration:none;
+}
+
+.bx-sliding-menu-main {
+    width:100%;    
+    opacity:0.9;
+    overflow-x:auto;
+    /* -webkit-overflow-scrolling:touch; */
+}
+
+.bx-sliding-menu-fullheight {
+    height:calc(100% - 3rem);
+    overflow-x:hidden;
+    overflow-y:auto;
+}
+
+.bx-sliding-menu-main ul.bx-menu-floating-blocks-big {
+    display:table;
+    margin:0 auto;
+    white-space:nowrap;
+}
+
+.bx-sliding-menu-main .bx-menu-account-popup-profile-switcher {
+    overflow:hidden;
+
+}
+
+.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:80px;
+    width:5rem;
+    height:80px;
+    height:5rem;
+}
+.bx-menu-floating-blocks-big li {
+    width:128px;
+    width:8rem;
+    height:128px;
+    height:8rem;
+}
+
+.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:16px 20px 3px 20px;
+    margin:1rem 1.5rem 0.2rem 1.5rem;
+}
+.bx-menu-floating-blocks-big li img,
+.bx-menu-floating-blocks-big li .sys-icon {
+    display:block;
+    width:64px;
+    width:4rem;
+    height:64px;
+    height:4rem;
+    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:64px;
+    line-height:4rem;
+    font-size:64px;
+    font-size:4rem;
+}
+
+.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;
+    white-space:nowrap;
+}
+
+/* --------------- 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-ver,
+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;
+}
+ul.bx-menu-btns-ver li {
+    text-align:center;
+}
+ul.bx-menu-btns-ver li .bx-btn {
+    float:none;
+    display:inline-block;
+}
+
+/* --------------- account popup menu */
+
+.bx-popup-content .bx-menu-account-popup {
+    min-width:17em;
+}
+
+.bx-menu-search-popup-title,
+.bx-menu-account-popup-profile,
+.bx-menu-account-popup-menu {
+    text-align:center;
+}
+
+.bx-sliding-menu-main > .bx-def-page-width {
+    position:relative;
+}
+
+.bx-menu-account-popup-profile-switcher-link b {
+    vertical-align:middle;
+}
+
+.bx-menu-account-popup-profile-switcher-link a:hover,
+.bx-menu-account-popup-profile-switcher-link a {
+    text-decoration:none;
+}
+
+/* --------------- custom menu */
+
+.bx-menu-custom > li > span,
+.bx-menu-custom > li > a {
+    display:inline-block;
+}

+ 29 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/template/menu_main.html

@@ -0,0 +1,29 @@
+<ul id="bx-menu-main" class="bx-menu-main bx-menu-object-__object__ bx-clearfix">
+    <li class="bx-def-color-bg-hl-hover bx-def-margin-right">
+        <a class="bx-def-padding-sec-leftright" href="javascript:void(0);">
+            <bx_if:icon><i class="sys-icon __icon__"></i></bx_if:icon>
+            <bx_if:image><img style="background-image:url(__icon_url__)" src="<bx_icon_url:spacer.gif />" /></bx_if:image>
+            <span class="bx-def-margin-sec-left">__title__</span>
+            <bx_if:menu>
+                <i class="sys-icon-a" data-rotate="down" data-icon="chevron" ></i>
+            </bx_if:menu>
+        </a>
+    </li>    
+    __popup__
+    <bx_repeat:menus>
+        <li class="bx-def-color-bg-hl-hover"><a class="bx-def-padding-sec-leftright" href="javascript:void(0);" onclick="$('#__id__').dolPopup({pointer:{el:$(this)}})"><i class="sys-icon __icon__"></i></a></li>
+        __popup__
+    </bx_repeat:menus>
+</ul>
+<script>
+    $("#bx-menu-main > li > a").click(function () {
+        var eIconAnim = (new Marka($(this).find('.sys-icon-a').get(0)));
+        eIconAnim.set('times');
+        $('#__id__').dolPopup({
+            pointer:{el:$(this)},
+            onBeforeHide:function () {
+                eIconAnim.set('chevron');
+            }
+        });
+    });
+</script>

+ 806 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/files/template/scripts/BxBaseCmts.php

@@ -0,0 +1,806 @@
+<?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    TridentCore Trident Core
+ * @{
+ */
+
+/**
+ * @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'));
+
+        $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(),
+        	'sBaseUrl' => $this->getBaseUrl(),
+            '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 = $aDp = array();
+		$this->_getParams($aBp, $aDp);
+
+        $aBp['parent_id'] = $iParentId;
+        $aBp['vparent_id'] = $iVParentId;
+        $aDp['show_empty'] = false;
+
+		//add live update
+		$this->actionResumeLiveUpdate();
+
+		$sServiceCall = BxDolService::getSerializedService('system', 'get_live_updates_comments', array($this->_sSystem, $this->_iId, $this->_getAuthorId(), '{count}'), 'TemplCmtsServices');
+		BxDolLiveUpdates::getInstance()->add($this->_sSystem . '_live_updates_cmts_' . $this->_iId, 1, $sServiceCall);
+		//add live update
+
+        $sCaption = _t('_cmt_block_comments_title', $this->getCommentsCountAll());
+        $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_DEF, $this->_getControlsBox()) : array(
+            'title' => $sCaption,
+            'content' => $sContent,
+            '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)) {
+        	if((int)$aBp['parent_id'] == 0 && !isLogged())	{
+        		$oPermalink = BxDolPermalinks::getInstance();
+        		return MsgBox(_t('_cmt_msg_login_required', $oPermalink->permalink('page.php?i=login'), $oPermalink->permalink('page.php?i=create-account')));
+        	}
+
+            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())
+    {
+        $sComment = $this->getComment(
+        	$iCmtId, 
+        	array_merge(array('type' => $this->_sBrowseType), $aBp), 
+        	array_merge(array('type' => BX_CMT_DISPLAY_THREADED), $aDp)
+        );
+        if (!$sComment)
+            return '';
+        return BxDolTemplate::getInstance()->parseHtmlByName('comment_block.html', array(
+            'system' => $this->_sSystem,
+            'id' => $this->getId(),
+            'comment' => $sComment,
+        	'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;
+        if (!$aCmt)
+            return '';
+
+        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';
+
+		if(!empty($aDp['blink']) && in_array($aCmt['cmt_id'], $aDp['blink']))
+			$sClass .= ' cmt-blink';
+
+        $sActions = $this->_getActionsBox($aCmt, $aDp);
+
+        $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_title' => bx_html_attribute(_t('_in_reply_to', $sParAuthorName)),
+                '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)) {
+        		$oStorage = BxDolStorage::getObjectInstance($this->getStorageObjectName());
+
+                $oTranscoder = BxDolTranscoderImage::getObjectInstance($this->getTranscoderPreviewName());
+
+                foreach($aImages as $aImage)
+                    $aTmplImages[] = array(
+                        'style_prefix' => $this->_sStylePrefix,
+                        'js_object' => $this->_sJsObjName,
+                        'image' => $oTranscoder->getFileUrl($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) {
+        	$aDp['show_empty'] = false;
+            $sReplies = $this->getComments(array('parent_id' => $aCmt['cmt_id'], 'vparent_id' => $aCmt['cmt_id'], 'type' => $aBp['type']), $aDp);
+        }
+
+		$sAgo = bx_time_js($aCmt['cmt_time']);
+        $bObjectTitle = !empty($this->_aSystem['trigger_field_title']);
+        return $oTemplate->parseHtmlByName('comment.html', array_merge(array(
+            'system' => $this->_sSystem,
+            'style_prefix' => $this->_sStylePrefix,
+            'js_object' => $this->_sJsObjName,
+            'id' => $aCmt['cmt_id'],
+            'class' => $sClass,
+            'bx_if:show_reply_to' => array(
+                'condition' => !empty($aTmplReplyTo),
+                'content' => $aTmplReplyTo
+            ),
+            '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
+                )
+            ),
+            'bx_if:show_attached' => array(
+                'condition' => !empty($aTmplImages),
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+                    'bx_repeat:attached' => $aTmplImages
+                )
+            ),
+            'actions' => $sActions,
+            'replies' =>  $sReplies
+        ), $this->_getTmplVarsAuthor($aCmt), $this->_getTmplVarsText($aCmt)));
+    }
+
+	/**
+     * 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);
+    }
+
+    function getNotification($iCountOld = 0, $iCountNew = 0)
+    {
+    	$iCount = (int)$iCountNew - (int)$iCountOld;
+
+		$aComments = $this->_oQuery->getCommentsBy(array('type' => 'latest', 'object_id' => $this->_iId, 'author' => $this->_getAuthorId(), 'others' => 1, 'start' => '0', 'per_page' => $iCount));
+		if(empty($aComments) || !is_array($aComments))
+			return '';
+
+		$sJsObject = $this->getJsObjectName();
+
+		$aComments = array_reverse($aComments);
+		$iComments = count($aComments);
+
+		$aTmplVarsNotifs = array();
+		foreach($aComments as $iIndex => $aComment) {
+			$iCommentId = $aComment['cmt_id'];
+
+			$sShowOnClick = "javascript:" . $sJsObject . ".goTo(this, '" . $this->_sSystem . $iCommentId . "', '" . $iCommentId . "');";
+			$sReplyOnClick = "javascript:" . $sJsObject . ".goToAndReply(this, '" . $this->_sSystem . $iCommentId . "', '" . $iCommentId . "');";
+
+	    	$aTmplVarsNotifs[] = array_merge(array(
+	    		'style_prefix' => $this->_sStylePrefix,
+	    		'js_object' => $sJsObject,
+	    		'bx_if:show_as_hidden' => array(
+	    			'condition' => $iIndex < ($iComments - 1),
+	    			'content' => array(),
+	    		),
+				'show_onclick' => $sShowOnClick,
+	    		'reply_onclick' => $sReplyOnClick,
+	    		'bx_if:show_previous' => array(
+	    			'condition' => $iIndex > 0,
+	    			'content' => array(
+	    				'style_prefix' => $this->_sStylePrefix,
+	    				'js_object' => $sJsObject
+	    			)
+	    		)
+			), $this->_getTmplVarsAuthor($aComment), $this->_getTmplVarsText($aComment));
+		}
+
+		return BxDolTemplate::getInstance()->parseHtmlByName('comments_notification.html', array(
+			'html_id' => $this->getNotificationId(),
+			'style_prefix' => $this->_sStylePrefix,
+			'bx_repeat:notifs' => $aTmplVarsNotifs
+		));
+    }
+
+    /**
+     * 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\');')
+            );
+
+            $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\');'),
+            );
+
+            $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 = (int)$this->_aSystem['is_browse_filter'] == 1;
+        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
+	        $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
+	            ));
+	
+	            $sMenuManage = BxTemplFunctions::getInstance()->transBox($this->_sSystem . '-manage-' . $aCmt['cmt_id'], $sMenuManage, true);
+	        }
+		}
+
+        return $oTemplate->parseHtmlByName('comment_actions.html', array(
+            'id' => $aCmt['cmt_id'],
+            'js_object' => $this->_sJsObjName,
+            'style_prefix' => $this->_sStylePrefix,
+            '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)) {
+                        $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);
+
+                if ($this->_sMetatagsObj) {
+                    $oMetatags = BxDolMetatags::getObjectInstance($this->_sMetatagsObj);
+                    $oMetatags->keywordsAdd($this->_oQuery->getUniqId($this->_aSystem['system_id'], $iCmtId), $sCmtText);
+                }
+
+                $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);
+
+                if ($this->_sMetatagsObj) {
+                    $oMetatags = BxDolMetatags::getObjectInstance($this->_sMetatagsObj);
+                    $oMetatags->keywordsAdd($this->_oQuery->getUniqId($this->_aSystem['system_id'], $iCmtId), $sCmtText);
+                }
+
+                $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' => $this->_prepareTextForOutput($sCmtText, $iCmtId));
+            }
+
+            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('_cmt_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' => ''
+    	));
+
+    	return BxTemplFunctions::getInstance()->transBox($sViewImagePopupId, $sViewImagePopupContent, true);
+    }
+
+    protected function _getTmplVarsAuthor($aCmt)
+    {
+    	list($sAuthorName, $sAuthorLink, $sAuthorIcon) = $this->_getAuthorInfo($aCmt['cmt_author_id']);
+    	$bAuthorIcon = !empty($sAuthorIcon);
+
+    	return array(
+    		'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_title' => bx_html_attribute($sAuthorName),
+                    'author_name' => $sAuthorName
+                )
+            ),
+            'bx_if:show_author_text' => array(
+                'condition' => empty($sAuthorLink),
+                'content' => array(
+                    'author_name' => $sAuthorName
+                )
+            ),
+    	);
+    }
+    protected function _getTmplVarsText($aCmt)
+    {
+    	$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, $aCmt['cmt_id']);
+        $sTextMore = $this->_prepareTextForOutput($sTextMore, $aCmt['cmt_id']);
+
+        return array(
+			'text' => $sText,
+            'bx_if:show_more' => array(
+                'condition' => !empty($sTextMore),
+                'content' => array(
+                    'style_prefix' => $this->_sStylePrefix,
+                    'js_object' => $this->_sJsObjName,
+                    'text_more' => $sTextMore
+                )
+            ),
+        );
+    }
+    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.B1-8.0.0.B2/files_delete.php

@@ -0,0 +1,43 @@
+<?php
+
+$aFilesDelete = array(
+    'plugins_public/codemirror/lib/codemirror.css',
+    'plugins_public/codemirror/lib/codemirror.js',
+    'plugins_public/codemirror/mode/css/css.js',
+    'plugins_public/codemirror/mode/css/index.html',
+    'plugins_public/codemirror/mode/css/less.html',
+    'plugins_public/codemirror/mode/css/less_test.js',
+    'plugins_public/codemirror/mode/css/scss.html',
+    'plugins_public/codemirror/mode/css/scss_test.js',
+    'plugins_public/codemirror/mode/css/test.js',
+    'plugins_public/codemirror/mode/htmlembedded/htmlembedded.js',
+    'plugins_public/codemirror/mode/htmlembedded/index.html',
+    'plugins_public/codemirror/mode/htmlmixed/htmlmixed.js',
+    'plugins_public/codemirror/mode/htmlmixed/index.html',
+    'plugins_public/codemirror/mode/index.html',
+    'plugins_public/codemirror/mode/javascript/index.html',
+    'plugins_public/codemirror/mode/javascript/javascript.js',
+    'plugins_public/codemirror/mode/javascript/json-ld.html',
+    'plugins_public/codemirror/mode/javascript/test.js',
+    'plugins_public/codemirror/mode/javascript/typescript.html',
+    'plugins_public/codemirror/mode/meta.js',
+    'plugins_public/codemirror/mode/php/index.html',
+    'plugins_public/codemirror/mode/php/php.js',
+    'plugins_public/codemirror/mode/php/test.js',
+    'plugins_public/codemirror/mode/sql/index.html',
+    'plugins_public/codemirror/mode/sql/sql.js',
+    'plugins_public/codemirror/mode/xml/index.html',
+    'plugins_public/codemirror/mode/xml/test.js',
+    'plugins_public/codemirror/mode/xml/xml.js',
+
+    'plugins_public/codemirror/mode/htmlembedded',
+    'plugins_public/codemirror/mode/javascript',
+    'plugins_public/codemirror/mode/htmlmixed',
+    'plugins_public/codemirror/mode/css',
+    'plugins_public/codemirror/mode/php',
+    'plugins_public/codemirror/mode/sql',
+    'plugins_public/codemirror/mode/xml',
+    'plugins_public/codemirror/mode',
+    'plugins_public/codemirror/lib',
+);
+

+ 2 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/script.php

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

Fișier diff suprimat deoarece este prea mare
+ 5 - 0
upgrade/files/8.0.0.B1-8.0.0.B2/sql.sql


Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff