Просмотр исходного кода

Upgrade Patch 8.0.0-A3 to 8.0.0-A4

Alex Trofimov 12 лет назад
Родитель
Сommit
d85e01341a
34 измененных файлов с 5631 добавлено и 4 удалено
  1. 4 4
      upgrade/BxDolUpgradeCron.php
  2. 8 0
      upgrade/files/8.0.0.A3-8.0.0.A4/check.php
  3. 80 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolCronQuery.php
  4. 29 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolCronUpgradeModulesCheck.php
  5. 202 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolFile.php
  6. 70 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolIO.php
  7. 113 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolService.php
  8. 216 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolUpgrader.php
  9. 72 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolUpgraderModules.php
  10. 12 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/version.inc.php
  11. 146 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/periodic/cron.php
  12. 44 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/builder_page.php
  13. 195 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/classes/BxDolStudioDashboard.php
  14. 420 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/classes/BxDolStudioInstallerUtils.php
  15. 170 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/classes/BxDolStudioUpdater.php
  16. 257 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/js/builder_page.js
  17. 199 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/js/dashboard.js
  18. 124 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/js/page.js
  19. 19 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/bp_add_block.html
  20. 19 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/bp_add_page.html
  21. 19 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/bp_edit_page.html
  22. 18 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/bp_edit_page_form.html
  23. 230 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/css/builder_page.css
  24. 56 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/css/dashboard.css
  25. 26 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/css/default.less
  26. 45 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/css/menu_top.css
  27. 15 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/dbd_versions.html
  28. 6 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/dbd_versions_upgrade.html
  29. 1621 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/scripts/BxBaseStudioBuilderPage.php
  30. 295 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/scripts/BxBaseStudioDashboard.php
  31. 220 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/scripts/BxBaseStudioFormView.php
  32. 674 0
      upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/scripts/BxBaseStudioStore.php
  33. 2 0
      upgrade/files/8.0.0.A3-8.0.0.A4/script.php
  34. 5 0
      upgrade/files/8.0.0.A3-8.0.0.A4/sql.sql

+ 4 - 4
upgrade/BxDolUpgradeCron.php

@@ -7,13 +7,13 @@
  * @{
  */
 
-require_once(BX_DIRECTORY_PATH_ROOT . 'upgrade/classes/BxDolUpgradeController.php');
-require_once(BX_DIRECTORY_PATH_ROOT . 'upgrade/classes/BxDolUpgradeUtil.php');
-require_once(BX_DIRECTORY_PATH_ROOT . 'upgrade/classes/BxDolUpgradeDb.php');
-
 $aPathInfo = pathinfo(__FILE__);
 define ('BX_UPGRADE_DIR_UPGRADES', $aPathInfo['dirname'] . '/files/');
 
+require_once($aPathInfo['dirname'] . '/classes/BxDolUpgradeController.php');
+require_once($aPathInfo['dirname'] . '/classes/BxDolUpgradeUtil.php');
+require_once($aPathInfo['dirname'] . '/classes/BxDolUpgradeDb.php');
+
 bx_import('BxDolCron');
 
 class BxDolUpgradeCron extends BxDolCron

+ 8 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/check.php

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

+ 80 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolCronQuery.php

@@ -0,0 +1,80 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolDb');
+
+/**
+ * Database queries for Cron
+ * @see BxDolCron
+ */
+class BxDolCronQuery extends BxDolDb 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();
+    }
+
+    /**
+     * 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
+     */
+    static function getInstance()
+    {
+        if(!isset($GLOBALS['bxDolClasses'][__CLASS__]))
+            $GLOBALS['bxDolClasses'][__CLASS__] = new BxDolCronQuery();
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    public function getJobs()
+    {
+    	return $this->fromCache('sys_cron_jobs', 'getAll', "SELECT * FROM `sys_cron_jobs`");
+    }
+
+    public function addTransientJobClass($sName, $sClass, $sFile)
+    {
+    	$sQuery = $this->prepare("INSERT INTO `sys_cron_jobs` SET `name`=?, `time`='transient', `class`=?, `file`=?", $sName, $sClass, $sFile);
+        return (int)$this->query($sQuery) > 0;
+    }
+
+	public function addTransientJobService($sName, $mixedService)
+    {
+    	if(is_array($mixedService))
+    		$mixedService = call_user_func_array('BxDolService::getSerializedService', $mixedService);
+
+    	if(!BxDolService::isSerializedService($mixedService))
+    		return false;
+
+    	$sQuery = $this->prepare("INSERT INTO `sys_cron_jobs` SET `name`=?, `time`='transient', `service_call`=?", $sName, $mixedService);
+        return (int)$this->query($sQuery) > 0;
+    }
+
+    public function getTransientJobs()
+    {
+    	return $this->getAllWithKey("SELECT * FROM `sys_cron_jobs` WHERE `time`='transient'", 'name');
+    }
+
+    public function deleteTransientJobs()
+    {
+    	return $this->query("DELETE FROM `sys_cron_jobs` WHERE `time`='transient'");
+    }
+}
+
+/** @} */

+ 29 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolCronUpgradeModulesCheck.php

@@ -0,0 +1,29 @@
+<?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    DolphinUpgradeModules Dolphin Upgrade Modules Script
+ * @{
+ */
+
+bx_import('BxDolCron');
+
+class BxDolCronUpgradeModulesCheck extends BxDolCron
+{
+    public function processing()
+    {
+        if ('on' != getParam('sys_autoupdate_modules'))
+            return;
+
+        $o = bx_instance('BxDolUpgraderModules');
+        if(!$o->prepare()) {
+            sendMailTemplateSystem('t_UpgradeFailed', array (
+                'error_msg' => $o->getError(),
+            ));
+            setParam('sys_autoupdate_modules', ''); // disable auto-update if it is failed
+        }
+    }
+}
+
+/** @} */

+ 202 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolFile.php

@@ -0,0 +1,202 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+class BxDolFile extends BxDol implements iBxDolSingleton
+{
+    protected $_sPathFrom;
+    protected $_sPathTo;
+
+    protected function __construct()
+    {
+        parent::__construct();
+
+        $this->_sPathFrom = BX_DIRECTORY_PATH_ROOT;
+        $this->_sPathTo = BX_DIRECTORY_PATH_ROOT;
+    }
+
+    /**
+     * 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 BxDolFile();
+
+        return $GLOBALS['bxDolClasses'][__CLASS__];
+    }
+
+    function copy($sFilePathFrom, $sFilePathTo)
+    {
+    	if(substr($sFilePathFrom, 0, strlen($this->_sPathFrom)) != $this->_sPathFrom)
+        	$sFilePathFrom = $this->_sPathFrom . $sFilePathFrom;
+
+        if(substr($sFilePathTo, 0, strlen($this->_sPathTo)) != $this->_sPathTo)
+        	$sFilePathTo = $this->_sPathTo . $sFilePathTo;
+
+        return $this->_copyFile($sFilePathFrom, $sFilePathTo);
+    }
+
+    function delete($sPath)
+    {
+        $sPath = $this->_sPathTo . $sPath;
+        return $this->_deleteDirectory($sPath);
+    }
+
+    function getPermissions($sPath)
+    {
+        $sPath = $this->_sPathTo . $sPath;
+        return $this->_getPermissions($sPath);
+    }
+
+    function setPermissions($sPath, $sMode)
+    {
+        $sPath = $this->_sPathTo . $sPath;
+        return $this->_setPermissions($sPath, $sMode);
+    }
+
+    protected function _copyFile($sFilePathFrom, $sFilePathTo)
+    {
+        if(substr($sFilePathFrom, -1) == '*')
+            $sFilePathFrom = substr($sFilePathFrom, 0, -1);
+
+        //--- Copy file to file/folder
+        if(is_file($sFilePathFrom)) {
+            if($this->_isFile($sFilePathTo)) {
+                $aFileParts = $this->_parseFile($sFilePathTo);
+                if(isset($aFileParts[0]))
+                    $this->_mkDirR($aFileParts[0]);
+
+                return @copy($sFilePathFrom, $sFilePathTo);
+            }
+            else {
+                $this->_mkDirR($sFilePathTo);
+
+                $aFileParts = $this->_parseFile($sFilePathFrom);
+				return @copy($sFilePathFrom, $this->_validatePath($sFilePathTo) . $aFileParts[1]);
+            }
+        }
+
+        //--- Copy directory to directory
+        if(is_dir($sFilePathFrom) && $this->_isDirectory($sFilePathTo)) {      	
+            $this->_mkDirR($sFilePathTo);
+
+            $bResult = true;
+            $aInnerFiles = $this->_readDirectory($sFilePathFrom);
+            foreach($aInnerFiles as $sFile)
+                $bResult = $bResult && $this->_copyFile($this->_validatePath($sFilePathFrom) . $sFile, $this->_validatePath($sFilePathTo) . $sFile);
+
+			return $bResult;
+        }
+
+        return false;
+    }
+
+    protected function _readDirectory($sFilePath)
+    {
+        if(!is_dir($sFilePath) || !($rSource = opendir($sFilePath)))
+            return false;
+
+        $aResult = array();
+        while(($sFile = readdir($rSource)) !== false) {
+            if($sFile == '.' || $sFile =='..')
+                continue;
+            $aResult[] = $sFile;
+        }
+        closedir($rSource);
+
+        return $aResult;
+    }
+
+    protected function _deleteDirectory($sPath)
+    {
+        if(!file_exists($sPath))
+            return true;
+
+        if($this->_isDirectory($sPath)) {
+        	$sPath = $this->_validatePath($sPath);
+
+            $aFiles = $this->_readDirectory($sPath);
+            if(is_array($aFiles) && !empty($aFiles))
+                foreach($aFiles as $sFile)
+                    $this->_deleteDirectory($sPath . $sFile);
+
+            if(!@rmdir($sPath))
+                return false;
+        } else if(!@unlink($sPath))
+            return false;
+
+        return true;
+    }
+
+    protected function _validatePath($sPath)
+    {
+        return $sPath . ($sPath && !$this->_isEndWithSlash($sPath) && $this->_isDirectory($sPath) ? '/' : '');
+    }
+
+    protected function _parseFile($sFilePath)
+    {
+        return array(dirname($sFilePath), basename($sFilePath));
+    }
+
+    protected function _isFile($sFilePath)
+    {
+    	if($this->_isEndWithSlash($sFilePath))
+    		return false;
+
+    	$aInfo = pathinfo($sFilePath);
+    	if(!isset($aInfo['extension']))
+    		return false;
+
+        return true;
+    }
+
+    protected function _isDirectory($sFilePath)
+    {
+        return !$this->_isFile($sFilePath);
+    }
+
+    protected function _isEndWithSlash($sFilePath)
+    {
+    	return in_array(substr($sFilePath, -1), array('/', '\\'));
+    }
+
+    protected function _getPermissions($sPath)
+    {
+        clearstatcache();
+
+        $hPerms = @fileperms($sPath);
+        if($hPerms == false)
+            return false;
+
+        return substr(decoct($hPerms), -3);
+    }
+
+    protected function _setPermissions($sPath, $sMode)
+    {
+        $aConvert = array('writable' => 0666, 'executable' => 0777);
+
+        return chmod($sPath, $aConvert[$sMode]);
+    }
+
+	protected function _mkDirR($sPath)
+    {
+        return @mkdir($sPath, 0777, true);
+    }
+}
+
+/** @} */

+ 70 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolIO.php

@@ -0,0 +1,70 @@
+<?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+class BxDolIO extends BxDol
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    public static function isRealOwner()
+    {
+    	if(defined('BX_DOL_CRON_EXECUTE'))
+    		trigger_error('Function can\'t be called under cron', E_USER_ERROR);
+
+		$sName = time() . rand(0, 999999999);
+		$sFilePath = BX_DIRECTORY_PATH_TMP . $sName . '.txt';
+		if(!$rHandler = fopen($sFilePath, 'w'))
+            return false;
+
+		if(!fwrite($rHandler, $sName))
+            return false;
+
+		fclose($rHandler);
+
+		$bResult = fileowner(BX_DIRECTORY_PATH_INC . 'utils.inc.php') === fileowner($sFilePath);
+		@unlink($sFilePath);
+
+		return $bResult;
+    }
+
+    public static function isExecutable($sFile)
+    {
+        clearstatcache();
+
+        $aPathInfo = pathinfo(__FILE__);
+        $sFile = $aPathInfo['dirname'] . '/../../' . $sFile;
+
+        return (is_file($sFile) && is_executable($sFile));
+    }
+
+    public static function isWritable($sFile)
+    {
+        clearstatcache();
+
+        $aPathInfo = pathinfo(__FILE__);
+        $sFile = $aPathInfo['dirname'] . '/../../' . $sFile;
+
+        return is_readable($sFile) && is_writable($sFile);
+    }
+
+    public static function getPermissions($sFileName)
+    {
+        $sPath = isset($GLOBALS['logged']['admin']) && $GLOBALS['logged']['admin'] ? BX_DIRECTORY_PATH_ROOT : '../';
+
+        clearstatcache();
+        $hPerms = @fileperms($sPath . $sFileName);
+        if($hPerms == false) return false;
+        $sRet = substr( decoct( $hPerms ), -3 );
+        return $sRet;
+    }
+}
+
+/** @} */

+ 113 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolService.php

@@ -0,0 +1,113 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolRequest');
+
+/**
+ * Service calls to modules' methods.
+ *
+ * The class has one static method is needed to make service calls
+ * to module's methods from the Dolphin's core or the other modules.
+ *
+ *
+ * Example of usage:
+ * @code
+ * BxDolService::call('payment', 'get_add_to_cart_link', array($iVendorId, $mixedModuleId, $iItemId, $iItemCount));
+ * @endcode
+ *
+ *
+ * Memberships/ACL:
+ * Doesn't depend on user's membership.
+ *
+ *
+ * Alerts:
+ * no alerts available
+ *
+ */
+class BxDolService extends BxDol
+{
+    /**
+     * Perform serice call
+     * @param $mixed module name or module id
+     * @param $sMethod service method name in format 'method_name', corresponding class metod is serviceMethodName
+     * @param $aParams params to pass to service method
+     * @param $sClass class to search for service method, by default it is main module class
+     * @return service call result
+     */
+    public static function call($mixed, $sMethod, $aParams = array(), $sClass = 'Module')
+    {
+        bx_import('BxDolModuleQuery');
+        $oDb = BxDolModuleQuery::getInstance();
+
+        $aModule = array();
+        if(is_string($mixed))
+            $aModule = $oDb->getModuleByName($mixed);
+        else
+            $aModule = $oDb->getModuleById($mixed);
+
+        return empty($aModule) ? '' : BxDolRequest::processAsService($aModule, $sMethod, $aParams, $sClass);
+    }
+
+    /**
+     * Perform serice call by accepting serialized array of service call parameters:
+     * @code
+     *     array (
+     *         'module' => 'system', // module name
+     *         'method' => 'test', // service method name
+     *         'params' => array(), // array of parameters to pass to service method
+     *         'class' => 'Module', // class to search service method in
+     *     )
+     * @endcode
+     *
+     * @param $s serialized array of serice call
+     * @param $aMarkers service method name in format 'method_name', corresponding class metod is serviceMethodName
+     * @param $sReplaceIn params to pass to service method
+     * @return service call result
+     */
+    public static function callSerialized($s, $aMarkers = array(), $sReplaceIn = 'params')
+    {
+        $a = @unserialize($s);
+        if (false === $a || !is_array($a))
+            return '';
+
+        if (isset($a[$sReplaceIn]))
+            $a[$sReplaceIn] = bx_replace_markers($a[$sReplaceIn], $aMarkers);
+
+        return self::call($a['module'], $a['method'], isset($a['params']) ? $a['params'] : array(), isset($a['class']) ? $a['class'] : 'Module');
+    }
+
+    /**
+     * Check if string is serialized array
+     */
+    public static function isSerializedService($s)
+    {
+        return preg_match('/^a:[\d+]:\{/', $s);
+    }
+
+    /**
+     * Serialized service call array
+     */
+    public static function getSerializedService($mixedModule, $sMethod, $aParams = array(), $sClass = '')
+    {
+		$aService = array(
+			'module' => $mixedModule,
+			'method' => $sMethod,
+		);
+
+		if(!empty($aParams))
+			$aService['params'] = $aParams;
+
+		if(!empty($sClass))
+			$aService['class'] = $sClass;
+
+		return serialize($aService);
+    }
+}
+
+/** @} */

+ 216 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolUpgrader.php

@@ -0,0 +1,216 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+class BxDolUpgrader extends BxDol
+{
+    protected $_sUrlVersionCheck = 'http://rss.boonex.com/u/';
+    protected $_sError = false;
+
+    /**
+     * Prepare for system upgrade.
+     * It prepares upgrade by downloading upgrade pack and performing different checks.
+     * After all check are successfully completed, transient cron job is set to run upgrade upon next cron run.
+     * @return true if upgrade was successfully scheduled to run, or false on error (you can call @see getError to determine particular error message)
+     */
+    public function prepare()
+    {
+        $this->setError(false);
+        $iUmaskSave = umask(0);
+
+        while(true) {
+
+            $aVersionUpdateInfo = $this->getVersionUpdateInfo();
+            if (null === $aVersionUpdateInfo) {
+                $this->setError(_t('_sys_upgrade_get_version_info_failed'));
+                break;
+            }
+
+            if (!$this->isUpgradeAvailable($aVersionUpdateInfo))
+                break;
+
+            if (BX_DOL_VERSION != bx_get_ver()) {
+                $this->setError(_t('_sys_upgrade_db_and_files_versions_different'));
+                break;
+            }
+
+            $fChangedFilesPercent = 1;
+            $aFailedFiles = $this->checkFilesChecksums ($fChangedFilesPercent);
+            $bAutoupdateForceModifiedFiles = ('on' == getParam('sys_autoupdate_force_modified_files'));
+            if (!empty($aFailedFiles) && !$bAutoupdateForceModifiedFiles) {
+                $this->setError(_t('_sys_upgrade_files_checksum_failed', implode(', ', $aFailedFiles)));
+                break;
+            }
+            elseif ($fChangedFilesPercent > BX_FORCE_AUTOUPDATE_MAX_CHANGED_FILES_PERCENT && $bAutoupdateForceModifiedFiles) {
+                $this->setError(_t('_sys_upgrade_files_checksum_failed_too_many', round($fChangedFilesPercent * 100)));
+            }
+
+            if (!($sPatchPath = $this->downloadPatch ($aVersionUpdateInfo))) {
+                $this->setError(_t('_sys_upgrade_patch_download_failed'));
+                break;
+            }
+
+            if (!$this->isPatchChecksumCorrect ($sPatchPath, $aVersionUpdateInfo)) {
+                $this->deleteUpgradePatch($sPatchPath);
+                $this->setError(_t('_sys_upgrade_patch_checksum_failed'));
+                break;
+            }
+
+            if (!($sUnpackedPath = $this->unpackPatch ($sPatchPath, true))) {
+                $this->deleteUpgradePatch($sPatchPath);
+                $this->setError(_t('_sys_upgrade_patch_unpack_failed'));
+                break;
+            }            
+
+            $this->deleteUpgradePatch($sPatchPath);
+
+            if (!$this->isValidPatch ($sUnpackedPath, $aVersionUpdateInfo)) {
+                $this->deleteUpgradeFolder($sPatchPath);
+                $this->setError(_t('_sys_upgrade_patch_invalid'));
+                break;
+            }
+
+            if (!$this->setTransientUpgradeCronJob ($sUnpackedPath)) {
+                $this->deleteUpgradeFolder($sPatchPath);
+                $this->setError(_t('_sys_upgrade_set_cron_failed'));
+                break;
+            }
+
+            break;
+        }
+
+        umask($iUmaskSave);
+
+        return $this->getError() ? false : true;
+    }
+
+    public function getVersionUpdateInfo ()
+    {
+        $s = bx_file_get_contents($this->_sUrlVersionCheck, array ('v' => bx_get_ver()));
+        if (!$s)
+            return null;
+
+        $a = json_decode($s, true);
+        if (!isset($a['latest_version']))
+            return null;
+
+        return $a;
+    }
+
+    public function isNewVersionAvailable ($a)
+    {
+        if (1 == version_compare(strtolower($a['latest_version']), strtolower(bx_get_ver())))
+            return true;
+        return false;
+    }
+
+    public function isUpgradeAvailable ($a)
+    {
+        if ($this->isNewVersionAvailable($a) && isset($a['patch']))
+            return true;
+        return false;
+    }
+
+    protected function downloadPatch ($a)
+    {
+        if (!isset($a['patch']['url']))
+            return false;
+
+        if (!($f = fopen($a['patch']['url'], "rb")))
+            return false;
+
+        $sTmpFile = BX_DIRECTORY_PATH_TMP . 'patch_' . bx_get_ver() . '_' . $a['patch']['ver'] . '.zip';
+        if (file_exists($sTmpFile) && !unlink($sTmpFile))
+            return false;        
+
+        $sRet = false;
+        if (false !== $sTmpFile && false !== file_put_contents($sTmpFile, $f))
+            $sRet = $sTmpFile;
+
+        fclose($f);
+
+        return $sRet;
+    }
+
+    protected function isPatchChecksumCorrect ($sPatchPath, $aVersionUpdateInfo)
+    {
+        return md5_file($sPatchPath) == $aVersionUpdateInfo['patch']['md5'];
+    }
+
+    protected function unpackPatch ($sPatchPath)
+    {
+        $sTmpFolder = $this->getTmpFolderFromZip($sPatchPath);
+        if (file_exists($sTmpFolder) && !bx_rrmdir($sTmpFolder))
+            return false;
+        
+        $oZip = new ZipArchive();
+        if ($oZip->open($sPatchPath) !== true)
+            return false;
+
+        $sRootFolder = $oZip->numFiles > 0 ? $oZip->getNameIndex(0) : false;
+        if (!$sRootFolder || !mkdir($sTmpFolder) || !$oZip->extractTo($sTmpFolder))
+            $sRootFolder = false;
+
+        $oZip->close();
+
+        return $sRootFolder ? $sTmpFolder . '/' . trim($sRootFolder, '/') . '/' : false;
+    }
+
+    protected function checkFilesChecksums (&$fChangedFilesPercent)
+    {
+        $oHasher = bx_instance('BxDolInstallerHasher');
+        return $oHasher->checkSystemFilesHash($fChangedFilesPercent);
+    }
+
+    protected function isValidPatch ($sUnpackedPath, $aVersionUpdateInfo)
+    {
+        $sPath = $sUnpackedPath . 'files/' . $this->normalizeVersion(bx_get_ver()) . '-' . $this->normalizeVersion($aVersionUpdateInfo['patch']['ver']) . '/';
+        $sCheckFilePath =  $sPath . 'check.php';
+        $sVersionFilePath =  $sPath . 'files/inc/version.inc.php';
+        return file_exists($sCheckFilePath) && file_exists($sVersionFilePath) && file_exists($sUnpackedPath . 'BxDolUpgradeCron.php');
+    }
+
+    public function setTransientUpgradeCronJob ($sUnpackedPath)
+    {
+        $sUnpackedPath = rtrim($sUnpackedPath, '/') . '/';
+        bx_import('BxDolCronQuery');
+        return BxDolCronQuery::getInstance()->addTransientJobClass('sys_perform_upgrade', 'BxDolUpgradeCron', bx_ltrim_str($sUnpackedPath, BX_DIRECTORY_PATH_ROOT) . 'BxDolUpgradeCron.php');
+    }
+
+    public function getError()
+    {
+        return $this->_sError;
+    }
+
+    protected function setError($s)
+    {
+        $this->_sError = $s;
+    }
+    
+    protected function normalizeVersion($s)
+    {
+        return str_replace(array('-', '_', ' '), '.', $s);
+    }
+
+    protected function getTmpFolderFromZip($sPatchPath)
+    {
+        return preg_replace('/\.zip$/', '', $sPatchPath);
+    }
+
+    protected function deleteUpgradeFolder($sPatchPath)
+    {
+        @bx_rrmdir($this->getTmpFolderFromZip($sPatchPath));
+    }
+
+    protected function deleteUpgradePatch($sPatchPath)
+    {
+        @unlink($sPatchPath);
+    }
+}
+
+/** @} */

+ 72 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/classes/BxDolUpgraderModules.php

@@ -0,0 +1,72 @@
+<?php use system\L;
+defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinCore Dolphin Core
+ * @{
+ */
+
+bx_import('BxDolUpgrader');
+
+class BxDolUpgraderModules extends BxDolUpgrader
+{
+    public function prepare()
+    {
+        $iUmaskSave = umask(0);
+
+        bx_import('BxDolStudioInstallerUtils');
+        $oInstallerUtils = BxDolStudioInstallerUtils::getInstance();
+
+        $aUpdated = array();
+        while(true) {
+        	$aUpdates = $oInstallerUtils->checkUpdates();
+	        foreach($aUpdates as $aUpdate) {
+	        	$mixedResult = $oInstallerUtils->downloadUpdatePublic($aUpdate['name']);
+	        	if($mixedResult !== true) {
+	        		$this->setError($mixedResult);
+	        		break;
+	        	}
+	        }
+
+	        if($this->getError()) 
+	        	break;
+
+        	$aUpdates = $oInstallerUtils->getUpdates();
+        	foreach($aUpdates as $aUpdate) {
+        		$aResult = BxDolStudioInstallerUtils::getInstance()->perform($aUpdate['dir'], 'update');
+        		if((int)$aResult['code'] != 0) {
+        			$this->setError($aResult['message']);
+        			break;
+        		}
+        		else 
+        			$aUpdated[$aUpdate['module_name']] = $aUpdate['version_to'];
+        	}
+
+        	break;
+        }
+
+        umask($iUmaskSave);
+
+        if(!empty($aUpdated)) {
+        	bx_import('BxDolModuleQuery');
+			$oModuleQuery = BxDolModuleQuery::getInstance();
+
+        	$sUpdated = '';
+        	foreach($aUpdated as $sModule => $sVersion) {
+        		$aModule = $oModuleQuery->getModuleByName($sModule);
+
+        		$sUpdated .= _t('_sys_et_txt_body_upgraded_module', $aModule['title'], $sVersion);
+        	}
+
+        	sendMailTemplateSystem('t_UpgradeModulesSuccess', array (
+                'conclusion' => $sUpdated,
+            ));
+        }
+
+        return $this->getError() ? false : true;
+    }
+}
+
+/** @} */

+ 12 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/inc/version.inc.php

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

+ 146 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/periodic/cron.php

@@ -0,0 +1,146 @@
+<?php
+/**
+ * @package     Dolphin Core
+ * @copyright   Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * @license     CC-BY - http://creativecommons.org/licenses/by/3.0/
+ */
+
+$GLOBALS['bx_profiler_disable'] = true;
+define('BX_DOL_CRON_EXECUTE', '1');
+
+$aPathInfo = pathinfo(__FILE__);
+require_once ($aPathInfo['dirname'] . '/../inc/header.inc.php');
+
+function getRange($iLow, $iHigh, $iStep)
+{
+    $aResult = array();
+    for ($i = $iLow; $i <= $iHigh && $iStep; $i += $iStep)
+        $aResult[] = $i;
+    return $aResult;
+}
+
+function getPeriod($sPeriod, $iLow, $iHigh)
+{
+    $aRes = array();
+    $iStep = 1;
+    $sErr = '';
+
+    do {
+        if ('' === $sPeriod) {
+            $sErr = 'Variable sPeriod is emply';
+            break;
+        }
+
+        $aParam = explode('/', $sPeriod);
+
+        if (count($aParam) > 2) {
+            $sErr = 'Error of format for string assigning period';
+            break;
+        }
+
+        if (count($aParam) == 2 && is_numeric($aParam[1]))
+            $iStep = $aParam[1];
+
+        $sPeriod = $aParam[0];
+
+        if ($sPeriod != '*') {
+            $aParam = explode('-', $sPeriod);
+
+            if (count($aParam) > 2) {
+                $sErr = 'Error of format for string assigning period';
+                break;
+            }
+
+            if (count($aParam) == 2)
+                $aRes = getRange($aParam[0], $aParam[1], $iStep);
+            else
+                $aRes = explode(',', $sPeriod);
+        } else
+            $aRes = getRange($iLow, $iHigh, $iStep);
+    } while(false);
+
+    if ($sErr) {
+        // show error or add to log
+    }
+
+    return $aRes;
+}
+
+function checkCronJob($sPeriods, $aDate = array())
+{
+    $aParam = explode(' ', preg_replace("{ +}", ' ', trim($sPeriods)));
+    $bRes = true;
+
+    if(empty($aDate))
+        $aDate = getdate(time());
+
+    for ($i = 0; $i < count($aParam); $i++) {
+        switch ($i) {
+            case 0:
+                $aRes = getPeriod($aParam[$i], 0, 59);
+                $bRes = in_array($aDate['minutes'], $aRes);
+                break;
+            case 1:
+                $aRes = getPeriod($aParam[$i], 0, 23);
+                $bRes = in_array($aDate['hours'], $aRes);
+                break;
+            case 2:
+                $aRes = getPeriod($aParam[$i], 1, 31);
+                $bRes = in_array($aDate['mday'], $aRes);
+                break;
+            case 3:
+                $aRes = getPeriod($aParam[$i], 1, 12);
+                $bRes = in_array($aDate['mon'], $aRes);
+                break;
+            case 4:
+                $aRes = getPeriod($aParam[$i], 0, 6);
+                $bRes = in_array($aDate['wday'], $aRes);
+                break;
+        }
+
+        if (!$bRes)
+            break;
+    }
+
+    return $bRes;
+}
+
+function runJob($aJob)
+{
+    if (!empty($aJob['file']) && !empty($aJob['class']) && file_exists(BX_DIRECTORY_PATH_ROOT . $aJob['file'])) {
+        if (!class_exists($aJob['class']))
+            require_once(BX_DIRECTORY_PATH_ROOT . $aJob['file']);
+
+        $oHandler = new $aJob['class']();
+        $oHandler->processing();
+    } else if (!empty($aJob['service_call']) && BxDolService::isSerializedService($aJob['service_call']))
+        BxDolService::callSerialized($aJob['service_call']);
+}
+
+bx_import('BxDolCronQuery');
+$oDb = BxDolCronQuery::getInstance();
+
+// run one time transient jobs
+$aJobsTransient = $oDb->getTransientJobs();
+if (!empty($aJobsTransient)) {
+	$oDb->deleteTransientJobs();
+
+    foreach ($aJobsTransient as $aRow)
+        runJob($aRow);
+
+    if (isset($aJobsTransient['sys_perform_upgrade']))
+        exit;
+}
+
+if (bx_check_maintenance_mode()) // don't run regular cron jobs when site is in maintenance mode
+    exit;
+
+bx_import('BxDolLanguages');
+
+// run regular cron jobs
+$aJobs = $oDb->getJobs();
+$aDate = getdate(time());
+foreach($aJobs as $aRow) {
+    if (checkCronJob($aRow['time'], $aDate))
+        runJob($aRow);
+}

+ 44 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/builder_page.php

@@ -0,0 +1,44 @@
+<?php
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinEndAdmin Dolphin Studio End Admin Pages
+ * @ingroup     DolphinStudio
+ * @{
+ */
+
+require_once('./../inc/header.inc.php');
+require_once(BX_DIRECTORY_PATH_INC . 'design.inc.php');
+require_once(BX_DOL_DIR_STUDIO_INC . 'utils.inc.php');
+
+bx_import('BxDolLanguages');
+
+bx_require_authentication(true);
+
+$sType = bx_get('type');
+if($sType === false)
+    $sType = bx_get('bp_type');
+$sType = $sType !== false ? bx_process_input($sType) : '';
+
+$sPage = bx_get('page');
+if($sPage === false)
+    $sPage = bx_get('bp_page');
+$sPage = $sPage !== false ? bx_process_input($sPage) : '';
+
+bx_import('BxTemplStudioBuilderPage');
+$oPage = new BxTemplStudioBuilderPage($sType, $sPage);
+$oPage->init();
+
+bx_import('BxDolStudioTemplate');
+$oTemplate = BxDolStudioTemplate::getInstance();
+$oTemplate->setPageNameIndex($oPage->getPageIndex());
+$oTemplate->setPageHeader($oPage->getPageHeader());
+$oTemplate->setPageContent('page_caption_code', $oPage->getPageCaption());
+$oTemplate->setPageContent('page_attributes', $oPage->getPageAttributes());
+$oTemplate->setPageContent('page_menu_code', $oPage->getPageMenu());
+$oTemplate->setPageContent('page_main_code', $oPage->getPageCode());
+$oTemplate->addCss($oPage->getPageCss());
+$oTemplate->addJs($oPage->getPageJs());
+$oTemplate->getPageCode();
+/** @} */

+ 195 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/classes/BxDolStudioDashboard.php

@@ -0,0 +1,195 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxTemplStudioPage');
+bx_import('BxDolStudioDashboardQuery');
+
+class BxDolStudioDashboard extends BxTemplStudioPage
+{
+	protected $aBlocks;
+	protected $aItemsCache;
+	protected $aItemsHTools;
+
+    function __construct()
+    {
+        parent::__construct('dashboard');
+
+        $this->oDb = new BxDolStudioDashboardQuery();
+
+        $this->aBlocks = array(
+        	'space' => 'serviceGetBlockSpace',
+        	'htools' => 'serviceGetBlockHostTools',
+        );
+
+        $this->aItemsCache = array (
+		    array('name' => 'all'),
+		    array('name' => 'db'),
+		    array('name' => 'template'),
+		    array('name' => 'css'),
+		    array('name' => 'js')
+		);
+
+		$this->aItemsHTools = array (
+	    	'PHP' => 'requirementsPHP',
+			'MySQL' => 'requirementsMySQL',
+			'Web Server' => 'requirementsWebServer',
+		);
+
+        //--- Check actions ---//
+        if(($sAction = bx_get('dbd_action')) !== false) {
+            $sAction = bx_process_input($sAction);
+
+            $aResult = array('code' => 1, 'message' => _t('_adm_err_cannot_process_action'));
+            switch($sAction) {
+            	case 'get_block':
+            		$sValue = bx_get('dbd_value');
+					if($sValue === false)
+						break;
+
+					$sValue = bx_process_input($sValue);
+					if(!isset($this->aBlocks[$sValue]))
+						break;
+						
+					$aBlock = $this->{$this->aBlocks[$sValue]}(false);
+					if(!empty($aBlock['content']))
+            			$aResult = array('code' => 0, 'data' => $aBlock['content']);
+
+            		break;
+
+				case 'check_for_upgrade':
+					$aResult = array('code' => 0, 'data' => $this->getPageCodeVersionAvailable());
+                    break;
+
+            	case 'perform_upgrade':
+            		$oUpgrader = bx_instance('BxDolUpgrader');
+			        if(!$oUpgrader->prepare())
+			        	$aResult = array('code' => 1, 'message' => $oUpgrader->getError());
+			        else
+			        	$aResult = array('code' => 0, 'message' => _t('_adm_dbd_msg_upgrade_started'));
+					break;
+
+				case 'clear_cache':
+					$sValue = bx_get('dbd_value');
+					if($sValue === false)
+						break;
+
+					$sValue = bx_process_input($sValue);
+
+					bx_import('BxDolCacheUtilities');
+					$oCacheUtilities = BxDolCacheUtilities::getInstance();
+
+					switch ($sValue) {
+				        case 'all':
+				        	$aResult = false;
+				            foreach($this->aItemsCache as $aItem) {
+				            	if($aItem['name'] == 'all')
+				            		continue;
+
+				                $aResultClear = $oCacheUtilities->clear($aItem['name']);
+				                if($aResultClear === false)
+				                	continue;
+
+								$aResult = $aResultClear;
+				                if(isset($aResult['code']) && $aResult['code'] != 0)
+									break;
+				            }
+				            break;
+
+				        case 'db':
+				        case 'template':
+				        case 'css':
+				        case 'js':
+				            $aResult = $oCacheUtilities->clear($sValue);
+				            break;
+
+				        default:
+				            $aResult = array('code' => 1, 'message' => _t('_Error Occured'));
+				    }
+
+				    if($aResult === false)
+				    	$aResult['data'] = MsgBox(_t('_adm_dbd_msg_c_all_disabled'));
+				    else if(isset($aResult['code']) && $aResult['code'] == 0)
+				        $aResult['data'] = $this->getCacheChartData(false);
+
+					break;
+
+				case 'server_audit':
+					bx_import('BxDolStudioToolsAudit');
+					$oAudit = new BxDolStudioToolsAudit();
+					echo $oAudit->generate();
+					exit;
+            }
+
+            if(!empty($aResult['message'])) {
+                bx_import('BxDolStudioTemplate');
+                $aResult['message'] = BxDolStudioTemplate::getInstance()->parseHtmlByName('page_action_result.html', array('content' => $aResult['message']));
+
+                bx_import('BxTemplStudioFunctions');
+                $aResult['message'] = BxTemplStudioFunctions::getInstance()->transBox('', $aResult['message']);
+            }
+
+            echo json_encode($aResult);
+            exit;
+        }
+    }
+
+    protected function getDbSize()
+    {
+        $iTotalSize = 0;
+        $oDb = BxDolDb::getInstance();
+
+        $aTables = $oDb->getAll('SHOW TABLE STATUS');
+        foreach($aTables as $aTable)
+            $iTotalSize += $aTable['Data_length'] + $aTable['Index_length'];
+
+        return $iTotalSize;
+    }
+
+    protected function getFolderSize($sPath)
+    {
+        $iTotalSize = 0;
+        $aFiles = scandir($sPath);
+
+        $sPath = rtrim($sPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
+        foreach($aFiles as $sFile) {
+            if(is_dir($sPath . $sFile))
+                  if($sFile != '.' && $sFile != '..')
+                      $iTotalSize += $this->getFolderSize($sPath . $sFile);
+            else
+                  $iTotalSize += filesize($sPath . $sFile);
+        }
+
+        return $iTotalSize;
+    }
+
+    protected function getCacheChartData($bAsString = true)
+    {
+		bx_import('BxDolCacheUtilities');
+		$oCacheUtilities = BxDolCacheUtilities::getInstance();
+
+    	$aChartData = array();
+    	foreach($this->aItemsCache as $aItem) {
+    		if($aItem['name'] == 'all')
+	            continue;
+
+	        $iSize = $oCacheUtilities->size($aItem['name']);
+	        if($iSize === false)
+	        	continue;
+
+	        $aChartData[] = array(bx_js_string(_t('_adm_dbd_txt_c_' . $aItem['name']), BX_ESCAPE_STR_APOS), array('v' => $iSize, 'f' => bx_js_string(_t_format_size($iSize))));
+    	}
+
+    	if(empty($aChartData))
+    		return false;
+
+    	return $bAsString ? json_encode($aChartData) : $aChartData;
+    }
+}
+
+/** @} */

+ 420 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/classes/BxDolStudioInstallerUtils.php

@@ -0,0 +1,420 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxDolInstallerUtils');
+
+define('BX_DOL_UNITY_URL_ROOT', 'http://www.boonex.com/');
+define('BX_DOL_UNITY_URL_MARKET', BX_DOL_UNITY_URL_ROOT . 'market/');
+
+class BxDolStudioInstallerUtils extends BxDolInstallerUtils implements iBxDolSingleton
+{
+    public function __construct()
+    {
+        if (isset($GLOBALS['bxDolClasses'][get_class($this)]))
+            trigger_error ('Multiple instances are not allowed for the class: ' . get_class($this), E_USER_ERROR);
+
+        parent::__construct();
+    }
+
+    /**
+     * 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 function serviceDownloadFile($aItem, $bUseFtp)
+    {
+    	return $this->performDownload($aItem, $bUseFtp);
+    }
+
+	public function servicePerform($sDirectory, $sOperation, $aParams)
+    {
+    	return $this->performAction($sDirectory, $sOperation, $aParams);
+    }
+
+    public function getModules()
+    {
+        $aModules = array();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aInstalledPathes = $aInstalledInfo = array();
+        $this->getInstalledInfo($aInstalledPathes, $aInstalledInfo);
+
+        $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;
+
+                        $sConfigPath = $sPath . $sVendor . '/' . $sModule . '/install/config.php';
+                        $aModule = $this->getConfigModule($sConfigPath, $aInstalledPathes, $aInstalledInfo);
+                        if(empty($aModule))
+                            continue;
+
+                        $aModules[$aModule['title']] = $aModule;
+                    }
+                    closedir($rHandleModule);
+                }
+            }
+            closedir($rHandleVendor);
+        }
+
+        ksort($aModules);
+        return $aModules;
+    }
+
+    public function getUpdates()
+    {
+        $aUpdates = array();
+
+        bx_import('BxDolStudioTemplate');
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aInstalledPathes = $aInstalledInfo = array();
+        $this->getInstalledInfo($aInstalledPathes, $aInstalledInfo);
+
+        $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;
+
+                        if(($rHandleUpdate = @opendir($sPath . $sVendor . '/' . $sModule . '/updates/')) !== false) {
+                            while(($sUpdate = readdir($rHandleUpdate)) !== false) {
+                                if(!is_dir($sPath . $sVendor . '/' . $sModule . '/updates/' . $sUpdate) || substr($sUpdate, 0, 1) == '.')
+                                    continue;
+
+                                $sConfigPathModule = $sPath . $sVendor . '/' . $sModule . '/install/config.php';
+                                $sConfigPathUpdate = $sPath . $sVendor . '/' . $sModule . '/updates/' . $sUpdate . '/install/config.php';
+                                $aUpdate = $this->getConfigUpdate($sConfigPathModule, $sConfigPathUpdate, $aInstalledPathes, $aInstalledInfo);
+                                if(empty($aUpdate))
+                                    continue;
+
+                                $aUpdates[$aUpdate['title']] = $aUpdate;
+                            }
+                            closedir($rHandleUpdate);
+                        }
+                    }
+                    closedir($rHandleModule);
+                }
+            }
+            closedir($rHandleVendor);
+        }
+
+        ksort($aUpdates);
+        return $aUpdates;
+    }
+
+    public function perform($sDirectory, $sOperation, $aParams = array())
+    {
+    	if(defined('BX_DOL_CRON_EXECUTE') || self::isRealOwner() || in_array($sOperation, array('install', 'uninstall', 'enable', 'disable')))
+    		return $this->performAction($sDirectory, $sOperation, $aParams);
+
+    	bx_import('BxDolCronQuery');
+		if(BxDolCronQuery::getInstance()->addTransientJobService('sys_perform_install', array('system', 'perform', array($sDirectory, $sOperation, $aParams), 'DolStudioInstallerUtils')))
+			return array('code' => 1, 'message' => _t('_adm_mod_msg_process_operation_scheduled'));
+
+		return array(
+			'code' => 2,
+            'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory),
+		);
+    }
+
+    public function checkUpdates($bAuthorizedAccess = false)
+    {
+		bx_import('BxDolModuleQuery');
+        $aModules = BxDolModuleQuery::getInstance()->getModules();
+
+        $aProducts = array();
+        foreach($aModules as $aModule) {
+            if(empty($aModule['name']))
+                continue;
+
+            $aProducts[] = array(
+                'name' => $aModule['name'],
+                'version' => $aModule['version'],
+            	'hash' => $aModule['hash'],
+            );
+        }
+        $sProducts = base64_encode(serialize($aProducts));
+
+        if($bAuthorizedAccess) {
+	        bx_import('BxDolStudioOAuth');
+	        return BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'available_updates', 'dol_products' => $sProducts));
+        }
+
+		bx_import('BxDolStudioJson');
+		return BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_browse_updates', array(
+			'products' => $sProducts,
+			'domain' => BX_DOL_URL_ROOT,
+			'user' => (int)getParam('sys_oauth_user') 
+		));
+    }
+
+    public function downloadFileAuthorized($iFileId)
+    {
+    	bx_import('BxDolStudioOAuth');
+		$aItem = BxDolStudioOAuth::getInstance()->loadItems(array('dol_type' => 'product_file', 'dol_file_id' => $iFileId));
+
+		return $this->downloadFile($aItem);
+    }
+
+    public function downloadUpdatePublic($sModuleName)
+    {
+    	bx_import('BxDolModuleQuery');
+		$aModule = BxDolModuleQuery::getInstance()->getModuleByName($sModuleName);
+
+		bx_import('BxDolStudioJson');
+		$aItem = BxDolStudioJson::getInstance()->load(BX_DOL_UNITY_URL_MARKET . 'json_download_update', array(
+			'product' => base64_encode(serialize(array(
+				'name' => $aModule['name'],
+	            'version' => $aModule['version'],
+	            'hash' => $aModule['hash'],
+			))),
+			'domain' => BX_DOL_URL_ROOT,
+			'user' => (int)getParam('sys_oauth_user') 
+		));
+
+		return $this->downloadFile($aItem);
+    }
+
+    protected function downloadFile($aItem, $bUseFtp = BX_FORCE_USE_FTP_FILE_TRANSFER)
+    {
+    	if(defined('BX_DOL_CRON_EXECUTE') || self::isRealOwner())
+    		return $this->performDownload($aItem, $bUseFtp);
+
+		bx_import('BxDolCronQuery');
+		if(BxDolCronQuery::getInstance()->addTransientJobService('sys_perform_download', array('system', 'download_file', array($aItem, $bUseFtp), 'DolStudioInstallerUtils')))
+			return _t('_adm_str_msg_download_scheduled');
+
+		return false;
+    }
+
+    protected function performDownload($aItem, $bUseFtp)
+    {
+        if(empty($aItem) || !is_array($aItem))
+            return $aItem;
+
+        //--- write ZIP archive.
+        $sFilePath = '';
+        $mixedResult = $this->performWrite($aItem, $sFilePath);
+
+        if($mixedResult !== true)
+        	return $mixedResult;
+
+        //--- Unarchive package.
+        $sPackagePath = '';
+        $mixedResult = $this->performUnarchive($sFilePath, $sPackagePath);
+
+        @unlink($sFilePath);
+        if($mixedResult !== true)
+        	return $mixedResult;
+
+        //--- Copy unarchived package.
+        $mixedResult = $this->performCopy($sPackagePath, $bUseFtp);
+
+        @bx_rrmdir($sPackagePath);
+        if($mixedResult !== true)
+        	return $mixedResult;
+
+        return true;
+    }
+
+    protected function performWrite($aItem, &$sFilePath)
+    {
+    	$sFilePath = BX_DIRECTORY_PATH_TMP . $aItem['name'];
+
+    	if(file_exists($sFilePath))
+        	@unlink($sFilePath);
+
+        if(!$rHandler = fopen($sFilePath, 'w'))
+            return _t('_adm_str_err_cannot_write');
+
+        $sContent = urldecode($aItem['content']);
+        if(!fwrite($rHandler, $sContent, strlen($sContent)))
+            return _t('_adm_str_err_cannot_write');
+
+        fclose($rHandler);
+        
+        return true;
+    }
+
+    protected function performUnarchive($sFilePath, &$sPackagePath)
+    {
+    	if(!class_exists('ZipArchive'))
+            return _t('_adm_str_err_zip_not_available');
+
+        $oZip = new ZipArchive();
+        if($oZip->open($sFilePath) !== true)
+            return _t('_adm_str_err_cannot_unzip_package');
+
+        $sPackageFolder = '';
+        if($oZip->numFiles > 0)
+        	$sPackageFolder = $oZip->getNameIndex(0);
+
+        if(empty($sPackageFolder))
+        	return _t('_adm_str_err_cannot_unzip_package');
+        
+		$sPackagePath = BX_DIRECTORY_PATH_TMP . $sPackageFolder;
+        if(file_exists($sPackagePath)) // remove existing tmp folder with the same name
+            @bx_rrmdir($sPackagePath);
+
+        if(!$oZip->extractTo(BX_DIRECTORY_PATH_TMP))
+            return _t('_adm_str_err_cannot_unzip_package');
+
+        $oZip->close();
+
+        return true;
+    }
+
+    protected function performCopy($sPackagePath, $bUseFtp)
+    {
+    	if($bUseFtp) {
+	        $sLogin = getParam('sys_ftp_login');
+	        $sPassword = getParam('sys_ftp_password');
+	        $sPath = getParam('sys_ftp_dir');
+	        if(empty($sLogin) || empty($sPassword) || empty($sPath))
+	            return _t('_adm_str_err_no_ftp_info');
+	
+	        bx_import('BxDolFtp');
+	        $oFile = new BxDolFtp($_SERVER['HTTP_HOST'], $sLogin, $sPassword, $sPath);
+	
+	        if(!$oFile->connect())
+	            return _t('_adm_str_err_cannot_connect_to_ftp');
+	
+	        if(!$oFile->isDolphin())
+	            return _t('_adm_str_err_destination_not_valid');
+        }
+        else {
+        	bx_import('BxDolFile');
+        	$oFile = BxDolFile::getInstance();
+        }
+
+        $aConfig = self::getModuleConfig($sPackagePath . 'install/config.php');
+        if(empty($aConfig) || !is_array($aConfig) || empty($aConfig['home_dir']))
+            return _t('_adm_str_err_wrong_package_format');
+
+        if(!$oFile->copy($sPackagePath, 'modules/' . $aConfig['home_dir']))
+            return _t('_adm_str_err_files_copy_failed');
+
+		return true;
+    }
+
+    protected function performAction($sDirectory, $sOperation, $aParams = array())
+    {
+        $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))
+        	return array(
+        		'code' => 2, 
+        		'message' => _t('_adm_mod_err_process_operation_failed', $sOperation, $sDirectory)
+        	); 
+
+		require_once($sPathInstaller);
+
+		$sClassName = $aConfig['class_prefix'] . $sInstallerClass;
+		$oInstaller = new $sClassName($aConfig);
+		$aResult = $oInstaller->$sOperation($aParams);
+
+        return array(
+        	'code' => $aResult['result'] ? 0 : 2,
+        	'message' => $aResult['message']
+        );
+    }
+
+    protected function getConfigModule($sConfigPath, $aInstalledPathes = array(), $aInstalledInfo = array())
+    {
+    	$aConfig = self::getModuleConfig($sConfigPath);
+        if(empty($aConfig) || !is_array($aConfig))
+            return array();
+
+        $sModulePath = $aConfig['home_dir'];
+
+        $bInstalled = !empty($aInstalledPathes) && in_array($sModulePath, $aInstalledPathes);
+        $bEnabled = $bInstalled && !empty($aInstalledInfo) && (int)$aInstalledInfo[$sModulePath]['enabled'] == 1;
+
+        return array(
+        	'type' => $aConfig['type'],
+            'name' => isset($aConfig['name']) ? $aConfig['name'] : $aConfig['home_uri'],
+            'title' => bx_process_output($aConfig['title']),
+            'vendor' => $aConfig['vendor'],
+            'version' => $aConfig['version'],
+            'uri' => $aConfig['home_uri'],
+            'dir' => $aConfig['home_dir'],
+            'note' => isset($aConfig['note']) ? bx_process_output($aConfig['note']) : '',
+            'installed' => $bInstalled,
+            'enabled' => $bInstalled && $bEnabled
+        );
+    }
+
+    protected function getConfigUpdate($sConfigPathModule, $sConfigPathUpdate, $aInstalledPathes = array(), $aInstalledInfo = array())
+    {
+        $aModule = $this->getConfigModule($sConfigPathModule, $aInstalledPathes, $aInstalledInfo);
+        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']
+        );
+    }
+
+    private function getInstalledInfo(&$aInstalledPathes, &$aInstalledInfo)
+    {
+        bx_import('BxDolModuleDb');
+        $aModules = BxDolModuleDb::getInstance()->getModules();
+
+        $aInstalledInfo = array();
+        foreach($aModules as $aModule)
+            $aInstalledInfo[$aModule['path']] = $aModule;
+
+        $aInstalledPathes = array_keys($aInstalledInfo);
+    }
+}
+
+/** @} */

+ 170 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/classes/BxDolStudioUpdater.php

@@ -0,0 +1,170 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+
+bx_import('BxDolStudioInstaller');
+
+class BxDolStudioUpdater extends BxDolStudioInstaller
+{
+    public function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+        $this->_sModulePath = $this->_sBasePath . $aConfig['module_dir'];
+
+        $this->_aActions = array_merge($this->_aActions, array(
+            'check_module_exists' => array(
+                'title' => _t('_adm_txt_modules_check_module_exists'),
+            ),
+            'check_module_version' => array(
+                'title' => _t('_adm_txt_modules_check_module_version'),
+            ),
+            'check_module_hash' => array(
+                'title' => _t('_adm_txt_modules_check_module_hash'),
+            ),
+            'update_files' => array(
+                'title' => _t('_adm_txt_modules_update_files'),
+            ),
+        ));
+    }
+
+    public function update($aParams)
+    {
+        $oDb = bx_instance('BxDolStudioInstallerQuery');
+
+        $aResult = array(
+            'operation_title' => _t('_adm_txt_modules_operation_update', $this->_aConfig['title'], $this->_aConfig['version_from'], $this->_aConfig['version_to'])
+        );
+
+        //--- Check for module to update ---//
+        $sQuery = $oDb->prepare("SELECT `id`, `version` FROM `sys_modules` WHERE `path`=? AND `uri`=? LIMIT 1", $this->_aConfig['module_dir'], $this->_aConfig['module_uri']);
+        $aModuleInfo = $oDb->getRow($sQuery);
+        if(!$aModuleInfo)
+            return array_merge($aResult, array(
+                'message' => $this->_displayResult('check_module_exists', false, '_adm_err_modules_module_not_found'),
+                'result' => false
+            ));
+
+        //--- Check version ---//
+        if($aModuleInfo['version'] != $this->_aConfig['version_from'])
+            return array_merge($aResult, array(
+                'message' => $this->_displayResult('check_module_version', false, '_adm_err_modules_wrong_version'),
+                'result' => false
+            ));
+
+        //--- Check hash ---//
+        $aFiles = array();
+        $this->hashFiles(BX_DIRECTORY_PATH_ROOT . 'modules/' . $this->_aConfig['module_dir'], $aFiles);
+
+        list($aFilesChanged, $fChangedPercent) = $this->hashCheck($aFiles, $aModuleInfo['id']);
+        $bAutoupdateForceModifiedFiles = getParam('sys_autoupdate_force_modified_files') == 'on';
+
+    	if(!empty($aFilesChanged) && !$bAutoupdateForceModifiedFiles) 
+    		return array_merge($aResult, array(
+                'message' => $this->_displayResult('check_module_hash', false, '_adm_err_modules_module_was_modified'),
+                'result' => false
+            ));
+		else if($fChangedPercent > BX_FORCE_AUTOUPDATE_MAX_CHANGED_FILES_PERCENT && $bAutoupdateForceModifiedFiles) 
+			return array_merge($aResult, array(
+                'message' => $this->_displayResult('check_module_hash', false, _t('_sys_upgrade_files_checksum_failed_too_many', round($fChangedPercent * 100))),
+                'result' => false
+            ));
+
+        //--- Perform action and check results ---//
+        $aResult = array_merge($aResult, $this->_perform('install', 'Update'));
+        if($aResult['result']) {
+            $sQuery = $oDb->prepare("UPDATE `sys_modules` SET `version`=? WHERE `id`=?", $this->_aConfig['version_to'], $aModuleInfo['id']);
+            $oDb->query($sQuery);
+
+            $oDb->deleteModuleTrackFiles($aModuleInfo['id']);
+
+            $aFiles = array();
+            $this->hashFiles(BX_DIRECTORY_PATH_ROOT . 'modules/' . $this->_aConfig['module_dir'], $aFiles);
+            foreach($aFiles as $aFile)
+                $oDb->insertModuleTrack($aModuleInfo['id'], $aFile);
+
+            //--- Remove update pckage ---//
+            $this->delete();
+        }
+
+        return $aResult;
+    }
+
+    //--- Action Methods ---//
+    public function actionUpdateFiles($bInstall = true)
+    {
+        $sPath = $this->_sHomePath . 'source/';
+        if(!file_exists($sPath))
+            return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+		$oFile = $this->_getFileManager();
+		if(empty($oFile))
+			return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+		if(!$oFile->copy($sPath . '*', 'modules/' . $this->_aConfig['module_dir']))
+			return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+		if(!empty($this->_aConfig['delete_files']) && is_array($this->_aConfig['delete_files']))
+			foreach($this->_aConfig['delete_files'] as $sFile)
+				if(!$oFile->delete('modules/' . $this->_aConfig['module_dir'] . $sFile))
+					return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+        return BX_DOL_STUDIO_INSTALLER_SUCCESS;
+    }
+
+    public function actionUpdateLanguages($bInstall = true)
+    {
+        $oDb = BxDolDb::getInstance();
+
+        bx_import('BxDolStudioLanguagesUtils');
+        $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+        $aLanguages = $oLanguages->getLanguages();
+
+        //--- Process languages' key=>value pears ---//
+        $aConfig = self::getModuleConfig($this->_sHomePath . 'install/config.php');
+        if(empty($aConfig) || !is_array($aConfig))
+            return BX_DOL_STUDIO_INSTALLER_FAILED;
+
+        $iCategoryId = $oLanguages->getLanguageCategory($aConfig['language_category']);
+
+        foreach($aLanguages as $sName => $sTitle)
+            $this->_updateLanguage($bInstall, $sName, $iCategoryId);
+
+        return $oLanguages->compileLanguage(0, true) ? BX_DOL_STUDIO_INSTALLER_SUCCESS : BX_DOL_STUDIO_INSTALLER_FAILED;
+    }
+
+    protected function _updateLanguage($bInstall, $sLanguage, $iCategoryId = 0)
+    {
+        $oDb = BxDolDb::getInstance();
+        $oLanguages = BxDolStudioLanguagesUtils::getInstance();
+
+        $sPath = $this->_sHomePath . 'install/langs/' . $sLanguage . '.xml';
+        $aLanguageInfo = $oLanguages->readLanguage($sPath, 'update');
+        if(empty($aLanguageInfo))
+            return false;
+
+        $iLanguage = $oLanguages->getLangId($sLanguage);
+
+        //--- Process delete ---//
+        if(isset($aLanguageInfo['strings_del']) && is_array($aLanguageInfo['strings_del']))
+            $oLanguages->deleteLanguageKeys($aLanguageInfo['strings_del']);
+
+        //--- Process add ---//
+        if(isset($aLanguageInfo['strings_add']) && is_array($aLanguageInfo['strings_add']))
+            $oLanguages->addLanguageKeys($iLanguage, $iCategoryId, $aLanguageInfo['strings_add']);
+
+        //--- Process update ---//
+        if(isset($aLanguageInfo['strings_upd']) && is_array($aLanguageInfo['strings_upd'])) {
+            $oLanguages->deleteLanguageKeys($aLanguageInfo['strings_upd']);
+            $oLanguages->addLanguageKeys($iLanguage, $iCategoryId, $aLanguageInfo['strings_upd']);
+        }
+
+        return true;
+    }
+}
+
+/** @} */

+ 257 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/js/builder_page.js

@@ -0,0 +1,257 @@
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+function BxDolStudioBuilderPage(oOptions) {
+	this.sActionUrl = oOptions.sActionUrl;
+	this.sPageUrl = oOptions.sPageUrl;
+    this.sObjName = oOptions.sObjName == undefined ? 'oBxDolStudioBuilderPage' : oOptions.sObjName;
+    this.sType = oOptions.sType == undefined ? '' : oOptions.sType;
+    this.sPage = oOptions.sPage == undefined ? '' : oOptions.sPage;
+    this.sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this.iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+    this.oHtmlIds = oOptions.oHtmlIds == undefined ? {} : oOptions.oHtmlIds;
+    this.oLanguages = oOptions.oLanguages == undefined ? {} : oOptions.oLanguages;
+
+    this.aSortingConf = {
+    	parent: '.adm-bp-cell-cnt',
+    	parent_prefix: 'adm-bpc-',
+		item: '.adm-bp-block',
+		placeholder: 'adm-bp-block adm-bp-block-empty'
+    };
+
+    var $this = this;
+    $(document).ready(function() {
+    	$($this.aSortingConf.parent).sortable({
+    		handle: $($this.aSortingConf.item + ' .adm-bpb-drag-handle'),
+    		items: $this.aSortingConf.item, 
+    		placeholder: $this.aSortingConf.placeholder,
+    		connectWith: $this.aSortingConf.parent,
+    		start: function(oEvent, oUi) {
+                oUi.item.addClass('adm-bp-block-dragging').width(300);
+    		},
+    		stop: function(oEvent, oUi) {
+                oUi.item.removeClass('adm-bp-block-dragging');
+                $this.reorder(oUi.item);
+    		}
+    	});
+    });
+}
+
+/**
+ * Main page methods.
+ */
+BxDolStudioBuilderPage.prototype.onChangePage = function(oSelect) {
+	document.location.href = this.parsePageUrl({page: $(oSelect).val()});
+};
+
+BxDolStudioBuilderPage.prototype.getUri = function(oElement) {
+	var mixedParams = {};
+
+	var sUri = $('#' + this.oHtmlIds['uri_field_id']).val();
+	if(sUri.length > 0)
+		mixedParams['uri'] = sUri;
+	else {
+		var bGot = false;
+
+		$.each(this.oLanguages, function(sKey, sValue) {
+			mixedParams[sKey] = $('#bx-form-input-title-' + sKey).val();
+			if(mixedParams[sKey].length > 0)
+				bGot = true;
+		});
+
+		if(!bGot)
+			return;
+	}
+
+	bx_loading(this.oHtmlIds['add_popup_id'], true);
+
+	this.performAction('uri_get', mixedParams);
+};
+
+BxDolStudioBuilderPage.prototype.onGetUri = function(oData) {
+	bx_loading(this.oHtmlIds['add_popup_id'], false);
+
+	$('#' + this.oHtmlIds['uri_field_id']).val(oData.uri);
+	$('#' + this.oHtmlIds['url_field_id']).val(oData.url);
+};
+
+BxDolStudioBuilderPage.prototype.onCreatePage = function(sType, sPage) {
+	window.location.href = this.parsePageUrl({type: sType, page: sPage});
+};
+
+BxDolStudioBuilderPage.prototype.deletePage = function() {
+	if(!confirm(aDolLang['_adm_bp_wrn_page_delete']))
+		return;
+
+	this.performAction('page_delete');
+};
+
+BxDolStudioBuilderPage.prototype.reorder = function(oDraggable) {
+	var $this = this;
+	var oDate = new Date();
+
+	var aParams = new Array();
+	$(this.aSortingConf.parent).each(function(iIndex, oElement){
+		var sId = $(oElement).attr('id');
+		aParams.push($('#' + sId).sortable('serialize', {key: 'bp_items_' + sId.replace($this.aSortingConf.parent_prefix, '') + '[]'}));
+	});
+
+	$.post(
+		this.sActionUrl + (this.sActionUrl.indexOf('?') == -1 ? '?' : '&') + aParams.join('&'),
+		{
+			bp_action: 'reorder',
+			bp_page: $this.sPage,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			if(oData.code != 0) {
+				alert(oData.message);
+				return;
+			}
+		},
+		'json'
+	);
+
+	return true;
+};
+
+/**
+ * "Add Block" popup methods. 
+ */
+BxDolStudioBuilderPage.prototype.onChangeModule = function(sName, oLink) {
+	var $this = this;
+	var oDate = new Date();
+
+	var sClass = 'bx-menu-tab-active';
+	$(oLink).parents('.bx-std-pmen-item:first').addClass(sClass).siblings('.bx-std-pmen-item').removeClass(sClass);
+
+	var sId = '#' + $this.oHtmlIds['block_list_id'] + sName;
+	if($(sId).length != 0) {
+		$('#' + this.oHtmlIds['block_lists_id'] + ' > div:visible').bx_anim('hide', this.sAnimationEffect, 0, function() {
+			$(sId).show();
+		});
+
+		return;
+	}
+
+	bx_loading(this.oHtmlIds['create_block_popup_id'], true);
+
+	$.post(
+		this.sActionUrl,
+		{
+			bp_action: 'block_list',
+			bp_module: sName,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			bx_loading($this.oHtmlIds['create_block_popup_id'], false);
+
+			$('#' + $this.oHtmlIds['block_lists_id'] + ' > div:visible').bx_anim('hide', $this.sAnimationEffect, 0, function() {
+				$(this).parent().append(oData.content);
+			});
+		},
+		'json'
+	);
+};
+
+BxDolStudioBuilderPage.prototype.onSelectBlock = function(oCheckbox) {
+	var iCounter = parseInt($('#adm-bp-cbf-counter').html());
+	iCounter += $(oCheckbox).attr('checked') == 'checked' ? 1 : -1;
+
+	$('#adm-bp-cbf-counter').html(iCounter);
+};
+
+BxDolStudioBuilderPage.prototype.onCreateBlock = function(oData) {
+	window.location.href = this.parsePageUrl({page: this.sPage});
+};
+
+BxDolStudioBuilderPage.prototype.deleteBlockImage = function(iId) {
+	bx_loading(this.oHtmlIds['edit_block_popup_id'], true);
+	this.performAction('image_delete', {id:iId});
+};
+
+BxDolStudioBuilderPage.prototype.onChangeVisibleFor = function(oSelect) {
+	$('#bx-form-element-visible_for_levels').bx_anim($(oSelect).val() == 'all' ? 'hide' : 'show', this.sAnimationEffect, this.iAnimationSpeed);
+};
+
+BxDolStudioBuilderPage.prototype.onEditBlock = function(oData) {
+	window.location.href = this.parsePageUrl({page: this.sPage});
+};
+
+BxDolStudioBuilderPage.prototype.deleteBlock = function(iId) {
+	bx_loading(this.oHtmlIds['edit_block_popup_id'], true);
+	this.performAction('block_delete', {id:iId});
+};
+
+BxDolStudioBuilderPage.prototype.onDeleteBlock = function(iId, oData) {
+	bx_loading(this.oHtmlIds['edit_block_popup_id'], false);
+	$('.bx-popup-applied:visible').dolPopupHide();
+
+	$('#' + this.oHtmlIds['block_id'] + iId).bx_anim('hide', this.sAnimationEffect, this.iAnimationSpeed, function() {
+		$(this).remove();
+	});
+};
+
+/**
+ * "Settings" popup methods.
+ */
+BxDolStudioBuilderPage.prototype.onChangeSettingGroup = function(sName, oLink) {
+	var $this = this;
+
+	var sClass = 'bx-menu-tab-active';
+	$(oLink).parents('.bx-std-pmen-item:first').addClass(sClass).siblings('.bx-std-pmen-item').removeClass(sClass);
+
+	$('#' + this.oHtmlIds['settings_groups_id'] + ' > div:visible').bx_anim('hide', this.sAnimationEffect, 0, function() {
+		$('#' + $this.oHtmlIds['settings_group_id'] + sName).show();
+	});
+};
+
+BxDolStudioBuilderPage.prototype.onChangeLayout = function(iId, oLink) {
+	$('#' + this.oHtmlIds['settings_group_id'] + 'layout > .adm-bp-layout-active').removeClass('adm-bp-layout-active');
+	$('#' + this.oHtmlIds['layout_id'] + iId).addClass('adm-bp-layout-active');
+	$("[name = 'layout_id']").val(iId);
+};
+
+BxDolStudioBuilderPage.prototype.onSaveSettings = function() {
+	window.location.href = this.parsePageUrl({page: this.sPage});
+};
+
+/**
+ * General methods.
+ */
+BxDolStudioBuilderPage.prototype.performAction = function(sAction, aParams) {
+	var $this = this;
+	var oDate = new Date();
+
+	if(aParams == undefined)
+		aParams = {};
+
+	aParams.bp_action = sAction;
+	aParams.bp_type = $this.sType;
+	aParams.bp_page = $this.sPage;
+	aParams._t = oDate.getTime();
+
+	if($('.bx-loading-ajax:visible').length == 0)
+		bx_loading('bx-std-page-columns', true);
+
+	$.post(
+		this.sActionUrl,
+		aParams,
+		function(oData) {
+			oBxDolStudioPage.processJson(oData);
+		},
+		'json'
+	);
+};
+
+BxDolStudioBuilderPage.prototype.parsePageUrl = function(aParams) {
+	var sType = aParams.type != undefined ? aParams.type : this.sType;
+	var sPage = aParams.page != undefined ? aParams.page : '';
+
+	return this.sPageUrl.replace('{0}', sType).replace('{1}', sPage);
+};
+/** @} */

+ 199 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/js/dashboard.js

@@ -0,0 +1,199 @@
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+function BxDolStudioDashboard(oOptions) {
+	this.sActionsUrl = oOptions.sActionUrl;
+    this.sObjName = oOptions.sObjName == undefined ? 'oBxDolStudioDashboard' : oOptions.sObjName;
+    this.sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
+    this.iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+
+    this.oData = [];
+
+    var $this = this;
+    $(document).ready(function() {
+    	$('.bx-dbd-block-content').bxTime();
+    });
+}
+
+BxDolStudioDashboard.prototype.checkForUpgrade = function() {
+	var $this = this;
+	var oDate = new Date();
+	var sDivId = 'bx-dbd-version';
+
+	bx_loading(sDivId, true);
+
+	$.get(
+		this.sActionsUrl,
+		{
+			dbd_action: 'check_for_upgrade',
+			_t: oDate.getTime()
+		},
+		function(oData) {
+			bx_loading(sDivId, false);
+
+			if(!oData.data)
+			    return;
+
+			$('#' + sDivId + ' .bx-dbd-version-available').html(oData.data).show();
+		},
+		'json'
+	);
+};
+
+BxDolStudioDashboard.prototype.performUpgrade = function() {
+	var $this = this;
+	var oDate = new Date();
+	var sDivId = 'bx-dbd-version';
+
+	bx_loading(sDivId, true);
+
+	$.get(
+		this.sActionsUrl,
+		{
+			dbd_action: 'perform_upgrade',
+			_t: oDate.getTime()
+		},
+		function(oData) {
+			bx_loading(sDivId, false);
+
+			if(!oData.message)
+			    return;
+
+			$this.popup(oData.message);
+		},
+		'json'
+	);
+};
+
+BxDolStudioDashboard.prototype.getBlockContent = function(sType) {
+	var $this = this;
+	var oDate = new Date();
+	var sDivId = 'bx-dbd-' + sType;
+
+	bx_loading(sDivId, true);
+
+	$.get(
+		this.sActionsUrl,
+		{
+			dbd_action: 'get_block',
+			dbd_value: sType,
+			_t: oDate.getTime()
+		},
+		function(oData) {
+			bx_loading(sDivId, false);
+
+			if(!oData.data)
+			    return;
+
+			$('#' + sDivId).replaceWith(oData.data);
+		},
+		'json'
+	);
+};
+
+BxDolStudioDashboard.prototype.initChart = function(sType, oData) {
+	var $this = this;
+	var sDivId = 'bx-dbd-' + sType;
+
+	bx_loading(sDivId, true);
+	google.load("visualization", "1", {packages:["corechart"], callback: function() {
+    	bx_loading(sDivId, false);
+    	$this.showChart(sType, oData);
+    }});
+};
+
+BxDolStudioDashboard.prototype.showChart = function(sType, oData) {
+	var oChart = $('#bx-dbd-' + sType + ' .bx-dbd-chart');
+	oChart.html('');
+
+    var oDataTable = new google.visualization.DataTable();
+    oDataTable.addColumn('string', 'Label');
+    oDataTable.addColumn('number', 'Size');   
+
+    if(oData != undefined)
+		this.oData = oData;
+    oDataTable.addRows(this.oData);
+
+    var oChart = new google.visualization.PieChart(oChart[0]);
+    oChart.draw(oDataTable, {
+		chartArea: {
+			left:10,
+			top:10,
+			width:'92%',
+			height:'92%'
+		}
+    });
+};
+
+BxDolStudioDashboard.prototype.clearCache = function(sType) {
+	var $this = this;
+	var oDate = new Date();
+	var sDivId = 'bx-dbd-cache';
+
+	$('#' + sDivId).parents('.bx-page-block-container').find('.bx-db-header .bx-popup-applied:visible').dolPopupHide();
+
+	bx_loading(sDivId, true);
+
+    $.post(
+    	sUrlStudio + 'dashboard.php', 
+        {
+    		dbd_action: 'clear_cache',
+    		dbd_value: sType,
+            _t: oDate.getTime()
+        }, 
+        function(oData) {
+        	bx_loading(sDivId, false);
+
+        	if(oData.message != undefined && oData.message.length > 0)
+    			$this.popup(oData.message);
+
+            if(oData.data != undefined) {
+            	if(typeof oData.data == 'object')
+            		$this.showChart('cache', oData.data);
+            	else if(typeof oData.data == 'string')
+            		$('#' + sDivId).html(oData.data);
+            }
+        },
+        'json'
+    );
+};
+
+BxDolStudioDashboard.prototype.serverAudit = function() {
+	var $this = this;
+	var oDate = new Date();
+	var sDivId = 'bx-dbd-htools';
+
+	$('#' + sDivId).parents('.bx-page-block-container').find('.bx-db-header .bx-popup-applied:visible').dolPopupHide();
+
+	bx_loading(sDivId, true);
+
+	$.get(
+		this.sActionsUrl,
+		{
+			dbd_action: 'server_audit',
+			_t: oDate.getTime()
+		},
+		function(sData) {
+			bx_loading(sDivId, false);
+
+			if(!sData.length)
+			    return;
+
+			$('#' + sDivId).hide().html(sData).bx_anim('show', 'fade', 'slow');
+		},
+		'html'
+	);
+};
+
+BxDolStudioDashboard.prototype.popup = function(sValue) {
+	var sId = 'bx-std-dbd-popup';
+
+    $('#' + sId).remove();
+    $('<div id="' + sId + '" style="display: none;"></div>').prependTo('body').html(sValue);
+    $('#' + sId).dolPopup({});
+};
+/** @} */

+ 124 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/js/page.js

@@ -0,0 +1,124 @@
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinStudio Dolphin Studio
+ * @{
+ */
+function BxDolStudioPage(oOptions) {
+	this.sActionsUrl = oOptions.sActionUrl;
+    this.sObjName = oOptions.sObjName == undefined ? 'oBxDolStudioPage' : oOptions.sObjName;
+    this.sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'slide' : oOptions.sAnimationEffect;
+    this.iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
+}
+
+BxDolStudioPage.prototype.processJson = function (oData) {
+	bx_loading('bx-std-page-columns', false);
+
+    //--- Show Message
+    if(oData && oData.msg != undefined)
+        alert(oData.msg);
+    if(oData && oData.message != undefined)
+        alert(oData.message);
+
+    //--- Show Popup
+    if(oData && oData.popup != undefined) {
+    	var oPopup = $(oData.popup).hide(); 
+
+    	$('#' + oPopup.attr('id')).remove();
+        oPopup.prependTo('body').dolPopup({
+            fog: {
+				color: '#fff',
+				opacity: .7
+            },
+            closeOnOuterClick: false
+        });
+    }
+
+    //--- Evaluate JS code
+    if (oData && oData.eval != undefined)
+        eval(oData.eval);
+};
+
+BxDolStudioPage.prototype.togglePopup = function(sName, oLink) {
+	var $this = this;
+
+	var sId = '#bx-std-pcap-menu-popup-' + sName;
+	if($(sId + ':visible').length > 0) {
+		$(sId).dolPopupHide();
+		return;
+	}
+
+	$(oLink).parent().addClass('bx-menu-tab-active');
+
+	var oPopupOptions = {
+		pointer:{
+			el:$(oLink)
+		},
+		onHide: function() {
+			$(oLink).parent().removeClass('bx-menu-tab-active');
+		}
+	};
+
+	switch(sName) {
+		case 'help':
+			oPopupOptions = $.extend({}, oPopupOptions, {
+				onShow: function() {
+					var oPopup = $(this);
+					var oPopupRss = oPopup.find('.RSSAggrCont');
+					if(!oPopupRss.find('.bx-loading-ajax').length)
+						return;
+
+					oPopupRss.dolRSSFeed({
+						onError: function() {
+							oPopupRss.html(_t('_adm_txt_show_help_content_empty'));
+							oPopup._dolPopupSetPosition(oPopupOptions);
+						},
+						onShow: function() {
+							oPopup._dolPopupSetPosition(oPopupOptions);
+						}
+					});
+				}
+			});
+			break;
+	}
+
+	if($(sId).html().length > 0)
+		$(sId).dolPopup(oPopupOptions);
+};
+
+BxDolStudioPage.prototype.showMessage = function(oData) {
+	if(oData.message)
+		alert(oData.message);
+
+	if(oData.on_result)
+		eval(oData.on_result);
+
+	if(oData.redirect)
+		window.location.href = oData.redirect;
+};
+
+BxDolStudioPage.prototype.bookmark = function(sPageName, oLink) {
+	var oDate = new Date();
+
+	$.get(
+		this.sActionsUrl,
+		{
+			action: 'page-bookmark',
+			page: sPageName,
+			_t:oDate.getTime()
+		},
+		function(oData) {
+			if(oData.code != 0) {
+				alert(oData.message);
+				return;
+			}
+
+			$(oLink).attr('title', oData.title);
+			$(oLink).find('img').attr('src', oData.icon).attr('title', oData.title);
+		},
+		'json'
+	);
+	return true;
+};
+/** @} */

+ 19 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/bp_add_block.html

@@ -0,0 +1,19 @@
+<div class="adm-bp-__action__-block-form bx-def-margin-top-neg bx-def-color-bg-block">
+    <div class="adm-bp-__action__-block-form-cnt">
+        __form__
+        <script type="text/javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function(formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function(oData) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        oBxDolStudioPage.processJson(oData);
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 19 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/bp_add_page.html

@@ -0,0 +1,19 @@
+<div class="adm-bp-add-form bx-def-color-bg-block">
+    <div class="adm-bp-add-form-cnt">
+        __form__
+        <script type="text/javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function(formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function(oData) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        oBxDolStudioPage.processJson(oData);
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 19 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/bp_edit_page.html

@@ -0,0 +1,19 @@
+<div class="adm-bp-edit-form bx-def-margin-top-neg bx-def-color-bg-block">
+    <div class="adm-bp-edit-form-cnt">
+        __form__
+        <script type="text/javascript">
+            $(document).ready(function () {
+                $("#__form_id__").ajaxForm({ 
+                    dataType: "json",
+                    beforeSubmit: function(formData, jqForm, options) {
+                        bx_loading($("#__form_id__"), true);
+                    },
+                    success: function(oData) {
+                        $(".bx-popup-applied:visible").dolPopupHide();
+                        oBxDolStudioPage.processJson(oData);
+                    }
+                });
+            });
+        </script>
+    </div>
+</div>

+ 18 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/bp_edit_page_form.html

@@ -0,0 +1,18 @@
+<div class="adm-bp-epf-left">
+    <div class="adm-bp-epf-left-cnt bx-def-padding-sec-top">
+        __menu__
+    </div>
+</div>
+<div class="adm-bp-epf-right">
+    <div id="__html_settings_groups_id__" class="adm-bp-epf-right-cnt bx-def-padding">
+        <bx_repeat:settings_groups>
+            <div id="__html_settings_group_id__" class="adm-bp-settings" style="<bx_if:hidden>display: none;</bx_if:hidden>">__content__</div>
+        </bx_repeat:settings_groups>
+    </div>
+</div>
+<div class="bx-std-cb"></div>
+<div class="adm-bp-epf-controls bx-def-padding-top bx-def-padding-leftright">
+    <button class="bx-btn bx-btn-primary" name="do_submit" value="__submit__" type="submit">__submit__</button>
+    <button class="bx-btn bx-def-margin-sec-left" name="do_cancel" value="<bx_text:_adm_bp_btn_page_cancel />" type="button" onclick="$('.bx-popup-applied:visible').dolPopupHide()"><bx_text:_adm_bp_btn_page_cancel /></button>
+    <div class="bx-std-cb"></div>
+</div>

+ 230 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/css/builder_page.css

@@ -0,0 +1,230 @@
+/*--- Panel Top style ---*/
+div.bx-std-pt-pages {
+	position: relative;
+    float: left;
+}
+div.bx-std-pt-pages div.bx-form-element-wrapper {
+	width: 165px;
+}
+div.bx-std-pt-buttons {
+	float: right;
+}
+
+/*--- Main section style ---*/
+div.adm-bp-cell {
+	background-color: rgba(255,255,255,0.8);
+}
+div.adm-bp-cell-cnt {
+	padding: 3px 6px 50px 6px;
+}
+div.adm-bp-blocks {
+	position: relative;
+}
+div.adm-bp-block {
+	position: relative;
+    overflow: hidden;
+	margin: 3px 0px;
+
+	border: 1px solid #b7b7b7;
+
+	border-radius: 5px 5px 5px 5px;
+
+	box-shadow: 0 0 1px rgba(51, 51, 51, 0.3);
+
+	background-color: #ececec;
+    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ececec));
+    background-image: -webkit-linear-gradient(top, #fff, #ececec); 
+    background-image: -moz-linear-gradient(top, #fff, #ececec); 
+    background-image: -ms-linear-gradient(top, #fff, #ececec); 
+    background-image: -o-linear-gradient(top, #fff, #ececec); 
+    background-image: linear-gradient(top, #fff, #ececec);	
+}
+div.adm-bpb-inactive {
+	opacity: 0.5;
+}
+div.adm-bp-block-empty {
+    height: 42px;
+	border-style: dashed;
+	box-shadow: none;
+}
+div.adm-bp-block-dragging {}
+div.adm-bpb-content {
+	position: relative;
+	padding: 5px;
+}
+div.adm-bpb-icon {
+	position: absolute;
+	width: 32px;
+	height: 32px;
+}
+div.adm-bpb-icon img {
+    width: 32px;
+    height: 32px;
+}
+div.adm-bpb-icon i.sys-icon {
+	position: absolute;
+    width:32px;
+	height: 32px;
+	top: 0px;
+	left: 0px;
+
+    color:white;
+    font-size:20px;
+    text-align:center;
+    text-shadow:none;
+	line-height: 32px;
+}
+div.adm-bpb-info {
+	min-height: 32px;
+	padding-left: 40px; 
+}
+div.adm-bpb-title {
+	font-weight: bold;
+}
+div.adm-bpb-description {
+	font-size: 11px;
+}
+div.adm-bpb-checkbox {
+	position: absolute;
+	top: 11px;
+	right: 5px;
+}
+div.adm-bpb-drag-handle {
+	position: absolute;
+    top: 14px;
+    left: 10px;
+	cursor: move;
+	color:#666666;
+    font-size:14px;
+}
+div.adm-bp-cell div.adm-bpb-icon {
+	left: 36px;
+}
+div.adm-bp-cell div.adm-bpb-info {
+	padding-left: 75px; 
+}
+
+/*--- Add Block popup style ---*/
+div#adm-bp-create-block-popup div.bx-popup-width {
+	width: 620px;
+}
+div#adm-bp-create-block-popup div.bx-popup-content {
+	padding-left: 0px;
+	padding-right: 0px; 
+}
+
+div.adm-bp-create-block-form {}
+
+div.adm-bp-create-block-form-cnt {}
+
+div.adm-bp-create-block-form fieldset.bx-form-section {
+	padding-bottom: 0px;
+}
+div.adm-bp-create-block-form div.adm-bp-block:first-child {
+	margin-top: 0px;
+}
+div.adm-bp-cbf-left {
+	position: absolute;
+    top: 0px;
+    left: 0px;
+	width: 179px;
+	height: 300px;
+	border-top: 1px solid #cccccc;
+	border-right: 1px solid #cccccc;
+	overflow-y: auto; 
+}
+div.adm-bp-cbf-left-cnt {}
+div.adm-bp-cbf-right {
+	position: relative;
+	width: 440px;
+	height: 300px;
+	margin-left: 180px;
+	border-top: 1px solid #cccccc;
+	overflow-y: auto;
+}
+div.adm-bp-cbf-right-cnt {}
+div.adm-bp-cbf-controls {
+	border-top: 1px solid #cccccc;
+}
+div.adm-bp-cbf-counter {
+	position: relative;
+	float: right;
+	line-height: 38px;
+	color: #b5b5b5;
+}
+
+/*--- Edit Block popup style ---*/
+div.adm-bp-edit-block-form {
+	margin-top: 0px;
+}
+
+/*--- Page Settings popups style ---*/
+div#adm-bp-add-popup div.bx-popup-width,
+div#adm-bp-edit-popup div.bx-popup-width {
+    width: 560px;
+}
+div#adm-bp-add-popup div.bx-popup-content,
+div#adm-bp-edit-popup div.bx-popup-content {
+    padding-left: 0px;
+    padding-right: 0px; 
+}
+
+div.adm-bp-add-form {}
+div.adm-bp-add-form-cnt {}
+
+div.adm-bp-edit-form {}
+div.adm-bp-edit-form-cnt {}
+
+div.adm-bp-add-form fieldset.bx-form-section,
+div.adm-bp-edit-form fieldset.bx-form-section {
+    padding-bottom: 0px;
+}
+div.adm-bp-add-form div.bx-std-pmen-item-cnt, 
+div.adm-bp-edit-form div.bx-std-pmen-item-cnt {
+	padding-left: 20px;
+}
+div.adm-bp-add-form div.bx-form-input-wrapper-custom, 
+div.adm-bp-edit-form div.bx-form-input-wrapper-custom {
+	position: relative;
+}
+div.adm-bp-epf-left {
+	position: absolute;
+	top: 0px;
+	left: 0px;
+    width: 179px;
+    height: 310px;
+	border-top: 1px solid #cccccc;
+    border-right: 1px solid #cccccc;
+    overflow-y: auto; 
+}
+div.adm-bp-epf-left-cnt {}
+div.adm-bp-epf-right {
+    position: relative;
+    width: 380px;
+    height: 310px;
+	margin-left: 180px;
+	border-top: 1px solid #cccccc;
+    overflow-y: auto;
+}
+div.adm-bp-epf-right-cnt {}
+div.adm-bp-epf-controls {
+    border-top: 1px solid #cccccc;
+}
+div#adm-bp-settings-group-layout {
+    margin: 0px -10px;
+}
+div.adm-bp-layout {
+	position: relative;
+	float: left;
+	width: 66px;
+	height: 66px;
+	margin-left: 10px;
+	margin-right: 10px; 
+	border: 2px solid transparent;
+}
+div.adm-bp-layout-active {
+	border-color: #9999cc;
+}
+div.adm-bp-layout-cnt {
+	padding: 1px;
+}

+ 56 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/css/dashboard.css

@@ -0,0 +1,56 @@
+div.bx-dashboard {
+	position: relative;
+}
+div.bx-dbd-column {
+	position: relative;
+	float: left;
+	width: 50%;
+}
+div.bx-dbd-block-content {
+	position: relative;
+}
+
+/*--- Versions block ---*/
+div.bx-dbd-virsion-image {
+	position: absolute;
+	top: 0px;
+	left: 0px;
+}
+div.bx-dbd-virsion-info {
+	position: relative;
+	min-height: 110px;
+	margin-left: 130px;
+}
+div.bx-dbd-vi-column {
+	position: relative;
+	float: left;
+	width: 50%;
+}
+div.bx-dbd-version-available {
+	display: none;
+}
+div.bx-dbd-version {
+	position: relative;
+	float: left;
+
+	line-height: 26px;
+	line-height: 1.6rem;
+}
+
+/*--- Chart based blocks ---*/
+div.bx-dbd-chart {
+	width: 400px;
+	height: 200px;
+	margin: 0px auto; 
+}
+
+/*--- Host Tools block ---*/
+div.bx-dbd-htools .bx-btn {
+    display: inline-block;
+    float: none;
+}
+
+div.bx-dbd-htools .bx-dbd-htools-item {
+    float: left;
+    width: 50%;
+}

+ 26 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/css/default.less

@@ -0,0 +1,26 @@
+@import url(../../../template/css/default.less);
+
+/* margins */
+.bx-def-margin-top-neg {
+    .margin-top(-@bx-margin);
+}
+
+/* widget */
+.bx-def-widget-size {
+    .width(@bx-size-widget);
+    .height(@bx-size-widget);
+}
+.bx-def-widget-size-max-height {
+    .max-height(@bx-size-widget);
+}
+.bx-def-widget-size-min-height {
+    .min-height(@bx-size-widget);
+}
+.bx-def-widget-size-left-indent {
+	.margin-left(@bx-size-widget + @bx-margin);
+}
+
+/* round corders */
+.bx-def-round-corners-widget {
+    .radius(@bx-round-corners-radius-widget);
+}

+ 45 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/css/menu_top.css

@@ -0,0 +1,45 @@
+div.bx-std-menu-top-wrp {
+    position: relative;
+    width: 100%;
+	min-height: 50px;
+}
+div.bx-std-menu-top {
+	height: 50px;
+}
+div.bx-std-menu-top-left {
+    position: absolute;
+	z-index: 3;
+    top: 0px;
+    left: 10px;
+	height: 50px;
+}
+div.bx-std-page-caption div.bx-std-menu-top-left {
+    position: absolute;
+    top: 10px;
+    left: 10px;
+	height: 50px;
+}
+div.bx-std-menu-top-center {
+    position: relative;
+	margin: 0px auto;
+	line-height: 50px;
+	text-align: center;
+	font-size: 2rem;
+}
+div.bx-std-menu-top-right {
+    position: absolute;
+    top: 0px;
+    right: 10px;
+	height: 50px;
+}
+div.bx-std-page-caption div.bx-std-menu-top-right {
+    position: absolute;
+    z-index: 3;
+    top: 10px;
+    right: 10px;
+	height: 50px;
+}
+
+div.bx-std-menu-top li.bx-menu-tab-active i.sys-icon {
+	color: #ffe900;
+}

+ 15 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/dbd_versions.html

@@ -0,0 +1,15 @@
+<div id="bx-dbd-version" class="bx-dbd-block-content bx-def-margin-bottom">
+	<div class="bx-dbd-virsion-image">
+		<img src="<bx_image_url:dbd_version.png />"/>
+	</div>
+	<div class="bx-dbd-virsion-info">
+		<div class="bx-def-margin-thd-top"><bx_text:_adm_dbd_txt_dolphin_on />&nbsp;<b>__domain__</b></div>
+		<div class="bx-def-margin-thd-top"><bx_text:_adm_dbd_txt_version />&nbsp;<b>__version__</b>&nbsp;<bx_text:_adm_dbd_txt_installed />&nbsp;<b>__installed__</b></div>
+		<div class="bx-dbd-version-available bx-def-margin-thd-top bx-clearfix"></div>
+	</div>
+	<script type="text/javascript">
+		$(document).ready(function() {
+			__js_object__.checkForUpgrade();
+		});
+	</script>
+</div>

+ 6 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/dbd_versions_upgrade.html

@@ -0,0 +1,6 @@
+<bx_if:show_version_available>
+	<div class="bx-dbd-version bx-def-margin-thd-right col-red1">__version_available__</div>
+</bx_if:show_version_available>
+<bx_if:show_upgrade_available>
+	<a class="bx-btn bx-btn-small" href="javascript:void(0)" onclick="javascript:__js_object__.performUpgrade()"><bx_text:_adm_dbd_btn_update /></a>
+</bx_if:show_upgrade_available>

+ 1621 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/scripts/BxBaseStudioBuilderPage.php

@@ -0,0 +1,1621 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinView Dolphin Studio Representation classes
+ * @ingroup     DolphinStudio
+ * @{
+ */
+
+bx_import('BxDolStudioUtils');
+bx_import('BxDolStudioBuilderPage');
+bx_import('BxTemplStudioFormView');
+
+class BxBaseStudioBuilderPage extends BxDolStudioBuilderPage
+{
+    protected $sParamsDivider = '#';
+    protected $sSelectKeyPrefix = 'id-';
+
+    protected $sActionPageCreate = 'page_create';
+    protected $sActionPageDelete = 'page_delete';
+    protected $sActionPageEdit = 'page_edit';
+    protected $sActionBlockCreate = 'block_create';
+    protected $sActionBlockEdit = 'block_edit';
+
+    protected $sBaseUrl;
+    protected $sTypeUrl;
+    protected $sPageUrl;
+
+    protected $aHtmlIds = array(
+        'add_popup_id' => 'adm-bp-add-popup',
+        'edit_popup_id' => 'adm-bp-edit-popup',
+        'uri_field_id' => 'adm-bp-field-uri',
+    	'url_field_id' => 'adm-bp-field-url',
+        'settings_group_id' => 'adm-bp-settings-group-',
+        'settings_groups_id' => 'adm-bp-settings-groups',
+        'create_block_popup_id' => 'adm-bp-create-block-popup',
+        'edit_block_popup_id' => 'adm-bp-edit-block-popup',
+        'block_id' => 'adm-bpb-',
+        'block_list_id' => 'adm-bpl-',
+        'block_lists_id' => 'adm-bp-block-lists',
+        'layout_id' => 'adm-bpl-',
+    );
+
+	protected $aPageSettings = array(
+		array('name' => 'options', 'title' => '_adm_bp_mi_page_options', 'active' => 1),
+        array('name' => 'layout', 'title' => '_adm_bp_mi_page_layout', 'active' => 0),
+        array('name' => 'visibility', 'title' => '_adm_bp_mi_page_visibility', 'active' => 0),
+        array('name' => 'cache', 'title' => '_adm_bp_mi_page_cache', 'active' => 0),
+        array('name' => 'seo', 'title' => '_adm_bp_mi_page_seo', 'active' => 0)
+	);
+
+    function __construct($sType = '', $sPage = '')
+    {
+        parent::__construct($sType, $sPage);
+
+        $this->sBaseUrl = BX_DOL_URL_STUDIO . 'builder_page.php';
+        $this->sTypeUrl = $this->sBaseUrl . '?type=%s';
+        $this->sPageUrl = $this->sTypeUrl . '&page=%s';
+    }
+
+    function getPageCss()
+    {
+        return array_merge(parent::getPageCss(), array('page_layouts.css', 'builder_page.css'));
+    }
+
+    function getPageJs()
+    {
+        return array_merge(parent::getPageJs(), array(
+            'jquery-ui/jquery.ui.core.min.js',
+            'jquery-ui/jquery.ui.widget.min.js',
+            'jquery-ui/jquery.ui.mouse.min.js',
+            'jquery-ui/jquery.ui.sortable.min.js',
+            'jquery.ui.touch-punch.min.js',
+            'jquery.easing.js',
+            'jquery.form.min.js',
+            'functions.js',
+            'builder_page.js'
+        ));
+    }
+
+    function getPageJsObject()
+    {
+        return 'oBxDolStudioBuilderPage';
+    }
+
+    function getPageMenu($aMenu = array(), $aMarkers = array())
+    {
+        $aMenuItems = array(
+            BX_DOL_STUDIO_MODULE_SYSTEM => array(
+                'name' => BX_DOL_STUDIO_MODULE_SYSTEM,
+                'icon' => 'cog',
+            ),
+            BX_DOL_STUDIO_MODULE_CUSTOM => array(
+                'name' => BX_DOL_STUDIO_MODULE_CUSTOM,
+                'icon' => 'wrench',
+                'title' => '_adm_bp_cpt_type_' . BX_DOL_STUDIO_MODULE_CUSTOM,
+            )
+        );
+
+        bx_import('BxDolModuleQuery');
+        $aModulesDb = BxDolModuleQuery::getInstance()->getModulesBy(array('type' => 'modules'));
+        foreach($aModulesDb as $aModuleDb) {
+        	$sName = $aModuleDb['name'];
+
+            if(!empty($aMenuItems[$sName]))
+                $aMenuItems[$sName] = array_merge($aMenuItems[$sName], $aModuleDb);
+            else
+                $aMenuItems[$sName] = $aModuleDb;
+
+			if(empty($aMenuItems[$sName]['icon']))
+				$aMenuItems[$sName]['icon'] = BxDolStudioUtils::getModuleIcon($aModuleDb, 'menu', false); 
+        }
+
+        $aMenu = array();
+        foreach($aMenuItems as $aMenuItem)
+            $aMenu[] = array(
+                'name' => $aMenuItem['name'],
+                'icon' => $aMenuItem['icon'],
+                'link' =>  sprintf($this->sTypeUrl, $aMenuItem['name']),
+                'title' => $aMenuItem['title'],
+                'selected' => $aMenuItem['name'] == $this->sType
+            );
+
+        return parent::getPageMenu($aMenu);
+    }
+
+    function getPageCode($bHidden = false)
+    {
+        bx_import('BxTemplPage');
+        $oPage = BxTemplPage::getObjectInstance($this->sPage);
+
+        $oTemplate = BxDolStudioTemplate::getInstance();
+        $sJsObject = $this->getPageJsObject();
+
+        $sContent = "";
+        if(($bPage = $this->sPage != '') === true) {
+            $aTmplVars = array();
+            for($i = 1; $i <= $this->aPageRebuild['layout_cells_number']; $i++) {
+                $aBlocks = array();
+                $this->oDb->getBlocks(array('type' => 'by_object_cell', 'object' => $this->aPageRebuild['object'], 'cell' => $i), $aBlocks, false);
+
+                $aTmplVarsCell = array('id' => $i, 'bx_repeat:blocks' => array());
+                foreach($aBlocks as $aBlock) {
+                	list($sIcon, $sIconUrl) = $this->getBlockIcon($aBlock);
+
+                    $aTmplVarsCell['bx_repeat:blocks'][] = array(
+                        'html_id' => $this->aHtmlIds['block_id'] . $aBlock['id'],
+                        'bx_if:is_inactive' => array(
+                            'condition' => (int)$aBlock['active'] == 0,
+                            'content' => array()
+                        ),
+                        'bx_if:show_link' => array(
+                            'condition' => true,
+                            'content' => array(
+                                'onclick' => $sJsObject . ".performAction('block_edit', {id: " . $aBlock['id'] . "})",
+                                'title' => $oPage->getBlockTitle($aBlock),
+                            )
+                        ),
+                        'bx_if:show_text' => array(
+                            'condition' => false,
+                            'content' => array()
+                        ),
+                        'bx_if:image' => array (
+			                'condition' => (bool)$sIconUrl,
+			                'content' => array('icon_url' => $sIconUrl),
+			            ),
+						'bx_if:icon' => array (
+			                'condition' => (bool)$sIcon,
+			                'content' => array('icon' => $sIcon),
+			            ),
+                        'module' => $this->getModuleTitle($aBlock['module']),
+                        'visible_for' => _t('_adm_bp_txt_visible_for', BxDolStudioUtils::getVisibilityTitle($aBlock['visible_for_levels'])),
+                        'bx_if:show_checkbox' => array(
+                            'condition' => false,
+                            'content' => array()
+                        ),
+                        'bx_if:show_drag_handle' => array(
+                            'condition' => true,
+                            'content' => array()
+                        )
+                    );
+                }
+
+                $aTmplVars['cell_' . $i] = $oTemplate->parseHtmlByName('bp_cell.html', $aTmplVarsCell);
+            }
+
+            $sContent = $oTemplate->parseHtmlByName($this->aPageRebuild['layout_template'], $aTmplVars);
+        }
+
+        bx_import('BxDolStudioLanguagesUtils');
+        $aLanguages = BxDolStudioLanguagesUtils::getInstance()->getLanguages();
+
+        $aTmplVars = array(
+            'js_object' => $sJsObject,
+            'action_url' => $this->sBaseUrl,
+            'page_url' => sprintf($this->sPageUrl, '{0}', '{1}'),
+            'type' => $this->sType,
+            'page' => $this->sPage,
+            'html_ids' => json_encode($this->aHtmlIds),
+            'languahes' => json_encode($aLanguages),
+            'bx_repeat:blocks' => array(
+                array(
+                    'caption' => '',
+                    'panel_top' => $this->getBlockPanelTop(),
+                    'items' => $sContent,
+                    'panel_bottom' => ''
+                )
+            )
+        );
+
+        $oTemplate->addJsTranslation('_adm_bp_wrn_page_delete');
+        return $oTemplate->parseHtmlByName('builder_page.html', $aTmplVars);
+    }
+
+    function getBlockPanelTop($aParams = array())
+    {
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        return parent::getBlockPanelTop(
+            array('panel_top' => $oTemplate->parseHtmlByName('bp_block_panel_top.html', $this->_getTmplVarsBlockPanelTop()))
+        );
+    }
+
+    protected function actionPageCreate()
+    {
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $sModule = BX_DOL_STUDIO_MODULE_CUSTOM;
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-page-create',
+                'action' => sprintf($this->sPageUrl, $this->sType, $this->sPage) . '&bp_action=' . $this->sActionPageCreate,
+                'method' => 'post'
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_objects_page',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'module' => array(
+                    'type' => 'hidden',
+                    'name' => 'module',
+                    'value' => $sModule,
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'deletable' => array(
+                    'type' => 'hidden',
+                    'name' => 'deletable',
+                    'value' => 1,
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'settings' => array(
+                    'type' => 'custom',
+                    'name' => 'settings',
+                    'content' => $oTemplate->parseHtmlByName('bp_edit_page_form.html', $this->_getTmplVarsPageSettings()),
+                ),
+			)
+		);
+
+		$oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+        	unset($oForm->aInputs['settings']);
+            foreach($this->aPageSettings as $aSetting)
+                $oForm->aInputs = array_merge($oForm->aInputs, $this->{'getSettings' . $this->getClassName($aSetting['name']) . 'Fields'}());
+
+        	bx_import('BxDolStudioLanguagesUtils');
+            $sLanguage = BxDolStudioLanguagesUtils::getInstance()->getCurrentLangName(false);
+
+            $sObject = BxDolForm::getSubmittedValue('title-' . $sLanguage, $aForm['form_attrs']['method']);
+            $sObject = uriGenerate($sObject, 'sys_objects_page', 'object', 'object');
+
+            $sUri = $oForm->getCleanValue('uri');
+
+            $iVisibleFor = BxDolStudioUtils::getVisibilityValue($oForm->getCleanValue('visible_for'), $oForm->getCleanValue('visible_for_levels'));
+            BxDolForm::setSubmittedValue('visible_for_levels', $iVisibleFor, $aForm['form_attrs']['method']);
+            unset($oForm->aInputs['visible_for']);
+
+            $iId = (int)$oForm->insert(array('object' => $sObject, 'url' => $this->sPageBaseUrl . $sUri));
+            if($iId != 0)
+                return array('eval' => $this->getPageJsObject() . '.onCreatePage(\'' . $sModule . '\', \'' . $sObject . '\')');
+            else
+                return array('msg' => _t('_adm_bp_err_page_create'));
+        }
+
+        bx_import('BxTemplStudioFunctions');
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->aHtmlIds['add_popup_id'], _t('_adm_bp_txt_create_popup'), $oTemplate->parseHtmlByName('bp_add_page.html', array(
+            'form_id' => $aForm['form_attrs']['id'],
+            'form' => $oForm->getCode(true)
+        )));
+
+        return array('popup' => $sContent);
+    }
+
+    protected function actionPageEdit()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-page-edit',
+                'action' => sprintf($this->sPageUrl, $this->sType, $this->sPage) . '&bp_action=' . $this->sActionPageEdit,
+                'method' => 'post'
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_objects_page',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'settings' => array(
+                    'type' => 'custom',
+                    'name' => 'settings',
+                    'content' => $oTemplate->parseHtmlByName('bp_edit_page_form.html', $this->_getTmplVarsPageSettings($this->aPageRebuild, false)),
+                ),
+            )
+        );
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            unset($oForm->aInputs['settings']);
+            foreach($this->aPageSettings as $aSetting)
+                $oForm->aInputs = array_merge($oForm->aInputs, $this->{'getSettings' . $this->getClassName($aSetting['name']) . 'Fields'}($this->aPageRebuild, false));
+
+            $iVisibleFor = BxDolStudioUtils::getVisibilityValue($oForm->getCleanValue('visible_for'), $oForm->getCleanValue('visible_for_levels'));
+            BxDolForm::setSubmittedValue('visible_for_levels', $iVisibleFor, $aForm['form_attrs']['method']);
+            unset($oForm->aInputs['visible_for']);
+
+            if($oForm->update($this->aPageRebuild['id'])) {
+                $iLevelId = $oForm->getCleanValue('layout_id');
+                if(!empty($iLevelId) && $iLevelId != $this->aPageRebuild['layout_id']) {
+                    $aLayoutOld = array();
+                    $this->oDb->getLayouts(array('type' => 'by_id', 'value' => $this->aPageRebuild['layout_id']), $aLayoutOld, false);
+
+                    $aLayoutNew = array();
+                    $this->oDb->getLayouts(array('type' => 'by_id', 'value' => $iLevelId), $aLayoutNew, false);
+
+                    if($aLayoutOld['cells_number'] > $aLayoutNew['cells_number'] && $this->oDb->resetBlocksByPage($this->sPage, $aLayoutNew['cells_number']) === false)
+                        return array('msg' => _t('_adm_bp_err_save'));
+
+                    return array('eval' => $sJsObject . '.onSaveSettings()');
+                }
+
+                return array();
+            } else
+                return array('msg' => _t('_adm_bp_err_save'));
+        }
+
+        bx_import('BxTemplStudioFunctions');
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->aHtmlIds['edit_popup_id'], _t('_adm_bp_txt_settings_popup'), $oTemplate->parseHtmlByName('bp_edit_page.html', array(
+            'form_id' => $aForm['form_attrs']['id'],
+            'form' => $oForm->getCode(true)
+        )));
+
+        return array('popup' => $sContent);
+    }
+
+    protected function actionPageDelete()
+    {
+        if(empty($this->sPage) || empty($this->aPageRebuild) || !is_array($this->aPageRebuild))
+            return array('msg' => _t('_adm_bp_err_page_delete'));
+
+        bx_import('BxDolStudioLanguagesUtils');
+        $oLangauge = BxDolStudioLanguagesUtils::getInstance();
+
+        bx_import('BxDolStorage');
+        $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+
+        $aBlocks = array();
+        $this->oDb->getBlocks(array('type' => 'by_object', 'value' => $this->sPage), $aBlocks, false);
+        if(is_array($aBlocks) && !empty($aBlocks)) {
+            foreach($aBlocks as $aBlock)
+                $this->onBlockDelete($aBlock);
+
+            $this->oDb->deleteBlocks(array('type' => 'by_object', 'value' => $this->sPage));
+        }
+
+        if($this->oDb->deletePages(array('type' => 'by_object', 'value' => $this->sPage))) {
+            $oLangauge->deleteLanguageString($this->aPageRebuild['title_system']);
+            $oLangauge->deleteLanguageString($this->aPageRebuild['title']);
+            return array('eval' => 'window.parent.location.href = "' . sprintf($this->sTypeUrl, $this->sType) . '";');
+        }
+
+        return array('msg' => _t('_adm_bp_err_page_delete'));
+    }
+
+    protected function actionBlockList()
+    {
+        $sModule = BX_DOL_STUDIO_BP_TYPE_DEFAULT;
+        if(bx_get('bp_module') !== false)
+            $sModule = bx_process_input(bx_get('bp_module'));
+
+        return array(
+            'content' => $this->getBlockList($sModule)
+        );
+    }
+
+    protected function actionBlockCreate()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $sSelected = BX_DOL_STUDIO_BP_SKELETONS;
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-block-create',
+                'action' => sprintf($this->sPageUrl, $this->sType, $this->sPage) .  '&bp_action=' . $this->sActionBlockCreate,
+                'method' => 'post'
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_pages_blocks',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'blocks' => array(
+                    'type' => 'custom',
+                    'name' => 'blocks',
+                    'content' => '',
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+            )
+        );
+
+        $aMenu = array(
+            BX_DOL_STUDIO_BP_SKELETONS => array(
+                'name' => BX_DOL_STUDIO_BP_SKELETONS,
+                'icon' => 'qrcode',
+                'title' => '_sys_block_types_skeletons',
+                'selected' => $sSelected == BX_DOL_STUDIO_BP_SKELETONS,
+            ),
+            BX_DOL_STUDIO_MODULE_SYSTEM => array(
+                'name' => BX_DOL_STUDIO_MODULE_SYSTEM,
+                'icon' => 'cog',
+                'title' => '_sys_block_types_system',
+                'selected' => $sSelected == BX_DOL_STUDIO_MODULE_SYSTEM,
+            ),
+            BX_DOL_STUDIO_MODULE_CUSTOM => array(
+                'name' => BX_DOL_STUDIO_MODULE_CUSTOM,
+                'icon' => 'wrench',
+                'title' => '_sys_block_types_custom',
+                'selected' => $sSelected == BX_DOL_STUDIO_MODULE_CUSTOM,
+            )
+        );
+
+        bx_import('BxDolModuleQuery');
+        $aModules = BxDolModuleQuery::getInstance()->getModulesBy(array('type' => 'modules'));
+        $aModulesWithBlocks = $this->oDb->getModulesWithCopyableBlocks();
+        foreach($aModules as $aModule) {
+        	$sName = $aModule['name'];
+        	if(!in_array($sName, $aModulesWithBlocks))
+        		continue;
+
+            if(!empty($aMenu[$sName]))
+                $aMenu[$sName] = array_merge($aMenu[$sName], $aModule);
+            else
+                $aMenu[$sName] = $aModule;
+                
+			if(empty($aMenu[$sName]['icon']))
+				$aMenu[$sName]['icon'] = BxDolStudioUtils::getModuleIcon($aModule, 'menu', false); 
+        }
+
+        foreach($aMenu as $sKey => $aItem)
+            $aMenu[$sKey]['onclick'] =  $sJsObject . '.onChangeModule(\'' . $aItem['name'] . '\', this);';
+
+        bx_import('BxTemplStudioMenu');
+        $oMenu = new BxTemplStudioMenu(array('template' => 'menu_side.html', 'menu_items' => $aMenu));
+
+        $aTmplParams = array(
+            'menu' => $oMenu->getCode(),
+            'html_block_lists_id' => $this->aHtmlIds['block_lists_id'],
+            'blocks' => $this->getBlockList($sSelected)
+        );
+
+        $aForm['inputs']['blocks']['content'] = $oTemplate->parseHtmlByName('bp_add_block_form.html', $aTmplParams);
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $aIds = $oForm->getCleanValue('blocks');
+
+            $aBlocks = array();
+            $this->oDb->getBlocks(array('type' => 'by_ids', 'value' => $aIds), $aBlocks, false);
+
+            bx_import('BxDolStudioLanguagesUtils');
+            $oLanguage = BxDolStudioLanguagesUtils::getInstance();
+
+            bx_import('BxDolStorage');
+            $oStorege = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+
+            $bResult = true;
+            foreach($aBlocks as $aBlock) {
+                $sTitleKey = $this->getSystemName($aBlock['title'] . '_' . time());
+                $sTitleValue = _t($aBlock['title']);
+
+                unset($aBlock['id']);
+                $aBlock['object'] = $this->sPage;
+                $aBlock['cell_id'] = 1;
+                $aBlock['module'] = $this->getBlockModule($aBlock);
+                $aBlock['title'] = $sTitleKey;
+				$aBlock['copyable'] = 0;
+                $aBlock['deletable'] = 1;
+
+                //--- Process Lang copy
+                $sContentKey = $sContentValue = "";
+                if($aBlock['type'] == BX_DOL_STUDIO_BP_BLOCK_LANG && $aBlock['content'] != '') {
+                    $sContentKey = $this->getSystemName($aBlock['content'] . '_' . time());
+                    $sContentValue = _t($aBlock['content']);
+
+                    $aBlock['content'] = $sContentKey;
+                    $oLanguage->addLanguageString($sContentKey, $sContentValue);
+                }
+
+                //--- Process Image copy
+                $iImageId = $sImageAlign = "";
+                if($aBlock['type'] == BX_DOL_STUDIO_BP_BLOCK_IMAGE && $aBlock['content'] != '') {
+                    list($iImageId, $sImageAlign) = explode($this->sParamsDivider, $aBlock['content']);
+
+                    $aBlock['content'] = "";
+                    if(is_numeric($iImageId) && (int)$iImageId != 0 && ($iImageId = $oStorege->storeFileFromStorage(array('id' => $iImageId))) !== false)
+                        $aBlock['content'] = implode($this->sParamsDivider, array($iImageId, $sImageAlign));
+                }
+
+                if(!$this->oDb->insertBlock($aBlock)) {
+                    if($sContentKey != "")
+                        $oLanguage->deleteLanguageString($sContentKey);
+
+                    if($iImageId != "")
+                        $oStorege->deleteFile((int)$iImageId, 0);
+
+                    $bResult = false;
+                    break;
+                }
+
+                $oLanguage->addLanguageString($sTitleKey, $sTitleValue);
+            }
+
+            if($bResult)
+                return array('eval' => $sJsObject . '.onCreateBlock(oData)');
+            else
+                return array('msg' => _t('_adm_bp_err_block_added'));
+        }
+
+        bx_import('BxTemplStudioFunctions');
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->aHtmlIds['create_block_popup_id'], _t('_adm_bp_txt_new_block_popup'), $oTemplate->parseHtmlByName('bp_add_block.html', array(
+        	'action' => 'create',
+            'form_id' => $aForm['form_attrs']['id'],
+            'form' => $oForm->getCode(true)
+        )));
+
+        return array('popup' => $sContent);
+    }
+
+    protected function actionBlockEdit()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $iId = (int)bx_get('id');
+        if(!$iId)
+            return array();
+
+        $aBlock = array();
+        $this->oDb->getBlocks(array('type' => 'by_id', 'value' => $iId), $aBlock, false);
+        if(empty($aBlock) || !is_array($aBlock))
+            return array('msg' => _t('_adm_bp_err_block_not_found'));
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-block-edit',
+                'action' => sprintf($this->sPageUrl, $this->sType, $this->sPage) . '&bp_action=' . $this->sActionBlockEdit,
+                'method' => 'post'
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_pages_blocks',
+                    'key' => 'id',
+                    'uri' => '',
+                    'uri_title' => '',
+                    'submit_name' => 'do_submit'
+                ),
+            ),
+            'inputs' => array (
+                'id' => array(
+                    'type' => 'hidden',
+                    'name' => 'id',
+                    'value' => $iId,
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                ),
+                'title' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title',
+                    'caption' => _t('_adm_bp_txt_block_title'),
+                    'info' => _t('_adm_bp_dsc_block_title'),
+                    'value' => $aBlock['title'],
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'length',
+                        'params' => array(3,100),
+                        'error' => _t('_adm_bp_err_block_title'),
+                    ),
+                ),
+                'designbox_id' => array(
+                    'type' => 'select',
+                    'name' => 'designbox_id',
+                    'caption' => _t('_adm_bp_txt_block_designbox'),
+                    'info' => '',
+                    'value' => $this->sSelectKeyPrefix . $aBlock['designbox_id'],
+                    'values' => array(
+                        array('key' => '', 'value' => _t('_adm_bp_txt_block_designbox_empty')),
+                    ),
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'avail',
+                        'params' => array(),
+                        'error' => _t('_adm_bp_err_block_designbox'),
+                    ),
+                ),
+                'visible_for' => array(
+                    'type' => 'select',
+                    'name' => 'visible_for',
+                    'caption' => _t('_adm_bp_txt_block_visible_for'),
+                    'info' => '',
+                    'value' => $aBlock['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_bp_txt_block_visible_for_all')),
+                        array('key' => BX_DOL_STUDIO_VISIBLE_SELECTED, 'value' => _t('_adm_bp_txt_block_visible_for_selected')),
+                    ),
+                    'required' => '0',
+                    'attrs' => array(
+                        'onchange' => $this->getPageJsObject() . '.onChangeVisibleFor(this)'
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    )
+                ),
+                'visible_for_levels' => array(
+                    'type' => 'checkbox_set',
+                    'name' => 'visible_for_levels',
+                    'caption' => _t('_adm_bp_txt_block_visible_for_levels'),
+                    'info' => _t('_adm_bp_dsc_block_visible_for_levels'),
+                    'value' => array(),
+                    'values' => array(),
+                    'tr_attrs' => array(
+                        'style' => $aBlock['visible_for_levels'] == BX_DOL_INT_MAX ? 'display:none' : ''
+                    ),
+                    'db' => array (
+                        'pass' => 'Int',
+                    )
+                ),
+                'active' => array(
+                    'type' => 'switcher',
+                    'name' => 'active',
+                    'caption' => _t('_adm_bp_txt_block_active'),
+                    'info' => '',
+                    'value' => '1',
+                    'checked' => $aBlock['active'] == '1',
+                    'db' => array (
+                        'pass' => 'Int',
+                    )
+                ),
+                'controls' => array(
+                    'name' => 'controls',
+                    'type' => 'input_set',
+                    array(
+                        'type' => 'submit',
+                        'name' => 'do_submit',
+                        'value' => _t('_adm_bp_btn_block_save'),
+                    ),
+                    array (
+                        'type' => 'reset',
+                        'name' => 'close',
+                        'value' => _t('_adm_bp_btn_block_cancel'),
+                        'attrs' => array(
+                            'onclick' => "$('.bx-popup-applied:visible').dolPopupHide()",
+                            'class' => 'bx-def-margin-sec-left',
+                        ),
+                    )
+                )
+            )
+        );
+
+        $aDBoxes = array();
+        $this->oDb->getDesignBoxes(array('type' => 'ordered'), $aDBoxes, false);
+        foreach($aDBoxes as $aDBox)
+            $aForm['inputs']['designbox_id']['values'][] = array('key' => $this->sSelectKeyPrefix . $aDBox['id'], 'value' => _t($aDBox['title']));
+
+        BxDolStudioUtils::getVisibilityValues($aBlock['visible_for_levels'], $aForm['inputs']['visible_for_levels']['values'], $aForm['inputs']['visible_for_levels']['value']);
+
+        $aForm['inputs'] = $this->addInArray($aForm['inputs'], 'visible_for_levels', $this->getBlockContent($aBlock));
+
+        if((int)$aBlock['deletable'] != 0)
+            $aForm['inputs']['controls'][] = array (
+                'type' => 'reset',
+                'name' => 'close',
+                'value' => _t('_adm_bp_btn_block_delete'),
+                'attrs' => array(
+                    'onclick' => $this->getPageJsObject() . ".deleteBlock(" . $aBlock['id'] . ")",
+                    'class' => 'bx-def-margin-sec-left',
+                ),
+            );
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $oForm->initChecker();
+
+        if($oForm->isSubmittedAndValid()) {
+            $this->onSaveBlock($oForm, $aBlock);
+
+            if($oForm->update($iId) !== false)
+                return array('eval' => $sJsObject . '.onEditBlock(oData)');
+            else
+                return array('msg' => _t('_adm_bp_err_block_edit'));
+        }
+
+        bx_import('BxTemplStudioFunctions');
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($this->aHtmlIds['edit_block_popup_id'], _t('_adm_bp_txt_edit_block_popup', _t($aBlock['title'])), $oTemplate->parseHtmlByName('bp_add_block.html', array(
+        	'action' => 'edit',
+            'form_id' => $aForm['form_attrs']['id'],
+            'form' => $oForm->getCode(true)
+        )));
+
+        return array('popup' => $sContent);
+    }
+
+    protected function actionBlockDelete()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $iId = (int)bx_get('id');
+
+        $aBlock = array();
+        $this->oDb->getBlocks(array('type' => 'by_id', 'value' => $iId), $aBlock, false);
+        if(empty($aBlock) || !is_array($aBlock))
+            return array('msg' => _t('_adm_bp_err_block_not_found'));
+
+        if((int)$aBlock['deletable'] == 0)
+            return array('msg' => _t('_adm_bp_err_block_not_deletable'));
+
+        if(!$this->oDb->deleteBlocks(array('type' => 'by_id', 'value' => $iId)))
+            return array('msg' => _t('_adm_bp_err_block_delete'));
+
+        $this->onBlockDelete($aBlock);
+        return array('eval' => $sJsObject . '.onDeleteBlock(' . $iId . ', oData)');
+    }
+
+    protected function actionImageDelete()
+    {
+        $sJsObject = $this->getPageJsObject();
+        $iId = (int)bx_get('id');
+
+        $aBlock = array();
+        $this->oDb->getBlocks(array('type' => 'by_id', 'value' => $iId), $aBlock, false);
+        if(empty($aBlock) || !is_array($aBlock))
+            return array('msg' => _t('_adm_bp_err_block_not_found'));
+
+        $iImageId = $sImageAlign = '';
+        if($aBlock['content'] != '')
+            list($iImageId, $sImageAlign) = explode($this->sParamsDivider, $aBlock['content']);
+
+        if(is_numeric($iImageId) && (int)$iImageId != 0) {
+            bx_import('BxDolStorage');
+            if(!BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES)->deleteFile((int)$iImageId, 0))
+                return array('msg' => _t('_adm_bp_err_block_content_image_preview_delete'));
+
+            $this->oDb->updateBlock($iId, array('content' => ''));
+        }
+
+        return $this->actionBlockEdit();
+    }
+
+    protected function actionUriGet()
+    {
+        bx_import('BxDolStudioLanguagesUtils');
+        $oLanguage = BxDolStudioLanguagesUtils::getInstance();
+        $aLanguages = $oLanguage->getLanguages();
+
+        $sLanguageDef = $oLanguage->getDefaultLangName();
+        $sLanguageCur = $oLanguage->getCurrentLangName(false);
+
+        $aValues = array();
+        foreach($aLanguages as $sName => $sTitle)
+            if(($mixedValue = bx_get($sName)) !== false)
+                $aValues[$sName] = bx_process_input($mixedValue);
+
+        $sUri = "";
+        if(($mixedValue = bx_get('uri')) !== false)
+        	$sUri = bx_process_input($mixedValue);
+        else if(array_key_exists('en', $aValues) && $aValues['en'] != '')
+            $sUri = $aValues['en'];
+        else if(array_key_exists($sLanguageDef, $aValues) && $aValues[$sLanguageDef] != '')
+            $sUri = $aValues[$sLanguageDef];
+        else if(array_key_exists($sLanguageCur, $aValues) && $aValues[$sLanguageCur] != '')
+            $sUri = $aValues[$sLanguageCur];
+        else if(count($aValues) > 0)
+            foreach($aValues as $sValue)
+                if(!empty($sValue)) {
+                    $sUri = $sValue;
+                    break;
+                }
+
+        $sUri = $sUri != "" ? uriGenerate($sUri, 'sys_objects_page', 'uri') : "";
+
+        bx_import('BxDolPermalinks');
+        $sUrl = BxDolPermalinks::getInstance()->permalink($this->sPageBaseUrl . $sUri);
+
+        return array('eval' => $this->getPageJsObject() . '.onGetUri(oData)', 'uri' => $sUri, 'url' => $sUrl);
+    }
+
+    protected function getSettingsOptions($aPage, $bCreate = true, $bInputsOnly = false)
+    {
+    	bx_import('BxDolPermalinks');
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-settings-seo',
+            ),
+            'params' => array (
+                'remove_form' => '1',
+                'csrf' => array(
+                    'disable' => true
+                )
+            ),
+            'inputs' => array (
+                'title_system' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title_system',
+                    'caption' => _t('_adm_bp_txt_page_title_system'),
+                    'info' => _t('_adm_bp_dsc_page_title_system'),
+                    'value' => isset($aPage['title_system']) ? $aPage['title_system'] : '',
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'length',
+                        'params' => array(3,100),
+                        'error' => _t('_adm_bp_err_page_title_system'),
+                    ),
+                ),
+                'title' => array(
+                    'type' => 'text_translatable',
+                    'name' => 'title',
+                    'caption' => _t('_adm_bp_txt_page_title'),
+                    'info' => _t('_adm_bp_dsc_page_title'),
+                    'value' => isset($aPage['title']) ? $aPage['title'] : '',
+                    'required' => '1',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                    'checker' => array (
+                        'func' => 'length',
+                        'params' => array(3,100),
+                        'error' => _t('_adm_bp_err_page_title'),
+                    ),
+                ),
+                'url' => array(
+					'type' => 'text',
+					'name' => 'url',
+					'caption' => _t('_adm_bp_txt_page_url'),
+					'info' => _t('_adm_bp_dsc_page_url'),
+					'value' => isset($aPage['url']) ? BxDolPermalinks::getInstance()->permalink($aPage['url']) : '',
+					'required' => '0',
+					'attrs' => array(
+                		'id' => $this->aHtmlIds['url_field_id'],
+						'disabled' => 'disabled'
+					),
+				)
+            )
+        );
+
+        if($bCreate) {
+        	$sJsObject = $this->getPageJsObject();
+
+        	$aForm['inputs']['title']['attrs']['onblur'] = $sJsObject . '.getUri(this);';
+
+        	$aForm['inputs'] = bx_array_insert_before(array(
+        		'uri' => array(
+					'type' => 'text',
+					'name' => 'uri',
+					'caption' => _t('_adm_bp_txt_page_uri'),
+					'info' => _t('_adm_bp_dsc_page_uri'),
+					'value' => '',
+					'required' => '1',
+        			'attrs' => array(
+						'id' => $this->aHtmlIds['uri_field_id'],
+						'onblur' => $sJsObject . '.getUri(this);'
+					),
+					'db' => array (
+						'pass' => 'Xss',
+					),
+					'checker' => array (
+						'func' => 'length',
+						'params' => array(3,100),
+					    'error' => _t('_adm_bp_err_page_uri'),
+					),
+				)
+        	), $aForm['inputs'], 'url');
+        }
+
+        if($bInputsOnly)
+            return $aForm['inputs'];
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        return $oForm->getCode();
+    }
+
+    protected function getSettingsOptionsFields($aPage = array(), $bCreate = true, $bInputsOnly = false)
+    {
+    	return $this->getSettingsOptions($aPage, $bCreate, true);
+    }
+
+    protected function getSettingsLayout($aPage = array(), $bCreate = true, $bInputsOnly = false)
+    {
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $iLayout = isset($aPage['layout_id']) ? (int)$aPage['layout_id'] : 5;
+
+        $aLayouts = array();
+        $this->oDb->getLayouts(array('type' => 'all'), $aLayouts, false);
+
+        $aTmplParams = array(
+            'bx_repeat:layouts' => array(),
+            'form' => ''
+        );
+
+        foreach($aLayouts as $aLayout)
+            $aTmplParams['bx_repeat:layouts'][] = array(
+                'id' => $aLayout['id'],
+                'html_id' => $this->aHtmlIds['layout_id'] . $aLayout['id'],
+                'js_object' => $sJsObject,
+                'bx_if:active' => array(
+                    'condition' => (int)$aLayout['id'] == $iLayout,
+                    'content' => array()
+                ),
+                'icon' => $oTemplate->getImageUrl($aLayout['icon']),
+                'title' => _t($aLayout['title']),
+            );
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-settings-seo',
+            ),
+            'params' => array (
+                'remove_form' => '1',
+                'csrf' => array(
+                    'disable' => true
+                )
+            ),
+            'inputs' => array (
+                'layout_id' => array(
+                    'type' => 'hidden',
+                    'name' => 'layout_id',
+                    'value' => $iLayout,
+                    'db' => array (
+                        'pass' => 'Int',
+                    ),
+                )
+            )
+        );
+
+        if($bInputsOnly)
+            return $aForm['inputs'];
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        $aTmplParams['form'] = $oForm->getCode();
+
+        return $oTemplate->parseHtmlByName('bp_layouts.html', $aTmplParams);
+    }
+
+    protected function getSettingsLayoutFields($aPage = array(), $bCreate = true)
+    {
+    	return $this->getSettingsLayout($aPage, $bCreate, true);
+    }
+
+    protected function getSettingsVisibility($aPage = array(), $bCreate = true, $bInputsOnly = false)
+    {
+    	$iVisibleForLevels = isset($aPage['visible_for_levels']) ? (int)$aPage['visible_for_levels'] : BX_DOL_INT_MAX;
+
+        if(isset($aPage['visible_for_levels_editable']) && (int)$aPage['visible_for_levels_editable'] == 0)
+            $aInputs = array(
+                'visible_for_levels' => array(
+                    'type' => 'custom',
+                    'name' => 'visible_for_levels',
+                    'content' => MsgBox(_t('_adm_bp_err_page_visible_for_levels'))
+                )
+            );
+        else
+            $aInputs = array(
+                'visible_for' => array(
+                    'type' => 'select',
+                    'name' => 'visible_for',
+                    'caption' => _t('_adm_bp_txt_block_visible_for'),
+                    'info' => '',
+                    'value' => $iVisibleForLevels == 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_bp_txt_block_visible_for_all')),
+                        array('key' => BX_DOL_STUDIO_VISIBLE_SELECTED, 'value' => _t('_adm_bp_txt_block_visible_for_selected')),
+                    ),
+                    'required' => '0',
+                    'attrs' => array(
+                        'onchange' => $this->getPageJsObject() . '.onChangeVisibleFor(this)'
+                    ),
+                    'db' => array (
+                        'pass' => 'Xss',
+                    )
+                ),
+                'visible_for_levels' => array(
+                    'type' => 'checkbox_set',
+                    'name' => 'visible_for_levels',
+                    'caption' => _t('_adm_bp_txt_page_visible_for_levels'),
+                    'info' => _t('_adm_bp_dsc_page_visible_for_levels'),
+                    'value' => '',
+                    'values' => array(),
+                    'tr_attrs' => array(
+                        'style' => $iVisibleForLevels == BX_DOL_INT_MAX ? 'display:none' : ''
+                    ),
+                    'db' => array (
+                        'pass' => 'Int',
+                    )
+                )
+            );
+
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-settings-visibility',
+            ),
+            'params' => array (
+                'remove_form' => '1',
+                'csrf' => array(
+                    'disable' => true
+                )
+            ),
+            'inputs' => $aInputs
+        );
+
+        BxDolStudioUtils::getVisibilityValues($iVisibleForLevels, $aForm['inputs']['visible_for_levels']['values'], $aForm['inputs']['visible_for_levels']['value']);
+
+        if($bInputsOnly)
+            return $aForm['inputs'];       	
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        return $oForm->getCode();
+    }
+
+    protected function getSettingsVisibilityFields($aPage = array(), $bCreate = true)
+    {
+    	return $this->getSettingsVisibility($aPage = array(), $bCreate, true);
+    }
+
+    protected function getSettingsCache($aPage = array(), $bCreate = true, $bInputsOnly = false)
+    {
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-settings-cache',
+            ),
+            'params' => array (
+                'remove_form' => '1',
+                'csrf' => array(
+                    'disable' => true
+                )
+            ),
+            'inputs' => array (
+                'cache_lifetime' => array(
+                    'type' => 'text',
+                    'name' => 'cache_lifetime',
+                    'caption' => _t('_adm_bp_txt_page_cache_lifetime'),
+                    'info' => _t('_adm_bp_dsc_page_cache_lifetime'),
+                    'value' => isset($aPage['cache_lifetime']) ? $aPage['cache_lifetime'] : 0,
+                    'required' => '',
+                    'attrs' => array(
+                        'disabled' => isset($aPage['cache_editable']) && (int)$aPage['cache_editable'] == 0 ? 'disabled' : ''
+                    ),
+                    'db' => array (
+                        'pass' => 'Int',
+                    )
+                ),
+            )
+        );
+
+        if($bInputsOnly)
+            return $aForm['inputs'];
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        return $oForm->getCode();
+    }
+
+    protected function getSettingsCacheFields($aPage = array(), $bCreate = true)
+    {
+    	return $this->getSettingsCache($aPage, $bCreate, true);
+    }
+
+    protected function getSettingsSeo($aPage = array(), $bCreate = true, $bInputsOnly = false)
+    {
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'adm-bp-settings-seo',
+            ),
+            'params' => array (
+                'remove_form' => '1',
+                'csrf' => array(
+                    'disable' => true
+                )
+            ),
+            'inputs' => array (
+                'meta_description' => array(
+                    'type' => 'textarea',
+                    'name' => 'meta_description',
+                    'caption' => _t('_adm_bp_txt_page_meta_description'),
+                    'info' => _t('_adm_bp_dsc_page_meta_description'),
+                    'value' => isset($aPage['meta_description']) ? $aPage['meta_description'] : '',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'meta_keywords' => array(
+                    'type' => 'textarea',
+                    'name' => 'meta_keywords',
+                    'caption' => _t('_adm_bp_txt_page_meta_keywords'),
+                    'info' => _t('_adm_bp_dsc_page_meta_keywords'),
+                    'value' => isset($aPage['meta_keywords']) ? $aPage['meta_keywords'] : '',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    ),
+                ),
+                'meta_robots' => array(
+                    'type' => 'text',
+                    'name' => 'meta_robots',
+                    'caption' => _t('_adm_bp_txt_page_meta_robots'),
+                    'info' => _t('_adm_bp_dsc_page_meta_robots'),
+                    'value' => isset($aPage['meta_robots']) ? $aPage['meta_robots'] : '',
+                    'required' => '0',
+                    'db' => array (
+                        'pass' => 'Xss',
+                    )
+                )
+            )
+        );
+
+        if($bInputsOnly)
+            return $aForm['inputs'];
+
+        $oForm = new BxTemplStudioFormView($aForm);
+        return $oForm->getCode();
+    }
+
+    protected function getSettingsSeoFields($aPage = array(), $bCreate = true)
+    {
+    	return $this->getSettingsSeo($aPage, $bCreate, true);
+    }
+
+    protected function getBlockIcon($aBlock)
+    {
+        $sIcon = $sIconUrl = "";
+
+        $sResult = '';
+        switch($aBlock['type']) {
+            case BX_DOL_STUDIO_BP_BLOCK_RAW:
+            	$sResult = 'file-text-o';
+            	break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_HTML:
+            	$sResult = 'code';
+            	break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_RSS:
+            	$sResult = 'rss';
+            	break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_IMAGE:
+            	$sResult = 'picture-o';
+            	break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_LANG:
+            	$sResult = 'globe';
+            	break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_MENU:
+            	$sResult = 'list-alt';
+            	break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_SERVICE:
+                $sResult = $this->getModuleIcon($aBlock['module'], 'page');
+                break;
+        }
+
+        if(strpos($sResult, '.') === false)
+			$sIcon = $sResult;
+		else
+        	$sIconUrl = $sResult;
+
+        return array($sIcon, $sIconUrl);
+    }
+
+    protected function getBlockModule($aBlock)
+    {
+        return  $aBlock['module'] != BX_DOL_STUDIO_BP_SKELETONS ? $aBlock['module'] : BX_DOL_STUDIO_MODULE_CUSTOM;
+    }
+
+    protected function getBlockContent($aBlock)
+    {
+        $aFields = array();
+
+        switch($aBlock['type']) {
+            case BX_DOL_STUDIO_BP_BLOCK_RAW:
+                $aFields = array(
+                    'content' => array(
+                        'type' => 'textarea',
+                        'name' => 'content',
+                        'caption' => _t('_adm_bp_txt_block_content_raw'),
+                        'info' => _t('_adm_bp_dsc_block_content_raw'),
+                        'value' => $aBlock['content'],
+                        'required' => '0',
+                        'db' => array (
+                            'pass' => 'Xss',
+                        ),
+                    ),
+                );
+                break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_HTML:
+                $aFields = array(
+                    'content' => array(
+                        'type' => 'textarea',
+                        'name' => 'content',
+                        'caption' => _t('_adm_bp_txt_block_content_raw'),
+                        'info' => _t('_adm_bp_dsc_block_content_raw'),
+                        'value' => $aBlock['content'],
+                        'required' => '0',
+                        'html' => 1,
+                        'db' => array (
+                            'pass' => 'XssHtml',
+                        ),
+                    ),
+                );
+                break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_LANG:
+                $aFields = array(
+                    'content' => array(
+                        'type' => 'textarea',
+                        'name' => 'content',
+                        'caption' => _t('_adm_bp_txt_block_content_lang'),
+                        'info' => _t('_adm_bp_dsc_block_content_lang'),
+                        'value' => $aBlock['content'] != '' ? _t($aBlock['content']) : '',
+                        'required' => '0',
+                        'html' => 3,
+                        'db' => array (
+                            'pass' => 'XssHtml',
+                        ),
+                    ),
+                );
+                break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_IMAGE:
+                $iImageId = $sImageAlign = '';
+                if($aBlock['content'] != '')
+                    list($iImageId, $sImageAlign) = explode($this->sParamsDivider, $aBlock['content']);
+
+                $aFields = array(
+                    'content' => array(
+                        'type' => 'hidden',
+                        'name' => 'content',
+                        'value' => '',
+                        'db' => array (
+                            'pass' => 'Xss',
+                        ),
+                    ),
+                    'image_file' => array(
+                        'type' => 'image_uploader',
+                        'name' => 'image_file',
+                        'caption' => _t('_adm_bp_txt_block_content_image_file'),
+                        'caption_preview' => _t('_adm_bp_txt_block_content_image_preview'),
+                        'info' => _t('_adm_bp_dsc_block_content_image_file'),
+                        'ajax_action_delete' => $this->getPageJsObject() . '.deleteBlockImage(' . $aBlock['id'] . ')',
+                        'value' => (int)$iImageId
+                    ),
+                    'image_align' => array(
+                        'type' => 'select',
+                        'name' => 'image_align',
+                        'caption' => _t('_adm_bp_txt_block_content_image_align'),
+                        'info' => '',
+                        'value' => $sImageAlign,
+                        'values' => array(
+                            array('key' => '', 'value' => _t('_adm_bp_txt_block_content_image_align_empty')),
+                            array('key' => 'left', 'value' => _t('_adm_bp_txt_block_content_image_align_left')),
+                            array('key' => 'center', 'value' => _t('_adm_bp_txt_block_content_image_align_center')),
+                            array('key' => 'right', 'value' => _t('_adm_bp_txt_block_content_image_align_right')),
+                        ),
+                        'required' => '0',
+                        'db' => array (
+                            'pass' => 'Xss',
+                        ),
+                    ),
+                );
+                break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_RSS:
+                $sRssUrl = $sRssLength = '';
+                if($aBlock['content'] != '')
+                    list($sRssUrl, $sRssLength) = explode($this->sParamsDivider, $aBlock['content']);
+
+                $aFields = array(
+                    'content' => array(
+                        'type' => 'hidden',
+                        'name' => 'content',
+                        'value' => '',
+                        'db' => array (
+                            'pass' => 'Xss',
+                        ),
+                    ),
+                    'rss_url' => array(
+                        'type' => 'text',
+                        'name' => 'rss_url',
+                        'caption' => _t('_adm_bp_txt_block_content_rss_url'),
+                        'info' => _t('_adm_bp_dsc_block_content_rss_url'),
+                        'value' => $sRssUrl,
+                        'required' => '0',
+                        'db' => array (
+                            'pass' => 'Xss',
+                        ),
+                    ),
+                    'rss_length' => array(
+                        'type' => 'text',
+                        'name' => 'rss_length',
+                        'caption' => _t('_adm_bp_txt_block_content_rss_length'),
+                        'info' => _t('_adm_bp_dsc_block_content_rss_length'),
+                        'value' => $sRssLength,
+                        'required' => '0',
+                        'db' => array (
+                            'pass' => 'Int',
+                        ),
+                    ),
+                );
+                break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_MENU:
+                $aFields = array(
+                    'content' => array(
+                        'type' => 'select',
+                        'name' => 'content',
+                        'caption' => _t('_adm_bp_txt_block_content_menu'),
+                        'info' => '',
+                        'value' => $aBlock['content'],
+                        'values' => array(),
+                        'required' => '0',
+                        'db' => array (
+                            'pass' => 'Xss',
+                        ),
+                    )
+                );
+
+                $aMenus = $this->oDb->getMenus();
+                foreach($aMenus as $sKey => $sValue)
+                    $aFields['content']['values'][$sKey] = _t($sValue);
+
+                asort($aFields['content']['values']);
+                $aFields['content']['values'] = array_merge(array('' => _t('_adm_bp_txt_block_content_menu_empty')), $aFields['content']['values']);
+                break;
+
+            case BX_DOL_STUDIO_BP_BLOCK_SERVICE:
+                $aService = array('module' => '', 'method' => '');
+                if($aBlock['content'] != '')
+                    $aService = unserialize($aBlock['content']);
+
+                $aFields = array(
+                    'service_module' => array(
+                        'type' => 'value',
+                        'name' => 'service_module',
+                        'caption' => _t('_adm_bp_txt_block_content_service_module'),
+                        'value' => $this->getModuleTitle($aService['module'])
+                    ),
+                    'service_method' => array(
+                        'type' => 'value',
+                        'name' => 'service_method',
+                        'caption' => _t('_adm_bp_txt_block_content_service_method'),
+                        'value' => $aService['method']
+                    )
+                );
+                break;
+        }
+
+        return $aFields;
+    }
+
+    protected function getBlockList($sModule)
+    {
+        if(empty($sModule))
+            return '';
+
+        $sJsObject = $this->getPageJsObject();
+        $oTemplate = BxDolStudioTemplate::getInstance();
+
+        $oForm = new BxTemplStudioFormView(array());
+
+        $aInputCheckbox = array(
+            'type' => 'checkbox',
+            'name' => 'blocks[]',
+            'attrs' => array(
+                'onChange' => 'javascript:' . $sJsObject . '.onSelectBlock(this);'
+            ),
+            'value' => ''
+        );
+
+        $aTmplParams = array(
+            'html_block_list_id' => $this->aHtmlIds['block_list_id'] . $sModule,
+            'bx_repeat:blocks' => array()
+        );
+
+        $aBlocks = $this->getBlocks($sModule);
+        foreach($aBlocks as $aBlock) {
+        	list($sIcon, $sIconUrl) = $this->getBlockIcon($aBlock);
+
+            $aInputCheckbox['value'] = $aBlock['id'];
+
+            $aTmplParams['bx_repeat:blocks'][] = array(
+                'js_object' => $sJsObject,
+                'html_id' => $this->aHtmlIds['block_id'] . $aBlock['id'],
+                'bx_if:is_inactive' => array(
+                    'condition' => false,
+                    'content' => array()
+                ),
+                'bx_if:show_link' => array(
+                    'condition' => false,
+                    'content' => array()
+                ),
+                'bx_if:show_text' => array(
+                    'condition' => true,
+                    'content' => array(
+                        'title' => _t($aBlock['title']),
+                    )
+                ),
+                'bx_if:image' => array (
+	                'condition' => (bool)$sIconUrl,
+	                'content' => array('icon_url' => $sIconUrl),
+	            ),
+				'bx_if:icon' => array (
+	                'condition' => (bool)$sIcon,
+	                'content' => array('icon' => $sIcon),
+	            ),
+                'module' => $this->getModuleTitle($aBlock['module']),
+                'visible_for' => _t('_adm_bp_txt_visible_for', BxDolStudioUtils::getVisibilityTitle($aBlock['visible_for_levels'])),
+                'bx_if:show_checkbox' => array(
+                    'condition' => true,
+                    'content' => array(
+                        'content' => $oForm->genRow($aInputCheckbox)
+                    )
+                ),
+                'bx_if:show_drag_handle' => array(
+                    'condition' => false,
+                    'content' => array()
+                )
+            );
+        }
+
+        return $oTemplate->parseHtmlByName('bp_blocks_list.html', $aTmplParams);
+    }
+
+    protected function onBlockDelete($aBlock)
+    {
+        bx_import('BxDolStudioLanguagesUtils');
+        BxDolStudioLanguagesUtils::getInstance()->deleteLanguageString($aBlock['title']);
+
+        //--- Process Lang block
+        if($aBlock['type'] == BX_DOL_STUDIO_BP_BLOCK_LANG && $aBlock['content'] != '')
+            BxDolStudioLanguagesUtils::getInstance()->deleteLanguageString($aBlock['content']);
+
+        //--- Process Image block
+        if($aBlock['type'] == BX_DOL_STUDIO_BP_BLOCK_IMAGE && $aBlock['content'] != '') {
+            $iImageId = $sImageAlign = '';
+            list($iImageId, $sImageAlign) = explode($this->sParamsDivider, $aBlock['content']);
+
+            if(is_numeric($iImageId) && (int)$iImageId != 0) {
+                bx_import('BxDolStorage');
+                BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES)->deleteFile((int)$iImageId, 0);
+            }
+        }
+    }
+
+    protected function getBlocks($sModule)
+    {
+        $aBlocks = array();
+        $this->oDb->getBlocks(array('type' => 'by_module_to_copy', 'value' => $sModule), $aBlocks, false);
+
+        return $aBlocks;
+    }
+
+    protected function _getTmplVarsBlockPanelTop()
+    {
+        $sJsObject = $this->getPageJsObject();
+
+        $oForm = new BxTemplStudioFormView(array());
+
+        $aInputPages = array(
+            'type' => 'select',
+            'name' => 'page',
+            'attrs' => array(
+                'onChange' => 'javascript:' . $this->getPageJsObject() . '.onChangePage(this)'
+            ),
+            'value' => $this->sPage,
+            'values' => array(
+                array('key' => '', 'value' => _t('_adm_bp_txt_select_page'))
+            )
+        );
+
+        $aPages = $aCounter = array();
+        $this->oDb->getPages(array('type' => 'by_module', 'value' => $this->sType), $aPages, false);
+        $this->oDb->getBlocks(array('type' => 'counter_by_pages'), $aCounter, false);
+        foreach($aPages as $aPage)
+            $aInputPages['values'][] = array('key' => $aPage['object'], 'value' => _t($aPage['title_system']) . " (" . (isset($aCounter[$aPage['object']]) ? $aCounter[$aPage['object']] : "0") . ")");
+
+        $aTmplVarsActions = array();
+        if(($this->sPage != '' && !empty($this->aPageRebuild)) !== false)
+            $aTmplVarsActions = $this->_getTmplVarsBlockPanelTopActions();
+
+        return array(
+            'js_object' => $this->getPageJsObject(),
+            'selector' => $oForm->genRow($aInputPages),
+            'action_page_create' => $this->sActionPageCreate,
+            'bx_if:show_actions' => array(
+                'condition' => $this->sPage != '',
+                'content' => $aTmplVarsActions
+            )
+        );
+    }
+
+    protected function _getTmplVarsBlockPanelTopActions()
+    {
+        $sJsObject = $this->getPageJsObject();
+
+        bx_import('BxDolPermalinks');
+        $oPermalinks = BxDolPermalinks::getInstance();
+
+        return array(
+            'js_object' => $sJsObject,
+            'url_view' => BX_DOL_URL_ROOT . $oPermalinks->permalink($this->aPageRebuild['url']),
+            'action_page_edit' => $this->sActionPageEdit,
+            'bx_if:can_delete' => array(
+                'condition' => (int)$this->aPageRebuild['deletable'] == 1,
+                'content' => array(
+                    'js_object' => $sJsObject
+                )
+            ),
+            'action_block_create' => $this->sActionBlockCreate,
+        );
+    }
+
+    protected function _getTmplVarsPageSettings($aPage = array(), $bCreate = true)
+    {
+    	$sJsObject = $this->getPageJsObject();
+
+        $aTmplParams = array(
+            'menu' => array(),
+            'html_settings_groups_id' => $this->aHtmlIds['settings_groups_id'],
+            'bx_repeat:settings_groups' => array(),
+        	'submit' => _t($bCreate ? '_adm_bp_btn_page_create' : '_adm_bp_btn_page_apply')
+        );
+        foreach($this->aPageSettings as $aSetting) {
+            //--- get menu items
+            $aTmplParams['menu'][$aSetting['name']] = array(
+                'name' => $aSetting['name'],
+                'icon' => '',
+                'onclick' => $sJsObject . '.onChangeSettingGroup(\'' . $aSetting['name'] . '\', this);',
+                'title' => $aSetting['title'],
+                'selected' => isset($aSetting['active']) && (int)$aSetting['active'] == 1
+            );
+
+            //--- get settings
+            $aTmplParams['bx_repeat:settings_groups'][] = array(
+                'html_settings_group_id' => $this->aHtmlIds['settings_group_id'] .  $aSetting['name'],
+                'bx_if:hidden' => array(
+                    'condition' => $aSetting['active'] != 1,
+                    'content' => array()
+                ),
+                'content' => $this->{'getSettings' . $this->getClassName($aSetting['name'])}($aPage, $bCreate)
+            );
+        }
+
+        bx_import('BxTemplStudioMenu');
+        $oMenu = new BxTemplStudioMenu(array('template' => 'menu_side.html', 'menu_items' => $aTmplParams['menu']));
+        $aTmplParams['menu'] = $oMenu->getCode();
+
+        return $aTmplParams;
+    }
+}
+
+/** @} */

+ 295 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/scripts/BxBaseStudioDashboard.php

@@ -0,0 +1,295 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinView Dolphin Studio Representation classes
+ * @ingroup     DolphinStudio
+ * @{
+ */
+
+bx_import('BxDolStudioDashboard');
+
+class BxBaseStudioDashboard extends BxDolStudioDashboard
+{
+    function __construct()
+    {
+        parent::__construct();
+    }
+
+    function getPageCss()
+    {
+        return array_merge(parent::getPageCss(), array(
+        	'page_layouts.css', 
+        	'dashboard.css',
+        ));
+    }
+
+    function getPageJs()
+    {
+        return array_merge(parent::getPageJs(), array(
+        	'jquery.anim.js',
+        	'dashboard.js'
+        ));
+    }
+
+	function getPageJsClass()
+    {
+        return 'BxDolStudioDashboard';
+    }
+
+    function getPageJsObject()
+    {
+        return 'oBxDolStudioDashboard';
+    }
+
+	function getPageJsCode($aOptions = array(), $bWrap = true)
+    {
+    	$sContent = BxDolStudioTemplate::getInstance()->_wrapInTagJs('https://www.google.com/jsapi');
+
+        $aOptions = array_merge($aOptions, array(
+            'sActionUrl' => BX_DOL_URL_STUDIO . 'dashboard.php',
+        	'sVersion' => '__version__'
+        ));
+        $sContent .= parent::getPageJsCode($aOptions, $bWrap);
+
+		return $sContent;
+    }
+
+    function getPageCode($bHidden = false)
+    {
+    	bx_import('BxDolPage');
+    	$oPage = BxDolPage::getObjectInstance('sys_std_dashboard');
+    	return $oPage->getCode();
+    }
+
+	public function getPageCodeVersionAvailable()
+	{
+		list($sVersionAvailable, $bUpgradeAvailable) = $this->getVersionUpgradeAvailable();
+		if(!$sVersionAvailable && !$bUpgradeAvailable)
+			return '';
+
+    	if($sVersionAvailable !== false)
+    		$sVersionAvailable = _t('_adm_dbd_txt_dolphin_n_available', $sVersionAvailable);
+
+		return BxDolStudioTemplate::getInstance()->parseHtmlByName('dbd_versions_upgrade.html', array(
+			'bx_if:show_version_available' => array(
+        		'condition' => !empty($sVersionAvailable),
+        		'content' => array(
+        			'version_available' => $sVersionAvailable
+        		)
+        	),
+			'bx_if:show_upgrade_available' => array(
+        		'condition' => $bUpgradeAvailable,
+        		'content' => array(
+        			'js_object' => $this->getPageJsObject()
+        		)
+        	)
+		));
+	}
+
+    public function serviceGetWidgetNotices() {
+    	$iResult = 0;
+
+    	//--- Check Version
+    	list($sVersionAvailable) = $this->getVersionUpgradeAvailable();
+    	if($sVersionAvailable !== false)
+    		$iResult += 1;
+
+    	//--- Check Host Requirements
+    	bx_import('BxDolStudioToolsAudit');
+		$oAudit = new BxDolStudioToolsAudit();
+
+    	foreach($this->aItemsHTools as $sTitle => $sFunc) {
+    		$aResult = $oAudit->checkRequirements(BX_DOL_AUDIT_FAIL, $sFunc);
+			if(!empty($aResult)) {
+				$iResult += 1;
+				break;
+			}
+    	}
+
+    	return $iResult;
+    }
+
+	public function serviceGetBlockVersion()
+    {
+    	$sJsObject = $this->getPageJsObject();
+
+        $sContent = BxDolStudioTemplate::getInstance()->parseHtmlByName('dbd_versions.html', array(
+        	'js_object' => $sJsObject,
+            'domain' => getParam('site_title'),
+            'version' => bx_get_ver(),
+            'installed' => bx_time_js(getParam('sys_install_time'))
+        ));
+
+    	return array('content' => $sContent);
+    }
+
+    public function serviceGetBlockSpace($bDynamic = false)
+    {
+    	$bInclideScriptSpace = false; //Use dynamic loading by default if this setting is enabled.
+
+    	$sJsObject = $this->getPageJsObject();
+
+    	$aChartData = array();
+    	if(!$bDynamic) {
+	    	$aItems = array(
+	    		array('label' => '_adm_dbd_txt_su_database', 'value' => $this->getDbSize()),
+	    	);
+
+	    	if($bInclideScriptSpace) {
+	    		$iSizeDiskTotal = $this->getFolderSize(BX_DIRECTORY_PATH_ROOT);
+	    		$iSizeDiskMedia = $this->getFolderSize(BX_DIRECTORY_STORAGE);
+	    	
+	    		$aItems[] = array('label' => '_adm_dbd_txt_su_system', 'value' => $iSizeDiskTotal - $iSizeDiskMedia);
+	    	}
+
+	    	bx_import('BxDolModuleQuery');
+	    	$aModules = BxDolModuleQuery::getInstance()->getModulesBy(array('type' => 'all'));
+	    	foreach($aModules as $aModule) {
+	    		$sName = $aModule['name'];
+	    		$sTitle = $aModule['title'];
+
+	    		if($aModule['name'] == 'system') {
+	    			$sName = 'sys';
+	    			$sTitle = _t('_adm_dbd_txt_su_system_media');	    			
+	    		}
+
+				$aItems[] = array(
+					'label' => $sTitle, 
+					'value' => (int)$this->oDb->getModuleStorageSize($sName)
+				);
+	    	}
+
+	    	$iSizeTotal = 0;
+	        $aChartData = array();
+	        foreach($aItems as $sColor => $aItem) {
+	        	$iSizeTotal += $aItem['value'];
+	            $aChartData[] = array(bx_js_string(strip_tags(_t($aItem['label'])), BX_ESCAPE_STR_APOS), array('v' => $aItem['value'], 'f' => bx_js_string(_t_format_size($aItem['value']))));
+	        }
+    	}
+
+        $sContent = BxDolStudioTemplate::getInstance()->parseHtmlByName('dbd_space.html', array(
+        	'bx_if:show_content' => array(
+        		'condition' => !$bDynamic,
+        		'content' => array(
+		        	'js_object' => $sJsObject,
+		        	'chart_data' => json_encode($aChartData)
+       			)
+       		),
+       		'bx_if:show_loader' => array(
+       			'condition' => $bDynamic,
+       			'content' => array(
+       				'js_object' => $sJsObject,
+       			)
+       		)
+        ));
+
+        return array('content' => $sContent);
+    }
+
+	public function serviceGetBlockHostTools($bDynamic = true)
+	{
+		$sJsObject = $this->getPageJsObject();
+
+		$aMenu = array(
+			array(
+				'name' => 'audit', 
+				'title' => _t('_sys_inst_server_audit'), 
+				'link' => 'javascript:void(0)',
+				'onclick' => $sJsObject . '.serverAudit()'
+			)
+		);
+
+		bx_import('BxDolStudioToolsAudit');
+		$oAudit = new BxDolStudioToolsAudit();
+
+		$aTmplVarsItems = array();
+		if(!$bDynamic) {
+
+	        bx_import('BxTemplFunctions');
+	        $oFunc = BxTemplFunctions::getInstance();
+
+	        $aLevels = array (BX_DOL_AUDIT_FAIL, BX_DOL_AUDIT_WARN, BX_DOL_AUDIT_UNDEF);
+
+	        $aTmplVarsItems = array();
+	        foreach ($this->aItemsHTools as $sTitle => $sFunc) {
+	            $sStatus = BX_DOL_AUDIT_OK;
+	            foreach ($aLevels as $sLevel) { 
+	                $a = $oAudit->checkRequirements($sLevel, $sFunc);
+	                if (!empty($a)) {
+	                    $sStatus = $sLevel;
+	                    break;
+	                }
+	            }
+	            $aTmplVarsItems[] = array('status' => $oFunc->statusOnOff($sStatus), 'msg' => _t('_adm_dbd_txt_htools_status', $sTitle, $oAudit->typeToTitle($sStatus)));
+	        }
+		}
+
+        $sContent = BxDolStudioTemplate::getInstance()->parseHtmlByName('dbd_htools.html', array(
+        	'bx_if:show_content' => array(
+        		'condition' => !$bDynamic,
+        		'content' => array(
+	    			'bx_repeat:items' => $aTmplVarsItems,
+       			)
+        	),
+        	'bx_if:show_loader' => array(
+       			'condition' => $bDynamic,
+       			'content' => array(
+       				'js_object' => $sJsObject,
+       			)
+       		)
+	    ));
+
+		return array('content' => $sContent, 'menu' => $aMenu);
+	}
+
+	public function serviceGetBlockCache()
+	{
+		$sJsObject = $this->getPageJsObject();
+
+		$sChartData = $this->getCacheChartData();
+		$bChartData = $sChartData !== false;
+
+		$aMenu = array();
+		if($bChartData)
+		    foreach($this->aItemsCache as $aItem)
+				$aMenu[] = array(
+					'name' => $aItem['name'], 
+					'title' => _t('_adm_dbd_txt_c_clear_' . $aItem['name']), 
+					'link' => 'javascript:void(0)', 
+					'onclick' => $sJsObject . ".clearCache('" . $aItem['name'] . "')"
+				);
+
+		$sContent = BxDolStudioTemplate::getInstance()->parseHtmlByName('dbd_cache.html', array(
+			'bx_if:show_chart' => array(
+				'condition' => $bChartData,
+				'content' => array(
+					'js_object' => $sJsObject,
+		        	'chart_data' => $sChartData,
+				)
+			),
+			'bx_if:show_empty' => array(
+				'condition' => !$bChartData,
+				'content' => array(
+					'message' => MsgBox(_t('_adm_dbd_msg_c_all_disabled'))
+				)
+			),
+		));
+
+		return array('content' => $sContent, 'menu' => $aMenu);
+	}
+
+    private function getVersionUpgradeAvailable()
+    {
+    	$oUpgrader = bx_instance('BxDolUpgrader'); 
+    	$aUpdateInfo = $oUpgrader->getVersionUpdateInfo();
+
+    	$mixedVersion = $oUpgrader->isNewVersionAvailable($aUpdateInfo) ? $aUpdateInfo['latest_version'] : false;
+    	$bUpgrade = $oUpgrader->isUpgradeAvailable($aUpdateInfo);
+
+    	return array($mixedVersion, $bUpgrade);
+    }
+}
+
+/** @} */

+ 220 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/scripts/BxBaseStudioFormView.php

@@ -0,0 +1,220 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
+ * CC-BY License - http://creativecommons.org/licenses/by/3.0/
+ *
+ * @defgroup    DolphinView Dolphin Studio Representation classes
+ * @ingroup     DolphinStudio
+ * @{
+ */
+
+bx_import('BxDolStudioForm');
+
+class BxBaseStudioFormView extends BxDolStudioForm
+{
+    function __construct($aInfo, $oTemplate)
+    {
+        parent::__construct($aInfo, $oTemplate);
+    }
+
+    function addCssJs()
+    {
+        parent::addCssJs();
+
+        $this->oTemplate->addJs(array(
+            'forms.js'
+        ));
+    }
+
+    function genRow(&$aInput)
+    {
+        $sRow = '';
+        switch($aInput['type']) {
+            case 'image_uploader':
+                $sRow = $this->genRowImageUploader($aInput);
+                break;
+
+             default:
+                $sRow = parent::genRow($aInput);
+        }
+
+        return $sRow;
+    }
+
+    function genInput(&$aInput)
+    {
+        $sInput = '';
+        switch($aInput['type']) {
+            case 'text_translatable':
+                $sInput = $this->genInputTranslatable($aInput, 'text');
+                break;
+
+            case 'textarea_translatable':
+                $aInput['html'] = 0;
+                $sInput = $this->genInputTranslatable($aInput, 'textarea');
+                break;
+
+            default:
+                $sInput = parent::genInput($aInput);
+        }
+
+        return $sInput;
+    }
+
+    function genRowImageUploader(&$aInput)
+    {
+        $aTmplVarsPreview = array(
+            'bx_if:show_empty' => array(
+                'condition' => true,
+                'content' => array()
+            ),
+            'bx_if:show_image' => array(
+                'condition' => false,
+                'content' => array()
+            )
+        );
+
+        if(($iId = (int)$aInput['value']) != 0) {
+            bx_import('BxDolStorage');
+            $sStorage = isset($aInput['storage_object']) && $aInput['storage_object'] != '' ? $aInput['storage_object'] : BX_DOL_STORAGE_OBJ_IMAGES;
+            $oStorage = BxDolStorage::getObjectInstance($sStorage);
+
+            $sFileUrl = $oStorage->getFileUrlById($iId);
+            if($sFileUrl !== false) {
+                $aTmplVarsPreview['bx_if:show_empty']['condition'] = false;
+                $aTmplVarsPreview['bx_if:show_image'] = array(
+                    'condition' => true,
+                    'content' => array(
+                        'url' => $sFileUrl,
+                        'bx_if:show_action_delete' => array(
+                            'condition' => isset($aInput['ajax_action_delete']) && $aInput['ajax_action_delete'] != "",
+                            'content' => array(
+                                'action' => $aInput['ajax_action_delete']
+                            )
+                        )
+                    )
+                );
+            }
+        }
+
+        $sRow = '';
+        $aInputPreview = array(
+            'type' => 'custom',
+            'name' => 'preview',
+            'caption' => isset($aInput['caption_preview']) ? $aInput['caption_preview'] : _t('_adm_txt_form_view_iu_preview'),
+            'content' => $this->oTemplate->parseHtmlByName('form_view_iu_preview.html', $aTmplVarsPreview)
+        );
+        $sRow .= $this->genRow($aInputPreview);
+
+        $aInput['type'] = 'file';
+        $sRow .= $this->genRow($aInput);
+
+        return $sRow;
+    }
+
+    function genInputTranslatable(&$aInput, $sType = 'text')
+    {
+        $iValueLength = 20;
+        $sInputIdPrefix = 'bx-form-input-';
+        $aInputMethod = array('text' => 'genInputStandard', 'textarea' => 'genInputTextarea');
+
+        $sInput = '';
+        $sInputName = $aInput['name'];
+        $aInputAttrs = isset($aInput['attrs']) && is_array($aInput['attrs']) ? $aInput['attrs'] : array();
+
+        bx_import('BxTemplStudioFunctions');
+        $oFunctions = BxTemplStudioFunctions::getInstance();
+
+        bx_import('BxDolStudioLanguagesUtils');
+        $oLanguage = BxDolStudioLanguagesUtils::getInstance();
+        $sLanguage = $oLanguage->getCurrentLangName(false);
+        $aLanguages = $oLanguage->getLanguagesInfo();
+
+        $aStrings = $oLanguage->getLanguageString($aInput['value']);
+
+        $aInput['type'] = 'hidden';
+        $aInput['attrs'] = array_merge($aInputAttrs, array(
+            'id' => $sInputIdPrefix . $aInput['name']
+        ));
+        $sInput .= $this->genInputStandard($aInput);
+
+        $aTmplVars = array();
+        foreach($aLanguages as $aLanguage) {
+            $bLanguage = $aLanguage['name'] == $sLanguage;
+
+            $sValue = '';
+            if(($bValue = key_exists($aLanguage['id'], $aStrings)) === true)
+                $sValue = $aStrings[$aLanguage['id']]['string'];
+            else if(isset($aInput['values'][$aLanguage['name']]))
+                $sValue = $aInput['values'][$aLanguage['name']];
+
+            $aInput['type'] = $sType;
+            $aInput['name'] = $sInputName . '-' . $aLanguage['name'];
+            $aInput['value'] = $sValue;
+            $aInput['attrs'] = array_merge($aInputAttrs, array(
+                'id' => $sInputIdPrefix . $aInput['name'],
+                'style' => !$bLanguage ? 'display:none;' : ''
+            ));
+            $sInput .= $this->$aInputMethod[$sType]($aInput);
+
+            $aTmplVarValue = array(
+                'condition' => $bValue,
+                'content' => array(
+                    'value' => strmaxtextlen($sValue, $iValueLength)
+                )
+            );
+            $aTmplVarMissing = array(
+                'condition' => !$bValue || empty($sValue),
+                'content' => array()
+            );
+            $aTmplVars[] = array(
+                'name' => $sInputName,
+                'lang_name' => $aLanguage['name'],
+                'lang_flag' => $aLanguage['icon'],
+                'lang_title' => $aLanguage['title'],
+                'bx_if:hide_active' => array(
+                    'condition' => !$bLanguage,
+                    'content' => array()
+                ),
+                'bx_if:act_value' => $aTmplVarValue,
+                'bx_if:act_missing' => $aTmplVarMissing,
+                'bx_if:hide_passive' => array(
+                    'condition' => $bLanguage,
+                    'content' => array()
+                ),
+                'bx_if:pas_value' => $aTmplVarValue,
+                'bx_if:pas_missing' => $aTmplVarMissing,
+            );
+        }
+
+        $sPopup = $this->oTemplate->parseHtmlByName('form_view_translator.html', array('bx_repeat:languages' => $aTmplVars));
+        $sPopup = $oFunctions->transBox('bx-form-field-translator-popup-' . $sInputName, $sPopup, true);
+
+        $aLanguage = $oLanguage->getLanguageInfo($sLanguage);
+        $sControl = $this->oTemplate->parseHtmlByName('form_view_translatable.html', array(
+            'flag' => $aLanguage['icon'],
+            'title' => $aLanguage['title'],
+            'name' => $sInputName,
+            'popup' => $sPopup
+        ));
+
+        return $sInput . $sControl;
+    }
+
+    /**
+     * Generate Reverse Checkbox Set Element
+     *
+     * @param  array  $aInput
+     * @return string
+     */
+    function genInputCheckboxSet(&$aInput)
+    {
+     	$aInput['value'] = isset($aInput['value']) && $aInput['value'] && is_array($aInput['value']) ? $aInput['value'] : array();
+		if(!empty($aInput['reverse']))
+			$aInput['value'] = array_diff(array_keys($aInput['values']), $aInput['value']);
+
+        return parent::genInputCheckboxSet($aInput);
+    }
+}
+
+/** @} */

+ 674 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/files/studio/template/scripts/BxBaseStudioStore.php

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

+ 2 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/script.php

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

+ 5 - 0
upgrade/files/8.0.0.A3-8.0.0.A4/sql.sql

@@ -0,0 +1,5 @@
+
+-- last step is to update current version
+
+UPDATE `sys_modules` SET `version` = '8.0.0-A4' WHERE `version` = '8.0.0-A3' AND `name` = 'system';
+