Ver Fonte

Upgrade script from 15.0.0-B2 to 15.0.0-B3

Aleksandr Trofimov há 4 meses atrás
pai
commit
6a3ddbd039

+ 9 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/check.php

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

+ 151 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/inc/classes/BxDolAlertsStats.php

@@ -0,0 +1,151 @@
+<?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+class BxDolAlertsStats extends BxDol
+{
+    private static array $logs = [];
+    private static array $stats = [];
+    private static bool $registered = false;
+
+    public static function log(object $oAlert): void
+    {
+        if (!self::$registered) {
+            self::$registered = true;
+            register_shutdown_function([self::class, 'flush']);
+        }
+
+        $s = $oAlert->sUnit . ':' . $oAlert->sAction;
+        if (!isset(self::$logs[$s])) {
+            self::$logs[$s] = [
+                'unit' => $oAlert->sUnit,
+                'action' => $oAlert->sAction,
+                'object' => $oAlert->iObject,
+                'sender' => $oAlert->iSender,
+                'extra' => $oAlert->aExtras,
+                'counter' => 1,
+            ];
+        }
+        else {
+            self::$logs[$s]['counter']++;
+        }
+    }
+
+    public static function hit(string $hookName, float $timing): void
+    {
+        if (!self::$registered) {
+            self::$registered = true;
+            register_shutdown_function([self::class, 'flush']);
+        }
+
+        if (!isset(self::$stats[$hookName])) {
+            self::$stats[$hookName] = [
+                'count' => 0,
+                'last' => time(),
+                'timing' => $timing,
+            ];
+        }
+
+        self::$stats[$hookName]['count']++;
+        self::$stats[$hookName]['last'] = time();
+        self::$stats[$hookName]['timing'] += $timing;
+    }
+
+    public static function flush(): void
+    {
+        self::flushStats();
+        self::flushLogs();
+    }
+
+    public static function flushStats(): void
+    {
+        if (!self::$stats || !getParam('sys_alerts_stats'))
+            return;
+
+        foreach (self::$stats as $hookName => $data) {
+
+            $sql = "
+                UPDATE `sys_alerts_handlers`
+                SET
+                    `ts` = :last,
+                    `timing` = :timing,
+                    `call_count` = call_count + :count, 
+                    `calls_per_request` = GREATEST(`calls_per_request`, :count)
+                WHERE `name` = :name
+            ";
+
+            BxDolDb::getInstance()->query($sql, [
+                'count' => $data['count'],
+                'last' => $data['last'],
+                'timing' => $data['timing'],
+                'name' => $hookName,
+            ]);
+        }
+    }
+
+    public static function flushLogs(): void
+    {
+        if (!self::$logs)
+            return;
+
+        $oDb = BxDolDb::getInstance();
+        if (!getParam('sys_alerts_stats'))
+            $aMap = $oDb->getPairs('SELECT CONCAT(unit, ":", action) AS `key`, 1 AS `val` FROM sys_alerts_log', 'key', 'val');
+
+        foreach (self::$logs as $s => $data) {
+            $aExtraRefs = [];
+            foreach($data['extra'] as $sKey => $mixedValue) {
+                if (preg_match('/^(ref|override|return)_/', $sKey) || preg_match('/_(ref|override|return)$/', $sKey) || $sKey == 'result' || $sKey == 'res' || $sKey == 'return' || $sKey == 'ret' || $sKey == 'check_result') {
+                    self::$logs[$s]['extra_refs'][] = $sKey;
+                }
+            }
+            self::$logs[$s]['extra_refs'] = json_encode(self::$logs[$s]['extra_refs'] ?? []);
+            self::$logs[$s]['extra'] = json_encode(self::$logs[$s]['extra']);            
+
+            if (getParam('sys_alerts_stats')) {
+                $oDb->query("
+                    INSERT INTO sys_alerts_log
+                    SET
+                        unit = :unit,
+                        action = :action,
+                        object = :object,
+                        sender = :sender,
+                        extra = :extra,
+                        extra_refs = :extra_refs,
+                        ts = UNIX_TIMESTAMP(),
+                        counter_total = :counter,
+                        counter_24h = :counter,
+                        counter_per_request = :counter
+                    ON DUPLICATE KEY UPDATE
+                        ts = UNIX_TIMESTAMP(),
+                        counter_total = counter_total + :counter,
+                        counter_24h = counter_24h + :counter,
+                        counter_per_request = GREATEST(counter_per_request, :counter)
+                ", self::$logs[$s]);
+            } else {                
+                $sKey = self::$logs[$s]['unit'] . ':' . self::$logs[$s]['action'];
+                if (!isset($aMap[$sKey])) {
+                    $oDb->query("
+                        INSERT IGNORE INTO sys_alerts_log
+                        SET
+                            unit = :unit,
+                            action = :action,
+                            object = :object,
+                            sender = :sender,
+                            extra = :extra,
+                            extra_refs = :extra_refs,
+                            ts = 0,
+                            counter_per_request = :counter
+                    ", self::$logs[$s]);
+                }
+            }
+        }
+    }
+}
+
+/** @} */

+ 167 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/inc/classes/BxDolBackgroundJobsQuery.php

@@ -0,0 +1,167 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * @see BxDolBackgroundJobs
+ */
+class BxDolBackgroundJobsQuery extends BxDolDb
+{
+    public function __construct()
+    {
+    	parent::__construct();
+    }
+
+    public function getClaimedJobs($sClasimToken)
+    {
+        return $this->getAll("
+            SELECT * FROM `sys_background_jobs` 
+            WHERE `claim_token` = :claim_token AND `status` = '" . BX_DOL_BG_JOBS_STATUS_PENDING . "' 
+            ORDER BY `priority` DESC, `added` ASC", [
+                'claim_token' => $sClasimToken
+            ]
+        );
+    }
+
+    public function claimJobs($sClasimToken, $iLimit)
+    {
+        return $this->query("UPDATE `sys_background_jobs` 
+            SET `reserved_at`=UNIX_TIMESTAMP(), `claim_token`=:claim_token 
+            WHERE claim_token = '' AND `reserved_at`='0' AND (`available_at`='0' OR `available_at`<=UNIX_TIMESTAMP()) AND `status`='" . BX_DOL_BG_JOBS_STATUS_PENDING . "' 
+            ORDER BY `priority` DESC, `added` ASC 
+            LIMIT :limit", [
+                'claim_token' => $sClasimToken,
+                'limit' => (int)$iLimit
+        ]) !== false;
+    }
+
+    public function getJobs($aParams = [])
+    {
+        $aMethod = ['name' => 'getAll', 'params' => [0 => 'query']];
+
+    	$sSelectClause = "*";
+    	$sJoinClause = $sWhereClause = $sGroupClause = $sOrderClause = $sLimitClause = "";
+        switch($aParams['sample']) {
+            case 'name':
+                $aMethod['name'] = 'getRow';
+            	$aMethod['params'][1] = [
+                    'name' => $aParams['name']
+                ];
+
+                $sWhereClause = " AND `name`=:name";
+                break;
+
+            case 'running':
+                $aMethod['name'] = 'getAllWithKey';
+                $aMethod['params'][1] = 'id';
+
+                $sWhereClause = " AND `status`='" . BX_DOL_BG_JOBS_STATUS_PROCESSING . "'";
+                break;
+
+            case 'process':
+                $aMethod['name'] = 'getAllWithKey';
+                $aMethod['params'][1] = 'id';
+
+                $sWhereClause = " AND `reserved_at`='0' AND (`available_at`='0' OR `available_at`<=UNIX_TIMESTAMP()) AND `status`='pending'";
+
+                $sOrderClause = "`added` ASC";
+                if(isset($aParams['with_priority']) && $aParams['with_priority'] === true)
+                    $sOrderClause = "`priority` DESC, " . $sOrderClause;
+
+                if(!empty($aParams['limit']))
+                    $sLimitClause = $aParams['limit'];
+                break;
+        }
+
+        $sOrderClause = !empty($sOrderClause) ? "ORDER BY " . $sOrderClause : $sOrderClause;
+        $sLimitClause = !empty($sLimitClause) ? "LIMIT " . $sLimitClause : $sLimitClause;
+
+        $aMethod['params'][0] = "SELECT
+                " . $sSelectClause . "
+            FROM `sys_background_jobs` " . $sJoinClause . "
+            WHERE 1" . $sWhereClause . " " . $sGroupClause . " " . $sOrderClause . " " . $sLimitClause;
+
+        return call_user_func_array([$this, $aMethod['name']], $aMethod['params']);
+    }
+
+    public function addJob($sName, $sServiceCall, $iPriority = 0)
+    {
+        return $this->query("INSERT INTO `sys_background_jobs` SET `name` = :name, `added`=UNIX_TIMESTAMP(), `priority` = :priority, `service_call`=:service_call ON DUPLICATE KEY UPDATE `added`=UNIX_TIMESTAMP(), `priority` = :priority, `service_call`=:service_call", [
+            'name' => $sName,
+            'priority' => $iPriority,
+            'service_call' => $sServiceCall
+        ]) !== false;
+    }
+
+    public function updateJob($sName, $aParamsSet)
+    {
+        if(empty($sName))
+            return false;
+
+        return $this->updateJobExt($aParamsSet, [
+            'name' => $sName
+        ]);
+    }
+
+    public function updateJobByIds($mixedIds, $aParamsSet)
+    {
+        if(empty($mixedIds))
+            return false;
+        
+        if(!is_array($mixedIds))
+            $mixedIds = [(int)$mixedIds];
+
+        return $this->query("UPDATE `sys_background_jobs` SET " . $this->arrayToSQL($aParamsSet) . " WHERE `id` IN (" . $this->implode_escape($mixedIds) . ")");
+    }
+
+    public function updateJobExt($aParamsSet, $aParamsWhere)
+    {
+        if(empty($aParamsSet) || empty($aParamsWhere))
+            return false;
+
+        return $this->query("UPDATE `sys_background_jobs` SET " . $this->arrayToSQL($aParamsSet) . " WHERE " . $this->arrayToSQL($aParamsWhere, " AND "));
+    }
+
+    public function deleteJobs($aParams = [])
+    {
+        $aMethod = ['name' => 'query', 'params' => [0 => 'query']];
+
+        $sWhereClause = $sLimitClause = "";
+        switch($aParams['sample']) {
+            case 'name':
+                $aMethod['params'][1] = [
+                    'name' => $aParams['name']
+                ];
+
+                $sWhereClause = " AND `name` = :name";
+                break;
+
+            case 'outdated':
+                $aMethod['params'][1] = [
+                    'timeout' => $aParams['timeout'],
+                    'status' => $aParams['status'],
+                ];
+
+                $sWhereClause = " AND `added` + :timeout < UNIX_TIMESTAMP() AND `status` = :status";
+                break;
+        }
+
+        $sLimitClause = !empty($sLimitClause) ? "LIMIT " . $sLimitClause : $sLimitClause;
+
+        $aMethod['params'][0] = "DELETE FROM `sys_background_jobs` WHERE 1" . $sWhereClause . " " . $sLimitClause;
+
+        return call_user_func_array([$this, $aMethod['name']], $aMethod['params']);
+    }
+
+    public function deleteJob($sName)
+    {
+        return $this->deleteJobs(['sample' => 'name', 'name' => $sName]) !== false;
+    }
+}
+
+/** @} */

+ 202 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/inc/classes/BxDolCategoriesQuery.php

@@ -0,0 +1,202 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * @see BxDolCategories
+ */
+class BxDolCategoriesQuery extends BxDolDb
+{
+    protected $_sTableCategories;
+
+    public function __construct()
+    {
+    	parent::__construct();
+
+        $this->_sTableCategories = 'sys_categories';
+    }
+
+    public function getData($aParams = array())
+    {
+        $aMethod = array('name' => 'getAll', 'params' => array(0 => 'query'));
+
+        $sSelectClause = "`sc`.*";
+        $sJoinClause = $sWhereClause = $sGroupClause = "";
+        $sOrderClause = "`sc`.`added` ASC";
+
+        if(isset($aParams['count_only']) && $aParams['count_only'] === true) {
+            $aMethod['name'] = 'getOne';
+            $sSelectClause = "COUNT(`sc`.`id`)";
+        }
+
+        switch($aParams['type']) {
+            case 'by_module':
+                $aMethod['name'] = 'getAllWithKey';
+                $aMethod['params'][1] = 'value';
+                $aMethod['params'][2] = array(
+                    'module' => $aParams['module']
+                );
+
+                $sSelectClause = "`sc`.*, `sc`.`value` as `key`, `sc`.`Value` as `value`";
+                $sWhereClause = " AND `sc`.`module` = :module AND (`sc`.`status` = 'active')";
+                $sOrderClause = "`sc`.`Value` ASC";
+                break;
+                
+            case 'by_module_and_author':
+                $aMethod['name'] = 'getAllWithKey';
+                $aMethod['params'][1] = 'value';
+                $aMethod['params'][2] = array(
+                    'module' => $aParams['module'],
+                    'author' => $aParams['author'],
+                );
+                
+                $sSelectClause = "`sc`.*, `sc`.`value` as `key`, `sc`.`Value` as `value`";
+                $sWhereClause = " AND `sc`.`module` = :module AND (`sc`.`author` = 0 OR `sc`.`author` = :author) AND (`sc`.`status` = 'active')";
+                $sOrderClause = "`sc`.`Value` ASC";
+                break;    
+                
+            case 'by_module_with_num':
+                $aMethod['name'] = 'getAll';
+                $aMethod['params'][1] = array(
+                    'module' => $aParams['module']
+                );
+                
+                $oModule = BxDolModule::getInstance($aParams['module']);
+                $CNF = $oModule->_oConfig->CNF;
+                
+                $sSelectClause = "`sc`.`value`, `sc`.`icon_type`, `sc`.`icon`, COUNT(`sc`.`id`) as `num`";
+                $sJoinClause = "INNER JOIN `sys_categories2objects` `soc` ON `sc`.`id` =  `soc`.`category_id`";
+                if (isset($CNF['FIELD_STATUS']))
+                    $sJoinClause .= "INNER JOIN `" . $CNF['TABLE_ENTRIES'] . "` `data` ON `soc`.`object_id` =  `data`.`id` AND `data`.`" . $CNF['FIELD_STATUS'] . "` = 'active'";
+                $sWhereClause = " AND `sc`.`status` = 'active' AND `soc`.`module` = :module";
+                $sGroupClause = "`sc`.`id`";
+                $sOrderClause = "`sc`.`added` ASC";
+                if(isset($aParams['order_by']))
+                    switch($aParams['order_by']) {
+                        case 'num_asc':
+                            $sOrderClause = "`num` ASC";
+                            break;
+                        case 'num_desc':
+                            $sOrderClause = "`num` DESC";
+                            break;
+                    }
+                break;
+                
+            case 'by_module&context_with_num':
+                $aMethod['name'] = 'getAll';
+                $aMethod['params'][1] = array(
+                    'module' => $aParams['module'],
+                    'context_id' => -$aParams['context_id'],
+                );
+                
+                $oModule = BxDolModule::getInstance($aParams['module']);
+                $CNF = $oModule->_oConfig->CNF;
+                
+                $sSelectClause = "`sc`.`value`, `sc`.`icon_type`, `sc`.`icon`, COUNT(`sc`.`id`) as `num`";
+                $sJoinClause = " INNER JOIN `sys_categories2objects` `soc` ON `sc`.`id` =  `soc`.`category_id`";
+                $sJoinClause .= " INNER JOIN `" . $CNF['TABLE_ENTRIES'] . "` `data` ON `soc`.`object_id` =  `data`.`id` AND `data`.`" . $CNF['FIELD_ALLOW_VIEW_TO'] . "` = :context_id";
+                if (isset($CNF['FIELD_STATUS']))
+                    $sJoinClause .= " AND `data`.`" . $CNF['FIELD_STATUS'] . "` = 'active' ";
+                $sWhereClause = " AND `sc`.`status` = 'active' AND `soc`.`module` = :module";
+                $sGroupClause = "`sc`.`id`";
+                $sOrderClause = "`sc`.`added` ASC";
+                if(isset($aParams['order_by']))
+                    switch($aParams['order_by']) {
+                        case 'num_asc':
+                            $sOrderClause = "`num` ASC";
+                            break;
+                        case 'num_desc':
+                            $sOrderClause = "`num` DESC";
+                            break;
+                    }
+                break;
+                
+            case 'by_module_and_object':
+                $aMethod['name'] = 'getColumn';
+                $aMethod['params'][1] = array(
+                    'module' => $aParams['module'],
+                    'object_id' => $aParams['object_id']
+                );
+                
+                $sSelectClause = "`sc`.`value`";
+                $sJoinClause = "INNER JOIN `sys_categories2objects` `soc` ON `sc`.`id` =  `soc`.`category_id`";
+                $sWhereClause = " AND `sc`.`status` = 'active' AND `soc`.`module` = :module AND `soc`.`object_id` = :object_id";
+                $sOrderClause = "`added` DESC";
+                break;
+                
+            case 'value_and_module':
+            	$aMethod['name'] = 'getRow';
+            	$aMethod['params'][1] = array(
+                    'value' => $aParams['value'],
+                    'module' => $aParams['module'],
+                );
+
+                $sWhereClause = " AND `sc`.`value` = :value AND `sc`.`module` = :module";
+                break;  
+                
+            case 'id':
+            	$aMethod['name'] = 'getRow';
+            	$aMethod['params'][1] = array(
+                    'id' => $aParams['id']
+                );
+
+                $sWhereClause = " AND `sc`.`id` = :id";
+                break;
+        }
+
+        if(!empty($sGroupClause))
+            $sGroupClause = "GROUP BY " . $sGroupClause;
+
+        if(!empty($sOrderClause))
+            $sOrderClause = "ORDER BY " . $sOrderClause;
+
+        $aMethod['params'][0] = "SELECT " . $sSelectClause . " 
+            FROM `" . $this->_sTableCategories . "` AS `sc`" . $sJoinClause . " 
+            WHERE 1" . $sWhereClause . " " . $sGroupClause . " " . $sOrderClause;
+        return call_user_func_array(array($this, $aMethod['name']), $aMethod['params']);
+    }
+    
+    public function delete($sModule, $iObject)
+    {
+        $sQuery = "DELETE FROM `sys_categories2objects` WHERE `module` = :module AND `object_id` = :object_id";
+        $aBindings = array(
+           'module' => $sModule,
+           'object_id' => $iObject,
+        );
+        $this->query($sQuery, $aBindings);
+    }
+
+    public function add($sModule, $iProfileId, $sValue, $iObject, $bAutoActivation)
+    {
+        $aBindings = [
+            'value' => $sValue,
+            'module' => $sModule,
+            'author' => $iProfileId 
+        ];
+
+        $iCategoryId = (int)$this->getOne("SELECT `tc`.`id` FROM `sys_categories` AS `tc` LEFT JOIN `sys_localization_keys` AS `tlk` ON `tc`.`value`=`tlk`.`Key` LEFT JOIN `sys_localization_strings` AS `tls` ON `tlk`.`ID`=`tls`.`IDKey` WHERE (`tc`.`value` = :value OR `tls`.`String`=:value) AND `tc`.`module` = :module AND (`tc`.`author` = 0 OR `tc`.`author` = :author)", $aBindings);
+        if($iCategoryId == 0) {
+            $this->query("INSERT INTO `sys_categories` (`value`, `module`, `status`, `added`, `author`) VALUES(:value, :module, :status, :added, :author)", array_merge($aBindings, [
+                'status' => $bAutoActivation ? 'active' : 'hidden',
+                'added' => time()
+            ]));
+
+            $iCategoryId = $this->lastId();
+        }
+
+        $this->query("INSERT INTO `sys_categories2objects` (`module`, `object_id`, `category_id`) VALUES(:module, :object_id, :category_id)", [
+           'module' => $sModule,
+           'object_id' => $iObject,
+           'category_id' => $iCategoryId,
+        ]);
+
+        return $iCategoryId;
+    }
+}
+
+/** @} */

+ 157 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/inc/classes/BxDolPushOneSignal.php

@@ -0,0 +1,157 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+class BxDolPushOneSignal extends BxDolPush
+{
+    protected $_sEndpoint;
+
+    protected $_sAppId;
+    protected $_sRestApi;
+
+    protected $_sShortName;
+    protected $_sSafariWebId;
+
+    protected $_bCodeAdded;
+
+    protected function __construct($aObject, $oTemplate = null, $sDbClassName = '')
+    {
+        parent::__construct($aObject, $oTemplate, $sDbClassName);
+
+        $this->_sEndpoint = 'https://onesignal.com/api/v1/notifications';
+
+        $this->_sAppId = getParam('sys_push_onesignal_app_id');
+        $this->_sRestApi = getParam('sys_push_onesignal_rest_api');
+
+        $this->_sShortName = getParam('sys_push_onesignal_short_name');
+        $this->_sSafariWebId = getParam('sys_push_onesignal_safari_id');
+
+        $this->_bCodeAdded = false;
+    }
+
+    public function send($iProfileId, $aMessage, $bAddToQueue = false)
+    {
+        if(empty($this->_sAppId) || empty($this->_sRestApi))
+            return false;
+
+        if($bAddToQueue && BxDolQueuePush::getInstance()->add($iProfileId, $aMessage))
+            return true;
+
+        $sUrlWeb = $sUrlApp = !empty($aMessage['url']) ? $aMessage['url'] : '';
+
+        if($this->_sApiUrlRootEmail !== false) {
+            if($sUrlWeb)
+                $sUrlWeb = str_replace(BX_DOL_URL_ROOT, $this->_sApiUrlRootEmail, $sUrlWeb);
+
+            if(!empty($aMessage['contents']) && is_array($aMessage['contents']))
+                foreach($aMessage['contents'] as $sKey => $sValue)
+                    $aMessage['contents'][$sKey] = str_replace(BX_DOL_URL_ROOT, $this->_sApiUrlRootEmail, $sValue);
+        }
+
+        if($this->_sApiUrlRootPush !== false) {
+            if($sUrlApp)
+                $sUrlApp = str_replace(BX_DOL_URL_ROOT, $this->_sApiUrlRootPush, $sUrlApp);
+        }
+        else
+            $sUrlApp = $sUrlWeb;
+
+        $aFilters = [
+            ['field' => 'tag', 'key' => 'user_hash', 'relation' => '=', 'value' => encryptUserId($iProfileId)]
+        ];
+        $aContents = !empty($aMessage['contents']) && is_array($aMessage['contents']) ? $aMessage['contents'] : [];
+        $aHeadings = !empty($aMessage['headings']) && is_array($aMessage['headings']) ? $aMessage['headings'] : [];
+
+        $sIcon = !empty($aMessage['icon']) ? $aMessage['icon'] : BxTemplFunctions::getInstance()->getMainLogoUrl();
+
+        $bResult = null;
+        /**
+         * @hooks
+         * @hookdef hook-system-check_send_push 'system', 'check_send_push' - hook for disabling push sending 
+         * - $unit_name - equals `system`
+         * - $action - equals `check_send_push` 
+         * - $object_id - recipient profile id 
+         * - $sender_id - not used 
+         * - $extra_params - array of additional params with the following array keys:
+         *      - `name` - [string] push notification processor
+         *      - `app_id` - [string] App ID 
+         *      - `filters` - [array] filters list
+         *      - `contents` - [array] contents
+         *      - `headings` - [array] headings list
+         *      - `web_url` - [string] URL for WEB
+         *      - `app_url` - [string] URL for APP
+         *      - `icon` - [string] icon URL
+         *      - `override_result` - [boolean] override result of `send` function, if the result isn't null sending will stop
+         * @hook @ref hook-system-check_send_push
+         */
+        bx_alert('system', 'check_send_push', $iProfileId, '', [
+            'name' => 'onesignal',
+            'app_id' => $this->_sAppId,
+            'filters' => $aFilters,
+            'contents' => $aContents,
+            'headings' => $aHeadings,
+            'web_url' => $sUrlWeb,
+            'app_url' => $sUrlApp,
+            'icon' => $sIcon,
+            'override_result' => &$bResult,
+        ]);
+
+        if($bResult !== null)
+            return $bResult;
+
+        $aFields = [
+            'app_id' => $this->_sAppId,
+            'filters' => $aFilters,
+            'contents' => $aContents,
+            'headings' => $aHeadings,
+            'web_url' => $sUrlWeb,
+            'app_url' => $sUrlApp,
+            'data' => [
+                'url' => $sUrlWeb
+            ]
+        ];
+
+        if($sIcon)
+            $aFields = array_merge($aFields, [
+                'chrome_web_icon' => $sIcon,
+                'large_icon' => $sIcon,
+                'ios_attachments' => [
+                    'id' => $sIcon
+                ]
+            ]);
+
+        if('on' == getParam('bx_nexus_option_push_notifications_count')) {
+            $aFields['ios_badgeType'] = 'SetTo';
+            $aFields['ios_badgeCount'] = $this->getNotificationsCount($iProfileId);
+        }
+
+        $oChannel = curl_init();
+        curl_setopt($oChannel, CURLOPT_URL, $this->_sEndpoint);
+        curl_setopt($oChannel, CURLOPT_HTTPHEADER, [
+            'Content-Type: application/json; charset=utf-8',
+            'Authorization: Basic ' . $this->_sRestApi
+        ]);
+        curl_setopt($oChannel, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($oChannel, CURLOPT_HEADER, false);
+        curl_setopt($oChannel, CURLOPT_POST, true);
+        curl_setopt($oChannel, CURLOPT_POSTFIELDS, json_encode($aFields));
+        if (getParam('sys_curl_ssl_allow_untrusted') == 'on')
+            curl_setopt($oChannel, CURLOPT_SSL_VERIFYPEER, false);
+
+        $sResult = curl_exec($oChannel);
+
+        $oResult = @json_decode($sResult, true);
+        if(isset($oResult['errors']))
+            foreach($oResult['errors'] as $sError) {  
+                bx_log('sys_push', $sError . " Message:" . json_encode($aMessage), BX_LOG_ERR);
+            }
+
+        return $sResult;
+    }
+}
+
+/** @} */

+ 1164 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/inc/js/classes/BxDolUploader.js

@@ -0,0 +1,1164 @@
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+ * Simple Uploader js class
+ */
+
+
+function BxDolUploaderBase (sUploaderObject, sStorageObject, sUniqId, options) {
+
+    this.init(sUploaderObject, sStorageObject, sUniqId, options);
+
+    this._sIframeId = 'bx-form-input-files-' + sUniqId + '-iframe';
+    this._eForm = null;
+}
+
+BxDolUploaderBase.prototype.init = function (sUploaderObject, sStorageObject, sUniqId, options) {
+    this._isUploadsInProgress = false;
+
+    this._sUploaderObject = sUploaderObject;
+    this._sStorageObject = sStorageObject;
+
+    this._sUniqId = sUniqId;
+
+    this._sUploaderJsInstance = 'glUploader_' + sUniqId + '_' + this._sUploaderObject;
+    this._sUploadInProgressContainerId = 'bx-form-input-files-' + sUniqId + '-upload-in-progress-' + this._sUploaderObject;
+    this._sPopupContainerId = 'bx-form-input-files-' + sUniqId + '-popup-wrapper-' + this._sUploaderObject;
+
+    this._sResultContainerId = 'bx-form-input-files-' + sUniqId + '-upload-result';
+    this._sErrorsContainerId = 'bx-form-input-files-' + sUniqId + '-errors'; 
+    this._sProgressContainerId = 'bx-form-input-files-' + sUniqId + '-progress'; 
+
+    this._sFormContainerId = 'bx-form-input-files-' + sUniqId + '-form-cont';
+
+    this._sTemplateGhost = options.template_ghost ? options.template_ghost : '<div id="' + this._getFileContainerId('{file_id}') + '"><input type="hidden" name="f[]" value="{file_id}" />{file_name} (<a href="javascript:void(0);" onclick="{js_instance_name}.deleteGhost(\'{file_id}\')">delete</a>)</div>';
+    this._sTemplateReorder = options.template_reorder ? options.template_reorder : '<div class="bx-uploader-ghost-reorder bx-btn bx-btn-small"><i class="sys-icon bars"></i></div>';
+    this._sTemplateError = options.template_error_msg ? options.template_error_msg : '<div>{error}</div>' ;
+    this._sTemplateErrorGhosts = options.template_error_ghosts ? options.template_error_ghosts : this._sTemplateError;
+
+    this._isMultiple = undefined == options.multiple || !options.multiple ? false : true;
+    this._isLatest = undefined == options.latest || !options.latest ? false : true;
+    this._isReordering = undefined == options.reordering || !options.reordering ? false : true;
+
+    this._iContentId = undefined == options.content_id || '' == options.content_id ? '' : parseInt(options.content_id);
+
+    this._sImagesTranscoder = options.images_transcoder ? options.images_transcoder : '';
+
+    this._isPrivate = undefined == options.storage_private || parseInt(options.storage_private) ? 1 : 0;
+
+    this._isErrorShown = false;
+
+    this._onUploadBefore = typeof options.on_upload_before === 'function' ? options.on_upload_before : function () {};
+    this._onUpload = typeof options.on_upload === 'function' ? options.on_upload : function () {};
+    this._onRestoreGhosts = typeof options.on_restore_ghosts === 'function' ? options.on_restore_ghosts : function () {};
+}
+
+BxDolUploaderBase.prototype.isMultiple = function () {    
+    return this._isMultiple;
+}
+
+BxDolUploaderBase.prototype.getCurrentFilesCount = function () {
+    return $('#' + $this._sResultContainerId + ' .bx-uploader-ghost').length;
+}
+
+BxDolUploaderBase.prototype.showUploaderForm = function () {
+    var $this = this;
+    var sUrl = this._getUrlWithStandardParams() + '&a=show_uploader_form&m=' + (this._isMultiple ? 1 : 0) + '&c=' + this._iContentId + '&p=' + this._isPrivate + '&_t=' + escape(new Date());
+
+    $(window).dolPopupAjax({
+        url: sUrl,
+        id: {force: true, value: this._sPopupContainerId},
+        onBeforeShow: function() {
+            if ($this.isMultiple())
+                $('#' + $this._sPopupContainerId + ' .bx-uploader-add-more-files').show();
+            else
+                $('#' + $this._sPopupContainerId + ' .bx-uploader-add-more-files').hide();
+            $('#' + $this._sPopupContainerId + ' .bx-popup-element-close').click(function() {
+                $this.onClickCancel();
+            });
+			$("#" + $this._sFormContainerId + " .bx-btn.bx-btn-primary:not(.bx-crop-upload)").hide();
+            if ('undefined' !== typeof($this.onBeforeShowPopup))
+                $this.onBeforeShowPopup();
+        },
+        onShow: function () {
+            if ('undefined' !== typeof($this.onShowPopup))
+                $this.onShowPopup();
+        },
+        closeElement: false,
+        closeOnOuterClick: false
+    });
+}
+
+BxDolUploaderBase.prototype.onClickCancel = function () {
+	var $this = this;
+    if (this._isUploadsInProgress) {
+    	bx_confirm(_t('_sys_uploader_confirm_close_popup'), function() {
+    		$this.cancelAll();
+            $('#' + $this._sPopupContainerId).dolPopupHide({});
+    	});
+    } else {
+        $('#' + this._sPopupContainerId).dolPopupHide();
+    }
+}
+
+BxDolUploaderBase.prototype.onBeforeUpload = function (params) {
+
+    this._eForm = params;
+
+    this._loading(true, false);
+        
+    this._isUploadsInProgress = true;
+    this._lockPageFromLeaving();
+    this._clearErrors();
+
+    if(typeof this._onUploadBefore === 'function')
+        this._onUploadBefore(this);
+}
+
+BxDolUploaderBase.prototype.onProgress = function (params) {
+
+}
+
+BxDolUploaderBase.prototype.onUploadCompleted = function (sErrorMsg) {
+
+    this._isUploadsInProgress = false;
+    this._unlockPageFromLeaving();
+    this._loading(false, true);
+
+    if (sErrorMsg.length) {
+        this.restoreGhosts();
+        this._showError(sErrorMsg);
+    } else {
+        this.restoreGhosts();
+        $('#' + this._sPopupContainerId).dolPopupHide({});
+
+        if(typeof this._onUpload === 'function')
+            this._onUpload(this, this._iContentId);
+    }
+}
+
+BxDolUploaderBase.prototype.cancelAll = function () {
+    $('#' + this._sIframeId).attr('src', 'javascript:false;');
+    this.onUploadCompleted(_t('_sys_uploader_upload_canceled'));
+}
+
+BxDolUploaderBase.prototype.restoreGhosts = function (bInitReordering, onComplete) {
+    var sUrl = this._getUrlWithStandardParams() + '&img_trans=' + this._sImagesTranscoder + '&a=restore_ghosts&f=json' + '&c=' + this._iContentId + '&m=' + (this._isMultiple ? 1 : 0) + '&l=' + (this._isLatest ? 1 : 0) + '&_t=' + escape(new Date());
+    var $this = this;
+
+    bInitReordering = bInitReordering !== undefined ? bInitReordering : this._isReordering;
+
+    $.getJSON(sUrl, function (aData) {
+
+        if (!$this.isMultiple())
+            $('#' + $this._sResultContainerId + ' .bx-uploader-ghost').remove();
+
+        if ('object' === typeof(aData)) {
+            if('object' === typeof(aData.g) && 'object' === typeof(aData.o)) 
+                for(var i in aData.o) {
+                    var iFileId = aData.o[i];
+                    $this.showGhost(iFileId, aData.g[iFileId]);
+                }
+            else
+                $.each(aData, function(iFileId, oVars) {
+                    $this.showGhost(iFileId, oVars);
+                });
+
+            $('#' + $this._sResultContainerId).bx_show_more_check_overflow().bxProcessHtml();
+
+            if(bInitReordering) {
+                var sClassGhost = 'bx-uploader-ghost';
+                $('#' + $this._sResultContainerId).find('.' + sClassGhost).each(function() {
+                    if($(this).find('.bx-uploader-ghost-reorder').length == 0)
+                        $(this).find('.bx-base-general-uploader-ghost').prepend($this._sTemplateReorder);
+                });
+
+                var fInitReordering = function() {                    
+                    $('#' + $this._sResultContainerId).sortable({
+                        items: '.' + sClassGhost, 
+                        start: function(oEvent, oUi) {
+                            oUi.item.addClass('bx-uploader-ghost-dragging');
+                        },
+                        stop: function(oEvent, oUi) {
+                            oUi.item.removeClass('bx-uploader-ghost-dragging');
+
+                            $this.reorderGhosts(oUi.item);
+                        }
+                    });
+                };
+
+                if($.sortable !== undefined)
+                    fInitReordering();
+                else
+                    setTimeout(fInitReordering, 2000);
+            }
+        }
+
+        if(typeof onComplete === 'function')
+            onComplete(aData);
+
+        if(typeof this._onRestoreGhosts === 'function')
+            this._onRestoreGhosts($this, aData);
+    });
+};
+
+BxDolUploaderBase.prototype.reorderGhosts = function(oDraggable) {
+    var sUrl = this._getUrlWithStandardParams() + '&a=reorder_ghosts&f=json' + '&c=' + this._iContentId + '&' + $('#' + this._sResultContainerId).sortable('serialize', {key: 'ghosts[]'}) + '&_t=' + escape(new Date());
+
+    $.getJSON(sUrl, function (aData) {
+        processJsonData(aData);
+    });
+};
+
+BxDolUploaderBase.prototype.showGhost = function(iId, oVars) {
+    var oFileContainer = $('#' + this._getFileContainerId(iId));
+    if(oFileContainer.length > 0)
+        return;
+
+    var sHTML;
+    if (typeof this._sTemplateGhost == 'object')
+        sHTML = this._sTemplateGhost[iId];
+    else
+        sHTML = this._sTemplateGhost;
+
+    for(var i in oVars)
+        sHTML = sHTML.replace (new RegExp('{' + i + '}', 'g'), oVars[i]);
+    
+    $('#' + this._sResultContainerId).append(sHTML).trigger( "contentchange" );
+
+    oFileContainer.find('.bx-uploader-ghost-preview img').hide().fadeIn(1000);
+};
+
+BxDolUploaderBase.prototype.deleteGhost = function (iFileId, onComplete) {
+    var sUrl = this._getUrlWithStandardParams() + '&a=delete&id=' + iFileId;
+    var $this = this;
+
+    var sFileContainerId = $this._getFileContainerId(iFileId);
+    bx_loading(sFileContainerId, true);
+
+    $.post(sUrl, {_t: escape(new Date())}, function (sMsg) {
+        var fOnComplete = function(sMsg) {
+            if(typeof onComplete === 'function')
+                onComplete(sMsg);
+        };
+
+        bx_loading(sFileContainerId, false);
+
+        if ('ok' == sMsg) {
+            $('#' + sFileContainerId).slideUp('slow', function () {
+                $(this).remove();
+
+                fOnComplete(sMsg);
+            });
+        } else {
+            $('#' + this._sResultContainerId).prepend($this._sTemplateErrorGhosts.replace('{error}', sMsg));
+
+            fOnComplete(sMsg);
+        }
+    });
+};
+
+BxDolUploaderBase.prototype._clearErrors = function () {
+    $(' #' + this._sErrorsContainerId).html('');
+    this._isErrorShown = false;
+}
+
+BxDolUploaderBase.prototype._showError = function (s, bAppend) {
+    if (s == undefined || !s.length)
+        return;
+
+    var eCont = $('#' + this._sPopupContainerId).find('[id=' + this._sErrorsContainerId + ']').size() ? $('#' + this._sPopupContainerId).find('[id=' + this._sErrorsContainerId + ']') : $('#' + this._sErrorsContainerId);
+
+    if (!bAppend)
+        eCont.html(this._sTemplateError.replace('{error}', s));
+    else
+        eCont.prepend(this._sTemplateError.replace('{error}', s));
+
+    this._isErrorShown = true;
+};
+
+BxDolUploaderBase.prototype._getFileContainerId = function (iFileId) {
+	return 'bx-uploader-file-' + this._sStorageObject + '-' + iFileId;
+};
+
+BxDolUploaderBase.prototype._getUrlWithStandardParams = function () {
+    return sUrlRoot + 'storage_uploader.php?uo=' + this._sUploaderObject + '&so=' + this._sStorageObject + '&uid=' + this._sUniqId;
+}
+
+BxDolUploaderBase.prototype._lockPageFromLeaving = function () {
+    $(window).bind('beforeunload', function (e) {
+        var e = e || window.event;
+        // for ie, ff
+        e.returnValue = _t('_sys_uploader_confirm_leaving_page');
+        // for webkit
+        return _t('_sys_uploader_confirm_leaving_page');
+    });
+}
+
+BxDolUploaderBase.prototype._unlockPageFromLeaving = function () {
+    $(window).unbind('beforeunload');
+}
+
+BxDolUploaderBase.prototype._loading = function (bShowProgress, bShowForm) {
+
+    var eForm = $('#' + this._sFormContainerId + ' .bx-uploader-files-list');
+    var eBtn = $('#' + this._sFormContainerId + ' .bx-btn-primary');
+
+    if (bShowForm) {
+        if (null != this._eForm) {
+            eForm.find('.bx-uploader-simple-file').filter(':not(:first)').remove();
+            if ('undefined' !== typeof(this._eForm.reset)) 
+                this._eForm.reset();
+        }
+        eForm.show();
+        eBtn.show();
+    } else {
+        eForm.hide();
+        eBtn.hide();
+    }
+
+    bx_loading($('#' + this._sFormContainerId + ' .bx-uploader-loading').get(0), bShowProgress);
+}
+
+BxDolUploaderBase.prototype.getMimeTypefromString  = function (ext) {
+    
+    var mimeTypes = 
+    {
+        
+        '3gp'    : 'video/3gpp',
+        '3g2'    : 'video/3gpp2',
+        'a'      : 'application/octet-stream',
+        'ai'     : 'application/postscript',
+        'aif'    : 'audio/x-aiff',
+        'aifc'   : 'audio/x-aiff',
+        'aiff'   : 'audio/x-aiff',
+        'asf'   : 'video/x-ms-asf',
+        'au'     : 'audio/basic',
+        'avi'    : 'video/x-msvideo',
+        'avi'    : 'video/avi',
+        'bat'    : 'text/plain',
+        'bin'    : 'application/octet-stream',
+        'bmp'    : 'image/x-ms-bmp',
+        'c'      : 'text/plain',
+        'cdf'    : 'application/x-cdf',
+        'csh'    : 'application/x-csh',
+        'css'    : 'text/css',
+        'divx'   : 'video/divx',
+        'dll'    : 'application/octet-stream',
+        'doc'    : 'application/msword',
+        'dot'    : 'application/msword',
+        'drc'    : 'video/drc',
+        'dvi'    : 'application/x-dvi',
+        'eml'    : 'message/rfc822',
+        'eps'    : 'application/postscript',
+        'etx'    : 'text/x-setext',
+        'exe'    : 'application/octet-stream',
+        'gif'    : 'image/gif',
+        'gtar'   : 'application/x-gtar',
+        'h'      : 'text/plain',
+        'hdf'    : 'application/x-hdf',
+        'htm'    : 'text/html',
+        'html'   : 'text/html',
+        'jpe'    : 'image/jpeg',
+        'jpeg'   : 'image/jpeg',
+        'jpg'    : 'image/jpeg',
+        'js'     : 'application/x-javascript',
+        'ksh'    : 'text/plain',
+        'latex'  : 'application/x-latex',
+        'm1v'    : 'video/mpeg',
+        'man'    : 'application/x-troff-man',
+        'me'     : 'application/x-troff-me',
+        'mht'    : 'message/rfc822',
+        'mhtml'  : 'message/rfc822',
+        'mkv'    : 'video/x-matroska',
+        'mif'    : 'application/x-mif',
+        'mov'    : 'video/quicktime',
+        'movie'  : 'video/x-sgi-movie',
+        'mp2'    : 'audio/mpeg',
+        'mp3'    : 'audio/mpeg',
+        'm4a'    : ['audio/mp4', 'audio/x-m4a'],
+        'mp4'    : 'video/mp4',  
+        'm4v'    : 'video/mp4', 
+        'mpa'    : 'video/mpeg',
+        'mpe'    : 'video/mpeg',
+        'mpeg'   : 'video/mpeg',
+        'mpg'    : 'video/mpeg',
+        'ms'     : 'application/x-troff-ms',
+        'nc'     : 'application/x-netcdf',
+        'nws'    : 'message/rfc822',
+        'o'      : 'application/octet-stream',
+        'obj'    : 'application/octet-stream',
+        'oda'    : 'application/oda',
+        'ogv'    : 'video/ogg',
+        'ogg'    : 'video/ogg',
+        'pbm'    : 'image/x-portable-bitmap',
+        'pdf'    : 'application/pdf',
+        'pfx'    : 'application/x-pkcs12',
+        'pgm'    : 'image/x-portable-graymap',
+        'png'    : 'image/png',
+        'pnm'    : 'image/x-portable-anymap',
+        'pot'    : 'application/vnd.ms-powerpoint',
+        'ppa'    : 'application/vnd.ms-powerpoint',
+        'ppm'    : 'image/x-portable-pixmap',
+        'pps'    : 'application/vnd.ms-powerpoint',
+        'ppt'    : 'application/vnd.ms-powerpoint',
+        'pptx'   : 'application/vnd.ms-powerpoint',
+        'ps'     : 'application/postscript',
+        'pwz'    : 'application/vnd.ms-powerpoint',
+        'py'     : 'text/x-python',
+        'pyc'    : 'application/x-python-code',
+        'pyo'    : 'application/x-python-code',
+        'qt'     : 'video/quicktime',
+        'ra'     : 'audio/x-pn-realaudio',
+        'ram'    : 'application/x-pn-realaudio',
+        'ras'    : 'image/x-cmu-raster',
+        'rdf'    : 'application/xml',
+        'rgb'    : 'image/x-rgb',
+        'rm'     : 'application/vnd.rn-realmedia',
+        'rmvb'   : 'application/vnd.rn-realmedia-vbr',
+        'roff'   : 'application/x-troff',
+        'rtx'    : 'text/richtext',
+        'sgm'    : 'text/x-sgml',
+        'sgml'   : 'text/x-sgml',
+        'sh'     : 'application/x-sh',
+        'shar'   : 'application/x-shar',
+        'snd'    : 'audio/basic',
+        'so'     : 'application/octet-stream',
+        'src'    : 'application/x-wais-source',
+        'swf'    : 'application/x-shockwave-flash',
+        't'      : 'application/x-troff',
+        'tar'    : 'application/x-tar',
+        'tcl'    : 'application/x-tcl',
+        'tex'    : 'application/x-tex',
+        'texi'   : 'application/x-texinfo',
+        'texinfo': 'application/x-texinfo',
+        'tif'    : 'image/tiff',
+        'tiff'   : 'image/tiff',
+        'tr'     : 'application/x-troff',
+        'tsv'    : 'text/tab-separated-values',
+        'txt'    : 'text/plain',
+        'ustar'  : 'application/x-ustar',
+        'vcf'    : 'text/x-vcard',
+        'wav'    : 'audio/x-wav',
+        'wav'    : 'audio/wav',
+        'webm'   : 'video/webm',
+        'wmv'    : 'video/wmv',
+        'wiz'    : 'application/msword',
+        'wsdl'   : 'application/xml',
+        'xbm'    : 'image/x-xbitmap',
+        'xlb'    : 'application/vnd.ms-excel',
+        'xls'    : 'application/vnd.ms-excel',
+        'xlsx'    : 'application/vnd.ms-excel',
+        'xml'    : 'text/xml',
+        'xpdl'   : 'application/xml',
+        'xpm'    : 'image/x-xpixmap',
+        'xsl'    : 'application/xml',
+        'xvid'   : 'video/xvid',
+        'webp'   : 'image/webp',
+        'xwd'    : 'image/x-xwindowdump',
+        'svg'    : 'image/svg+xml',
+        'zip'    : ['application/zip', 'application/x-zip-compressed']
+       
+    }
+    return mimeTypes[ext.replace('.', '')];
+}
+
+/**
+ * HTML5 Uploader js class
+ */
+function BxDolUploaderHTML5 (sUploaderObject, sStorageObject, sUniqId, options) {
+
+    this.init(sUploaderObject, sStorageObject, sUniqId, options);
+
+    this._sIframeId = 'bx-form-input-files-' + sUniqId + '-iframe';
+    this._eForm = null;    
+    this._sDivId = 'bx-form-input-files-' + sUniqId + '-div-' + this._sUploaderObject;
+    this._sFocusDivId = 'bx-form-input-files-' + sUniqId + '-focus-' + this._sUploaderObject;
+    this._uploader = null;
+    this._aFiles = [];
+  
+
+    this.initUploader = function (o) {
+
+        var $this = this;
+
+        if (null != this._uploader)
+            this._uploader = null;
+
+        var $this = this;
+        var _options = {
+            allowProcess: false,
+            allowPaste: false,
+            allowRevert: false,
+            allowRemove: false,
+            imagePreviewHeight: 100,
+            credits: {},
+            allowMultiple: $this._isMultiple ? true : false,
+            maxFiles: $this.isMultiple() ? 50 : 1,
+            maxFileSize: o.maxFilesize > 2 ? Math.round(o.maxFilesize) + 'MB' : Math.round(o.maxFilesize*1024) + 'KB',
+            instantUpload: true,
+            onaddfile: (error, file) => { 
+                if (error){
+                    $this._uploader.removeFile(file);
+                    if($this._uploader.status != 3)
+                        $this.onUploadCompleted(''); 
+                }
+                else{
+                    $this.onBeforeUpload(''); 
+                }
+            },
+            onerror: (error) => { 
+                if (error.main)
+                    $this._showError(error.main + '. ' + error.sub);
+            },
+            onprocessfiles: (files) => { 
+                $this.onUploadCompleted(''); 
+                oProgress.hide();
+                $this._aFiles = [];
+            },
+            onprocessfileprogress(file, progress) {
+                $this._aFiles[file.source.lastModified+'-'+file.source.size+'-'+file.source.name] = progress * file.source.size;
+                iTotal = 0;
+                iUploaded = 0; 
+                for (const propertyName in $this._aFiles) {
+                    iUploaded += $this._aFiles[propertyName];
+                }
+                
+                for (i = 0; i < $this._uploader.getFiles().length; i++){
+                    iTotal += $this._uploader.getFiles()[i].source.size;
+                }
+            
+                iProgress = iUploaded / iTotal * 100;
+            
+                oProgress =  $('#' + $this._sProgressContainerId);
+                
+                if (oProgress.parents('.bx-db-container').find('.uploader_progress').length > 0){
+                    oProgress = oProgress.parents('.bx-db-container').find('.uploader_progress');
+                }
+                
+                oProgress.show();
+                oProgress.find('.progress_line').css('width', iProgress + '%');
+            },
+            server: {
+				process: (fieldName, file, metadata, load, error, progress, abort) => {
+					const formData = new FormData();
+					formData.append('file', file, file.name);
+					formData.append('uo', this._sUploaderObject);
+					formData.append('so', this._sStorageObject);
+					formData.append('uid', this._sUniqId);
+					formData.append('m', this._isMultiple ? 1 : 0);
+					formData.append('c', this._iContentId);
+					formData.append('p', this._isPrivate);
+					formData.append('a', 'upload');
+
+					const request = new XMLHttpRequest();
+					request.open('POST', sUrlRoot + 'storage_uploader.php');
+
+					request.upload.onprogress = (e) => {
+						progress(e.lengthComputable, e.loaded, e.total);
+					};
+
+					request.onload = function(res) {
+						if (request.status >= 200 && request.status < 300) {
+							var o = null;
+                            try {
+                                o = JSON.parse(request.responseText);
+                            } 
+                            catch (e) {}
+                            if (o && 'undefined' !== typeof(o.error)) {
+                                $this._showError(o.error, true);
+                            }
+                            
+                            load(request.responseText);
+						}
+						else {
+							error('error');
+						}
+					};
+					request.send(formData);
+				}
+			},
+        };
+        
+        aAcceptableFiles = [];
+        if (o.acceptedFiles && o.acceptedFiles != ''){
+            a = o.acceptedFiles.trim().split(/\s*,\s*/);
+            a.forEach(function(item, i, arr) {
+                if ($this.getMimeTypefromString(item)){
+                    oT = $this.getMimeTypefromString(item);
+                    if (Array.isArray(oT)){
+                        oT.forEach(function(oTs, j, ar) {
+                            aAcceptableFiles.push(oTs);
+                        });
+                    }
+                    else{
+                        aAcceptableFiles.push(oT);
+                    }
+                }
+                    
+            });
+        }
+        
+        if (aAcceptableFiles.length){
+            _options.acceptedFileTypes = aAcceptableFiles;
+        }
+        
+      
+        
+        if (o.resizeWidth || o.resizeHeight){
+            _options.allowImageResize = true;
+            _options.imageResizeTargetWidth = o.resizeWidth;
+            _options.imageResizeTargetHeight = o.resizeHeight;
+            _options.imageResizeMode = o.resizeMethod;
+        }
+        else{
+            _options.allowImageResize = false;
+            _options.allowImageTransform = false;
+        }
+        
+        FilePond.registerPlugin(
+            FilePondPluginImagePreview,
+            FilePondPluginFileValidateType,
+            FilePondPluginFileValidateSize,
+            FilePondPluginImageTransform,
+            FilePondPluginImageCrop,
+            FilePondPluginImageResize,
+            
+        );
+
+        this._uploader = FilePond.create(
+            document.querySelector('#' + this._sDivId),
+            $.extend({}, _options, o)
+        );
+
+        if (typeof(glFilepondLocale) !== 'undefined');
+            this._uploader.setOptions(glFilepondLocale);
+
+        // need to be activated if catch paste needed
+        //this.initPasteEditor();
+    }
+
+    this.onUploadCompleted = function (sErrorMsg) {        
+        if (sErrorMsg.length)
+            this._showError(sErrorMsg);        
+        
+        var $this = this;
+        if($this._uploader.status != 3) {
+            this._isUploadsInProgress = false;
+            this.restoreGhosts();
+            if(!this._isErrorShown) {
+                $('#' + this._sPopupContainerId).dolPopupHide({});
+                this.removeFiles();
+            }
+
+            if(typeof this._onUpload === 'function')
+                this._onUpload(this, this._iContentId);
+        }
+    }
+
+    this.removeFiles = function () {
+        oFiles = this._uploader.getFiles();
+        for (i = 0; i < oFiles.length; i++){
+            this._uploader.removeFile(i);
+        }
+    }
+
+    this.cancelAll = function () {
+        this.removeFiles();
+    }
+
+    this.onBeforeUpload = function (params) {
+        this._isUploadsInProgress = true;
+        this._clearErrors();
+
+        if(typeof this._onUploadBefore === 'function')
+            this._onUploadBefore(this);
+    }
+
+    this.onProgress = function (params) {
+    }
+
+    this.onClickCancel = function () {
+        var $this = this;
+        if (this._isUploadsInProgress) {
+            bx_confirm(_t('_sys_uploader_confirm_close_popup'), function() {
+                $this.removeFiles();
+                $this.cancelAll();
+                $('#' + $this._sPopupContainerId).dolPopupHide({});
+            });
+        } else {
+            $this.removeFiles();
+            $('#' + this._sPopupContainerId).dolPopupHide();
+        }
+        
+        BxDolUploaderBase.prototype._clearErrors.call(this);
+    }
+
+    this.onShowPopup = function () {
+        var $this = this;
+        setTimeout(function () {
+            $this.focusPasteEditor();
+        }, 200);
+    }
+
+    this.focusPasteEditor = function () {
+        $('#' + this._sFocusDivId).focus();
+    }
+
+    this.initPasteEditor = function () {
+        var $this = this;
+        $('#' + this._sFocusDivId).on('paste', function (e) {
+            var aFiles = [];
+            if (e.type == 'paste') {
+                var aItems = (e.clipboardData || e.originalEvent.clipboardData).items;
+                
+                for (var i in aItems) {
+                    var oItem = aItems[i];
+                    if (oItem.kind === 'file')
+                        aFiles.push(oItem.getAsFile());
+                }
+                $this._uploader.addFiles(aFiles);
+            }
+        });
+    }
+    
+    this.showUploaderForm = function() {
+        this._uploader.browse();
+    }
+    
+    this.initUploader({
+        'maxFilesize': options.maxFilesize,
+        'acceptedFiles': options.acceptedFiles,
+        'resizeWidth': options.resizeWidth,
+        'resizeHeight': options.resizeHeight,
+        'resizeMethod': options.resizeMethod,
+        'dictDefaultMessage': options.dictDefaultMessage,
+        'dictFileTooBig': options.dictFileTooBig,
+        'dictMaxFilesExceeded': options.dictMaxFilesExceeded,
+        'dictInvalidFileType': options.dictInvalidFileType,
+    });
+}
+
+BxDolUploaderHTML5.prototype = BxDolUploaderBase.prototype;
+
+function BxDolUploaderSimple(sUploaderObject, sStorageObject, sUniqId, options)
+{
+    BxDolUploaderHTML5.call(this, sUploaderObject, sStorageObject, sUniqId, options);
+}
+
+BxDolUploaderSimple.prototype = Object.create(BxDolUploaderHTML5.prototype);
+BxDolUploaderSimple.prototype.constructor = BxDolUploaderSimple;
+
+/**
+ * Crop Image Uploader js class
+ */
+function BxDolUploaderCrop (sUploaderObject, sStorageObject, sUniqId, options) {
+
+    this.init(sUploaderObject, sStorageObject, sUniqId, options);
+
+    this._eForm = null;
+
+    this.initUploader = function (oOptions) {
+
+        var $this = this;
+
+        var aExt = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
+
+        var eCroppie = $("#" + this._sFormContainerId + " .bx-croppie-element").croppie(oOptions);
+
+		$("#" + this._sFormContainerId + ' .bx-crop-rotate').on('click', function(ev) {
+			eCroppie.croppie('rotate', parseInt($(this).data('deg')));
+		});
+        
+        $("#" + this._sFormContainerId + " input[name=f]").on("change", function() {
+            var input = this;            
+
+            if (input.files && input.files[0]) {
+
+                var m = input.files[0].name.match(/\.([A-Za-z0-9]+)$/);
+
+                if (2 != m.length || -1 == aExt.indexOf(m[1].toLowerCase())) {                    
+                    $(input).replaceWith($(input).val('').clone(true));
+                    $this._showError(_t('_sys_uploader_crop_wrong_ext'));
+                    return;
+                }
+
+                $this._clearErrors();
+
+                var reader = new FileReader()
+
+                reader.onload = function(e) {
+                    eCroppie.croppie('bind', {
+                        url: e.target.result
+                    });
+                    $("#" + $this._sFormContainerId + " .bx-croppie-element").addClass('ready');
+                    $("#" + $this._sFormContainerId + " .bx-crop-action").removeClass('bx-btn-disabled');
+                    $("#" + $this._sFormContainerId + " .bx-croppie-element").data('bx-filename', input.files[0].name.replace(/(\.[A-Za-z0-9]+)$/, '.jpg'));
+                }
+                reader.readAsDataURL(input.files[0]);
+            }
+        });
+
+        $("#" + this._sFormContainerId + " .bx-crop-upload").on('click', function(ev) {
+            eCroppie.croppie('result', {
+                type: 'canvas',
+                size: 'original',
+                format: 'jpeg',
+                quality: '0.85',
+            }).then(function(resp) {
+                var fd = new FormData();
+
+                fd.append("f", dataURItoBlob(resp), $("#" + $this._sFormContainerId + " .bx-croppie-element").data('bx-filename'));
+                $.each(oOptions.bx_form, function (sName) {
+                    fd.append(sName, this);
+                });
+                
+                $this.onBeforeUpload(fd);
+
+                $.ajax({
+                    url: sUrlRoot + 'storage_uploader.php',
+                    type: "POST",
+                    processData: false,
+                    contentType: false,
+                    data: fd,
+                    success: function(data) {
+                        eval(data);
+                    },
+                    error: function() {
+                        $this._showError(_t('_sys_uploader_crop_err_upload'));
+                    }
+                })
+
+            });
+        });
+
+        function dataURItoBlob(dataURI) {
+            var split = dataURI.split(','),
+                dataTYPE = split[0].match(/:(.*?);/)[1],
+                binary = atob(split[1]),
+                array = [];
+                
+            for (var i = 0; i < binary.length; i++) 
+                array.push(binary.charCodeAt(i));
+
+            return new Blob([new Uint8Array(array)], {
+                type: dataTYPE
+            });
+        }
+
+        
+    };
+}
+
+BxDolUploaderCrop.prototype = BxDolUploaderBase.prototype;
+
+
+/**
+ * Record Video Uploader js class
+ */
+function BxDolUploaderRecordVideo (sUploaderObject, sStorageObject, sUniqId, options) {
+    this._camera = null;
+    this._blob = null;
+    this._recorder = null;
+    this._camera_type = 'user';
+
+    this._audio_bitrate = undefined !== options.audio_bitrate ? parseInt(options.audio_bitrate) : 128000;
+    this._video_bitrate = undefined !== options.video_bitrate ? parseInt(options.video_bitrate) : 1000000;
+
+    this.init(sUploaderObject, sStorageObject, sUniqId, options);
+
+    this.showUploaderForm = function() {
+        if (typeof MediaRecorder == 'undefined' || !('requestData' in MediaRecorder.prototype)) {
+            bx_alert(_t('_sys_uploader_unsupported_browser'));
+            return;
+        }
+
+        BxDolUploaderBase.prototype.showUploaderForm.call(this);
+    }
+
+    this.onBeforeShowPopup = function() {
+        this._blob = null;
+        this._camera = null;
+        this._recorder = null
+        this._clearErrors();
+
+        $('#bx-upoloader-recording-preview').hide();
+        $('#bx-upoloader-camera-capture').show();
+
+        $('#' + this._sFormContainerId + ' .bx-uploader-record-video-controls').hide();
+    }
+
+    this.switchCamera = function () {
+        this._camera_type = this._camera_type == 'user' ? 'environment' : 'user';
+
+        this.releaseCamera();
+        this.onShowPopup();
+    }
+
+    this.onShowPopup = function () {
+        var $this = this;
+        try {
+            navigator.mediaDevices.getUserMedia({ audio: true, video: {facingMode: this._camera_type} }).then(function(camera) {
+                $this._camera = camera;
+                $this.showCameraCapture();
+
+                $('#' + $this._sFormContainerId + ' .bx-uploader-recording-start').show();
+                $('#' + $this._sFormContainerId + ' .bx-uploader-recording-stop').hide();
+                $('#' + $this._sFormContainerId + ' .bx-uploader-record-video-controls').show();
+
+                navigator.mediaDevices.enumerateDevices().then(function(mediaDevices){
+                    let constraints = navigator.mediaDevices.getSupportedConstraints();
+                    if ($this.getDevicesNum(mediaDevices) > 1 && typeof constraints.facingMode != 'undefined' && constraints.facingMode) {
+                        $('#' + $this._sFormContainerId + ' .bx-record-camera-switch').show();
+                    } else {
+                        $('#' + $this._sFormContainerId + ' .bx-record-camera-switch').hide();
+                    }
+                });
+
+            })
+        } catch (err) {
+            $this._showError(_t('_sys_uploader_camera_capture_failed'));
+        }
+    }
+
+    this.onClickCancel = function () {
+        this.releaseCamera();
+
+        BxDolUploaderBase.prototype.onClickCancel.call(this);
+    }
+
+    this.startRecording = function() {
+        this._recorder = RecordRTC(this._camera, {
+            type: 'video',
+            audioBitsPerSecond: this._audio_bitrate,
+            videoBitsPerSecond: this._video_bitrate,
+            disableLogs: true
+        });
+
+        if (!this._recorder) {
+            this._showError(_t('_sys_uploader_unsupported_browser'));
+            return;
+        }
+
+        $("#" + this._sFormContainerId + " .bx-btn.bx-btn-primary:not(.bx-crop-upload)").hide();
+        $('#' + this._sFormContainerId + ' .bx-uploader-recording-start').hide();
+        $('#' + this._sFormContainerId + ' .bx-uploader-recording-stop').show();
+
+        this._recorder.startRecording();
+
+        this.showCameraCapture();
+    }
+
+    this.stopRecording = function(bSubmitWhenReady) {
+        $('#' + this._sFormContainerId + ' .bx-uploader-recording-start').show();
+        $('#' + this._sFormContainerId + ' .bx-uploader-recording-stop').hide();
+
+        var $this = this;
+        this._recorder.stopRecording(function(){
+            $this._blob = $this._recorder.getBlob();
+            $this.showRecordingPreview();
+
+            $this._recorder.destroy();
+            $this._recorder = null;
+
+            $("#" + $this._sFormContainerId + " .bx-btn.bx-btn-primary:not(.bx-crop-upload)").show();
+            if (bSubmitWhenReady)
+                $this.submitRecording($('#' + $this._sFormContainerId + ' form').get(0));
+        });
+    }
+
+    this.showCameraCapture = function() {
+        var video = $('#bx-upoloader-recording-preview').hide().get(0);
+        if (video.pause !== 'undefined') video.pause();
+        video.removeAttribute('src');
+
+        video = $('#bx-upoloader-camera-capture').show().get(0);
+        video.srcObject = this._camera;
+        video.muted = true;
+        video.volume = 0;
+        $('#' + this._sFormContainerId + ' .bx-record-video-preview .bx-record-video-preview-filesize').html('');
+    }
+
+    this.showRecordingPreview = function() {
+        $('#bx-upoloader-camera-capture').hide();
+        $('#bx-upoloader-recording-preview').show().get(0).src = URL.createObjectURL(this._blob);
+
+        var mbytes = (this._blob.size/1024/1024).toFixed(2);
+        $('#' + this._sFormContainerId + ' .bx-record-video-preview .bx-record-video-preview-filesize').html(mbytes + ' ' + _t('_sys_uploader_record_video_mb'));
+    }
+
+    this.submitRecording = function(form) {
+        if (this._recorder) {
+            this.stopRecording(true);
+            return;
+        }
+
+        this.onBeforeUpload(form);
+
+        var data = new FormData(form);
+        if (this._blob != null) data.append("f[]", this._blob, new Date().toISOString() + '.webm');
+
+        var $this = this;
+        $.ajax({
+            url: $(form).attr('action'),
+            type: "POST",
+            data: data,
+            processData: false,
+            contentType: false,
+            success:function(sErrorMsg, textStatus, jqXHR) {
+                $this.onUploadCompleted(sErrorMsg);
+                if (!sErrorMsg.length) {
+                    $this.releaseCamera();
+                }
+            },
+        });
+    }
+
+    this.releaseCamera = function() {
+        //stop playing recorded file
+        var video = $('#bx-upoloader-recording-preview').get(0);
+        if (video.pause != 'undefined') video.pause();
+        video.removeAttribute('src');
+
+        if (this._recorder) {
+            this._recorder.destroy();
+            this._recorder = null;
+        }
+
+        if (this._camera) {
+            if (typeof this._camera.stop != 'undefined') this._camera.stop();
+            this._camera.getTracks().forEach(function (track) {
+                if (track.readyState == 'live') {
+                    track.stop();
+                }
+            });
+        }
+    }
+
+    this.getDevicesNum = function(mediaDevices) {
+        let count = 0;
+        mediaDevices.forEach(mediaDevice => {
+            if (mediaDevice.kind === 'videoinput') count++;
+        });
+        return count;
+    }
+}
+
+BxDolUploaderRecordVideo.prototype = BxDolUploaderBase.prototype;
+
+
+function BxDolImageTweak (unique_id, action_url, content_id, field, js_object, allow_tweak) {
+    var $this = this;
+    this._sUniqueId = unique_id;
+    this._sActionUrl = action_url;
+    this._sContentId = content_id;
+    this._sField = field;
+    this._sJsObject = js_object;
+    this._bAllowTweak = allow_tweak;
+    this._oContainerButtons = $('.bx-image-edit-buttons-' + unique_id);
+    this._oContainerButtons.removeClass('hidden');
+    $("#bx-form-input-files-" + this._sUniqueId + "-upload-result").bind( "contentchange", function(){ 
+        $this.uploadComplete($(this));
+    });
+    
+    if (this._bAllowTweak){
+        this._oContainerButtons.find('.bx-image-edit-buttons-edit').removeClass('hidden');
+    }
+}
+
+BxDolImageTweak.prototype.showUploaderForm = function(){
+    var $this = this;
+    $('#bx-form-input-files-' + $this._sUniqueId + '-upload-result').html('');
+    eval($this._sJsObject + '.showUploaderForm()');
+}
+    
+BxDolImageTweak.prototype.uploadComplete = function(obj){
+    var $this = this;
+    var sUrl = $this._sActionUrl + "updateImage/" + $this._sField + "/" + $this._sContentId + "/" + obj.html() + "/"; 
+    
+    $this._oContainerButtons.removeClass('bx-image-edit-buttons-no-image');
+    $.post(sUrl, function (aData) {
+        var sSelImage = ".bx-image-edit-source-" + $this._sUniqueId;
+        var sSelPlaceholder = ".bx-image-edit-placeholder-" + $this._sUniqueId;
+
+        if ($this._bAllowTweak){
+            $(sSelImage).css("background-image", "url(" + aData + ")").css('background-position', '').show();
+
+            if($(sSelPlaceholder).length > 0)
+                $(sSelPlaceholder).hide();
+            else
+                $(sSelImage).parents('.bx-base-pofile-cover-image.bx-bpci-holder').removeClass('bx-bpci-holder h-32 lg:h-32').addClass('h-64 lg:h-80')
+        }
+        else{
+            $(sSelImage).attr("src", aData).show();
+
+            if($(sSelPlaceholder).length > 0)
+                $(sSelPlaceholder).hide();
+            else
+                $(sSelImage).parents('.bx-base-pofile-cover-thumb').find('p.bx-base-pofile-unit-thumb').hide();
+        }
+    });
+}
+
+BxDolImageTweak.prototype.changePosition = function (){
+    var $this = this;
+    $(".bx-image-edit-source-" + $this._sUniqueId).parent().append("<div class='bx-image-edit-move-info'><i class='sys-icon sys-colored arrows-alt '></i>" + _t('_sys_uploader_image_reposition_info') + "</div>");
+    $(".bx-image-edit-source-" + $this._sUniqueId).addClass('bx-image-edit-move').bind('dragover', function(e){
+        $(".bx-image-edit-source-" + $this._sUniqueId).parent().find('.bx-image-edit-move-info').remove();
+        $(".bx-image-edit-source-" + $this._sUniqueId).css('background-position', " 0px " + e.offsetY / $(e.currentTarget).height() * 100 + '%');
+    });
+    if (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0)) {
+        $('body').css('overflow', 'hidden')
+        $(".bx-image-edit-source-" + $this._sUniqueId).bind('touchmove', function(e){
+            var touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
+            $(".bx-image-edit-source-" + $this._sUniqueId).parent().find('.bx-image-edit-move-info').remove();
+            $(".bx-image-edit-source-" + $this._sUniqueId).css('background-position', " 0px " + touch.pageY / $(e.currentTarget).height() * 100 + '%');
+        });
+    }
+    with ($this._oContainerButtons) {
+        find('.bx-image-edit-buttons-cancel').removeClass('hidden');
+        find('.bx-image-edit-buttons-save').removeClass('hidden');
+        find('.bx-image-edit-buttons-edit').addClass('hidden');
+        find('.bx-image-edit-buttons-upload').addClass('hidden');
+    }
+}
+
+BxDolImageTweak.prototype.cancelPosition = function (){
+    var $this = this;
+    $('body').css('overflow', '')
+    $(".bx-image-edit-source-" + $this._sUniqueId).parent().find('.bx-image-edit-move-info').remove();
+    $(".bx-image-edit-source-" + $this._sUniqueId).addClass('bx-image-edit-move').unbind('dragover').unbind('touchmove'); 
+    
+    with ($this._oContainerButtons) {
+        find('.bx-image-edit-buttons-cancel').addClass('hidden');
+        find('.bx-image-edit-buttons-save').addClass('hidden');
+        find('.bx-image-edit-buttons-edit').removeClass('hidden');
+        find('.bx-image-edit-buttons-upload').removeClass('hidden');
+    }
+}
+    
+BxDolImageTweak.prototype.savePosition = function (){
+    var $this = this;
+    $('body').css('overflow', '')
+    $(".bx-image-edit-source-" + $this._sUniqueId).parent().find('.bx-image-edit-move-info').remove();
+    $(".bx-image-edit-source-" + $this._sUniqueId).addClass('bx-image-edit-move').unbind('dragover').unbind('touchmove'); 
+    $(".bx-image-edit-source-" + $this._sUniqueId).removeClass('bx-image-edit-move')
+    
+    aPos = $(".bx-image-edit-source-" + $this._sUniqueId).css('background-position').split(' ');
+    $.post($this._sActionUrl + 'updateImagePosition/' + $this._sContentId + '/' + $this._sField + '/' + aPos[0].replace('%','').replace('px','') + '/' + aPos[1].replace('%','').replace('px','') + '/', function () {
+        with ($this._oContainerButtons) {
+            find('.bx-image-edit-buttons-cancel').addClass('hidden');
+            find('.bx-image-edit-buttons-save').addClass('hidden');
+            find('.bx-image-edit-buttons-edit').removeClass('hidden');
+            find('.bx-image-edit-buttons-upload').removeClass('hidden');
+        }
+    });
+}
+
+/** @} */

+ 12 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/inc/version.inc.php

@@ -0,0 +1,12 @@
+<?php defined('BX_DOL') or defined('BX_DOL_INSTALL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+define('BX_DOL_VERSION', '15.0.0-B3'); ///< system files version, for DB version @see bx_get_ver, DB and files version must match to
+
+/** @} */

+ 715 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/plugins/composer/installed.php

@@ -0,0 +1,715 @@
+<?php return array(
+    'root' => array(
+        'name' => 'unacms/una',
+        'pretty_version' => 'dev-master',
+        'version' => 'dev-master',
+        'reference' => '4c355bafc9931bd8059604004c984b3b49933f06',
+        'type' => 'library',
+        'install_path' => __DIR__ . '/../../',
+        'aliases' => array(),
+        'dev' => true,
+    ),
+    'versions' => array(
+        'achingbrain/php5-akismet' => array(
+            'pretty_version' => '0.5',
+            'version' => '0.5.0.0',
+            'reference' => 'master',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../achingbrain/php5-akismet',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'akeeba/s3' => array(
+            'pretty_version' => 'dev-development',
+            'version' => 'dev-development',
+            'reference' => '6e251af705c5a61710b32f79bfb905483634c5fb',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../akeeba/s3',
+            'aliases' => array(
+                0 => '9999999-dev',
+            ),
+            'dev_requirement' => false,
+        ),
+        'avto-dev/composer-cleanup-plugin' => array(
+            'pretty_version' => 'dev-master',
+            'version' => 'dev-master',
+            'reference' => '75187ca4854265174021765e3b67cb973dc37723',
+            'type' => 'composer-plugin',
+            'install_path' => __DIR__ . '/../avto-dev/composer-cleanup-plugin',
+            'aliases' => array(
+                0 => '9999999-dev',
+            ),
+            'dev_requirement' => false,
+        ),
+        'aws/aws-crt-php' => array(
+            'pretty_version' => 'v1.2.7',
+            'version' => '1.2.7.0',
+            'reference' => 'd71d9906c7bb63a28295447ba12e74723bd3730e',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../aws/aws-crt-php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'aws/aws-sdk-php' => array(
+            'pretty_version' => '3.382.2',
+            'version' => '3.382.2.0',
+            'reference' => '6844cc6421c47d6b96633ab8039045012acbeb27',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../aws/aws-sdk-php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'bshaffer/oauth2-server-php' => array(
+            'pretty_version' => 'v1.14.2',
+            'version' => '1.14.2.0',
+            'reference' => '1c715c2d2f71254d1c683b8c89a0f16b61ccd0d6',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../bshaffer/oauth2-server-php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'chargebee/chargebee-php' => array(
+            'pretty_version' => 'v3.51.0',
+            'version' => '3.51.0.0',
+            'reference' => '34746cfc45f838f735c888b00803a874b821e77d',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../chargebee/chargebee-php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'clue/stream-filter' => array(
+            'pretty_version' => 'v1.7.0',
+            'version' => '1.7.0.0',
+            'reference' => '049509fef80032cb3f051595029ab75b49a3c2f7',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../clue/stream-filter',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'curl/cacert' => array(
+            'pretty_version' => '0.0.1',
+            'version' => '0.0.1.0',
+            'reference' => null,
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../curl/cacert',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'elastic/transport' => array(
+            'pretty_version' => 'v9.0.1',
+            'version' => '9.0.1.0',
+            'reference' => '3488b9d070e220f2a1ebdb500e6c588069f1897f',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../elastic/transport',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'elasticsearch/elasticsearch' => array(
+            'pretty_version' => 'v9.4.0',
+            'version' => '9.4.0.0',
+            'reference' => '47f3edae4f6f2ecf507fe574d359839668f69525',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../elasticsearch/elasticsearch',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'enshrined/svg-sanitize' => array(
+            'pretty_version' => '0.22.0',
+            'version' => '0.22.0.0',
+            'reference' => '0afa95ea74be155a7bcd6c6fb60c276c39984500',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../enshrined/svg-sanitize',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'erusev/parsedown' => array(
+            'pretty_version' => 'dev-master',
+            'version' => 'dev-master',
+            'reference' => '4e433a8d57078645b55d21fd12c887d3ba6ca01e',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../erusev/parsedown',
+            'aliases' => array(
+                0 => '9999999-dev',
+            ),
+            'dev_requirement' => false,
+        ),
+        'ezimuel/guzzlestreams' => array(
+            'pretty_version' => '4.1.0',
+            'version' => '4.1.0.0',
+            'reference' => '903161be81e9f497cc42fb7db982404a4e6441b0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../ezimuel/guzzlestreams',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'ezimuel/ringphp' => array(
+            'pretty_version' => '1.4.1',
+            'version' => '1.4.1.0',
+            'reference' => 'b97f46088940671100012482577eeb59f26a13b5',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../ezimuel/ringphp',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'ezyang/htmlpurifier' => array(
+            'pretty_version' => 'v4.19.0',
+            'version' => '4.19.0.0',
+            'reference' => 'b287d2a16aceffbf6e0295559b39662612b77fcf',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../ezyang/htmlpurifier',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'ffmpeg/ffmpeg' => array(
+            'pretty_version' => '6.0.0',
+            'version' => '6.0.0.0',
+            'reference' => null,
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../ffmpeg/ffmpeg',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'guzzlehttp/guzzle' => array(
+            'pretty_version' => '7.10.5',
+            'version' => '7.10.5.0',
+            'reference' => '7c8d84b39e680315f687e8662a9d6fb0865c5148',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzlehttp/guzzle',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'guzzlehttp/promises' => array(
+            'pretty_version' => '2.4.1',
+            'version' => '2.4.1.0',
+            'reference' => '09e8a212562fb1fb6a512c4156ed71525969d6c2',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzlehttp/promises',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'guzzlehttp/psr7' => array(
+            'pretty_version' => '2.10.3',
+            'version' => '2.10.3.0',
+            'reference' => '7c1472269227dc6f18930bd903d7a88fe6c52130',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../guzzlehttp/psr7',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'guzzlehttp/ringphp' => array(
+            'dev_requirement' => false,
+            'replaced' => array(
+                0 => '1.4.1',
+            ),
+        ),
+        'html2text/html2text' => array(
+            'pretty_version' => '4.3.2',
+            'version' => '4.3.2.0',
+            'reference' => '3b443cbe302b52eb5806a21a9dbd79524203970a',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../html2text/html2text',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'http-interop/http-factory-guzzle' => array(
+            'pretty_version' => '1.2.1',
+            'version' => '1.2.1.0',
+            'reference' => 'c2c859ceb05c3f42e710b60555f4c35b6a4a3995',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../http-interop/http-factory-guzzle',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'inspector-apm/inspector-php' => array(
+            'pretty_version' => '3.17.1',
+            'version' => '3.17.1.0',
+            'reference' => '43ce00c8d32f50ef9b2ef30a9e0469ab829fad16',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../inspector-apm/inspector-php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'intervention/image' => array(
+            'pretty_version' => '2.7.2',
+            'version' => '2.7.2.0',
+            'reference' => '04be355f8d6734c826045d02a1079ad658322dad',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../intervention/image',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'knplabs/github-api' => array(
+            'pretty_version' => 'v3.16.0',
+            'version' => '3.16.0.0',
+            'reference' => '25d7bafd6b0dd088d4850aef7fcc74dc4fba8b28',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../knplabs/github-api',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'lusitanian/oauth' => array(
+            'pretty_version' => 'v0.8.11',
+            'version' => '0.8.11.0',
+            'reference' => 'fc11a53db4b66da555a6a11fce294f574a8374f9',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../lusitanian/oauth',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'matthiasmullie/minify' => array(
+            'pretty_version' => '1.3.75',
+            'version' => '1.3.75.0',
+            'reference' => '76ba4a5f555fd7bf4aa408af608e991569076671',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../matthiasmullie/minify',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'matthiasmullie/path-converter' => array(
+            'pretty_version' => '1.1.3',
+            'version' => '1.1.3.0',
+            'reference' => 'e7d13b2c7e2f2268e1424aaed02085518afa02d9',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../matthiasmullie/path-converter',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'monolog/monolog' => array(
+            'pretty_version' => '3.10.0',
+            'version' => '3.10.0.0',
+            'reference' => 'b321dd6749f0bf7189444158a3ce785cc16d69b0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../monolog/monolog',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'mpratt/embera' => array(
+            'pretty_version' => '2.0.42',
+            'version' => '2.0.42.0',
+            'reference' => 'afa728339c6f078c803c9277a5054ca241b3c469',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../mpratt/embera',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'mtdowling/jmespath.php' => array(
+            'pretty_version' => '2.8.0',
+            'version' => '2.8.0.0',
+            'reference' => 'a2a865e05d5f420b50cc2f85bb78d565db12a6bc',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../mtdowling/jmespath.php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'neuron-core/neuron-ai' => array(
+            'pretty_version' => '3.15.5',
+            'version' => '3.15.5.0',
+            'reference' => 'd8e826cdbc52a41539a10501428e4745f1702b31',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../neuron-core/neuron-ai',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'nyholm/psr7' => array(
+            'pretty_version' => '1.8.2',
+            'version' => '1.8.2.0',
+            'reference' => 'a71f2b11690f4b24d099d6b16690a90ae14fc6f3',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../nyholm/psr7',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'open-telemetry/api' => array(
+            'pretty_version' => '1.9.0',
+            'version' => '1.9.0.0',
+            'reference' => '6f8d237ce2c304ca85f31970f788e7f074d147be',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../open-telemetry/api',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'open-telemetry/context' => array(
+            'pretty_version' => '1.5.0',
+            'version' => '1.5.0.0',
+            'reference' => '3c414b246e0dabb7d6145404e6a5e4536ca18d07',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../open-telemetry/context',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'opensearch-project/opensearch-php' => array(
+            'pretty_version' => '2.6.0',
+            'version' => '2.6.0.0',
+            'reference' => '6bcff126ffd8f54ca33a0dd659128cbc5d3b2bcd',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../opensearch-project/opensearch-php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'paypal/paypal-checkout-sdk' => array(
+            'pretty_version' => '1.0.2',
+            'version' => '1.0.2.0',
+            'reference' => '19992ce7051ff9e47e643f28abb8cc1b3e5f1812',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../paypal/paypal-checkout-sdk',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'paypal/paypalhttp' => array(
+            'pretty_version' => '1.0.1',
+            'version' => '1.0.1.0',
+            'reference' => '7b09c89c80828e842c79230e7f156b61fbb68d25',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../paypal/paypalhttp',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'php-http/async-client-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '*',
+            ),
+        ),
+        'php-http/cache-plugin' => array(
+            'pretty_version' => '2.1.0',
+            'version' => '2.1.0.0',
+            'reference' => '1443d3882c4c18556fe9b409f664ebc8c021940c',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../php-http/cache-plugin',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'php-http/client-common' => array(
+            'pretty_version' => '2.7.3',
+            'version' => '2.7.3.0',
+            'reference' => 'dcc6de29c90dd74faab55f71b79d89409c4bf0c1',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../php-http/client-common',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'php-http/client-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '*',
+            ),
+        ),
+        'php-http/discovery' => array(
+            'pretty_version' => '1.20.0',
+            'version' => '1.20.0.0',
+            'reference' => '82fe4c73ef3363caed49ff8dd1539ba06044910d',
+            'type' => 'composer-plugin',
+            'install_path' => __DIR__ . '/../php-http/discovery',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'php-http/httplug' => array(
+            'pretty_version' => '2.4.1',
+            'version' => '2.4.1.0',
+            'reference' => '5cad731844891a4c282f3f3e1b582c46839d22f4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../php-http/httplug',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'php-http/message' => array(
+            'pretty_version' => '1.16.2',
+            'version' => '1.16.2.0',
+            'reference' => '06dd5e8562f84e641bf929bfe699ee0f5ce8080a',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../php-http/message',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'php-http/message-factory-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '1.0',
+            ),
+        ),
+        'php-http/multipart-stream-builder' => array(
+            'pretty_version' => '1.4.2',
+            'version' => '1.4.2.0',
+            'reference' => '10086e6de6f53489cca5ecc45b6f468604d3460e',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../php-http/multipart-stream-builder',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'php-http/promise' => array(
+            'pretty_version' => '1.3.1',
+            'version' => '1.3.1.0',
+            'reference' => 'fc85b1fba37c169a69a07ef0d5a8075770cc1f83',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../php-http/promise',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'phpmailer/phpmailer' => array(
+            'pretty_version' => 'v7.1.1',
+            'version' => '7.1.1.0',
+            'reference' => '1bc1716a507a65e039d4ac9d9adebbbd0d346e15',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../phpmailer/phpmailer',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/cache' => array(
+            'pretty_version' => '3.0.0',
+            'version' => '3.0.0.0',
+            'reference' => 'aa5030cfa5405eccfdcb1083ce040c2cb8d253bf',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/cache',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/http-client' => array(
+            'pretty_version' => '1.0.3',
+            'version' => '1.0.3.0',
+            'reference' => 'bb5906edc1c324c9a05aa0873d40117941e5fa90',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/http-client',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/http-client-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '*',
+                1 => '1.0',
+            ),
+        ),
+        'psr/http-factory' => array(
+            'pretty_version' => '1.1.0',
+            'version' => '1.1.0.0',
+            'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/http-factory',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/http-factory-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '*',
+                1 => '1.0',
+                2 => '^1.0',
+            ),
+        ),
+        'psr/http-message' => array(
+            'pretty_version' => '2.0',
+            'version' => '2.0.0.0',
+            'reference' => '402d35bcb92c70c026d1a6a9883f06b2ead23d71',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/http-message',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/http-message-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '*',
+                1 => '1.0',
+            ),
+        ),
+        'psr/log' => array(
+            'pretty_version' => '3.0.2',
+            'version' => '3.0.2.0',
+            'reference' => 'f16e1d5863e37f8d8c2a01719f5b34baa2b714d3',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../psr/log',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'psr/log-implementation' => array(
+            'dev_requirement' => false,
+            'provided' => array(
+                0 => '3.0.0',
+            ),
+        ),
+        'pusher/pusher-php-server' => array(
+            'pretty_version' => '7.2.8',
+            'version' => '7.2.8.0',
+            'reference' => '4aa139ed2a2a805cd265449b691198beee1309d2',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../pusher/pusher-php-server',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'ralouphie/getallheaders' => array(
+            'pretty_version' => '3.0.3',
+            'version' => '3.0.3.0',
+            'reference' => '120b605dfeb996808c31b6477290a714d356e822',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../ralouphie/getallheaders',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'react/promise' => array(
+            'pretty_version' => 'v3.3.0',
+            'version' => '3.3.0.0',
+            'reference' => '23444f53a813a3296c1368bb104793ce8d88f04a',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../react/promise',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'snipe/banbuilder' => array(
+            'pretty_version' => 'dev-master',
+            'version' => 'dev-master',
+            'reference' => '5e76a97b3ff80e2beaae75ac140c38573ea027a0',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../snipe/banbuilder',
+            'aliases' => array(
+                0 => '9999999-dev',
+            ),
+            'dev_requirement' => false,
+        ),
+        'spatie/calendar-links' => array(
+            'pretty_version' => '1.11.1',
+            'version' => '1.11.1.0',
+            'reference' => 'bf4645d6a2d8eb32248a5b0b7da7976a13e5e2b5',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../spatie/calendar-links',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'stripe/stripe-php' => array(
+            'pretty_version' => 'v19.4.1',
+            'version' => '19.4.1.0',
+            'reference' => '095384404587d07de2ad1154c389c4051c5ed92f',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../stripe/stripe-php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/deprecation-contracts' => array(
+            'pretty_version' => 'v3.7.0',
+            'version' => '3.7.0.0',
+            'reference' => '50f59d1f3ca46d41ac911f97a78626b6756af35b',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/filesystem' => array(
+            'pretty_version' => 'v7.4.11',
+            'version' => '7.4.11.0',
+            'reference' => 'd721ea61b4a5fba8c5b6e7c1feda19efea144b50',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/filesystem',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/options-resolver' => array(
+            'pretty_version' => 'v7.4.8',
+            'version' => '7.4.8.0',
+            'reference' => '2888fcdc4dc2fd5f7c7397be78631e8af12e02b4',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/options-resolver',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-ctype' => array(
+            'pretty_version' => 'v1.37.0',
+            'version' => '1.37.0.0',
+            'reference' => '141046a8f9477948ff284fa65be2095baafb94f2',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-mbstring' => array(
+            'pretty_version' => 'v1.38.1',
+            'version' => '1.38.1.0',
+            'reference' => '14c5439eec4ccff081ac14eca2dc57feb2a66d92',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-php80' => array(
+            'pretty_version' => 'v1.37.0',
+            'version' => '1.37.0.0',
+            'reference' => 'dfb55726c3a76ea3b6459fcfda1ec2d80a682411',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-php80',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/polyfill-php82' => array(
+            'pretty_version' => 'v1.38.1',
+            'version' => '1.38.1.0',
+            'reference' => '002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/polyfill-php82',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'symfony/yaml' => array(
+            'pretty_version' => 'v7.4.13',
+            'version' => '7.4.13.0',
+            'reference' => 'a7ec3b1156faf8815db7683ec7c1e7338e6f977c',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../symfony/yaml',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'tpyo/amazon-s3-php-class' => array(
+            'pretty_version' => 'dev-master',
+            'version' => 'dev-master',
+            'reference' => '7c62b0e9b7d956a04e377ca236979a78dbac4e66',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../tpyo/amazon-s3-php-class',
+            'aliases' => array(
+                0 => '9999999-dev',
+            ),
+            'dev_requirement' => false,
+        ),
+        'twilio/sdk' => array(
+            'pretty_version' => '8.11.6',
+            'version' => '8.11.6.0',
+            'reference' => '57754efb0998086007ec9a63874fd85def5afb7f',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../twilio/sdk',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'typesense/typesense-php' => array(
+            'pretty_version' => 'v6.0.0',
+            'version' => '6.0.0.0',
+            'reference' => 'e113f90449e159316a433a5509205939f271c5e2',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../typesense/typesense-php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'unacms/amazon-s3-php-class-hmac-v2' => array(
+            'pretty_version' => '0.6',
+            'version' => '0.6.0.0',
+            'reference' => '3687dec0af7be2cb11f21828aa4b12d20c4c5935',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../unacms/amazon-s3-php-class-hmac-v2',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'unacms/una' => array(
+            'pretty_version' => 'dev-master',
+            'version' => 'dev-master',
+            'reference' => '4c355bafc9931bd8059604004c984b3b49933f06',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../../',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+        'wikimedia/less.php' => array(
+            'pretty_version' => 'v5.5.1',
+            'version' => '5.5.1.0',
+            'reference' => 'f3e9516585fb28e5119a050e32c5a1708d9899a7',
+            'type' => 'library',
+            'install_path' => __DIR__ . '/../wikimedia/less.php',
+            'aliases' => array(),
+            'dev_requirement' => false,
+        ),
+    ),
+);

Diff do ficheiro suprimidas por serem muito extensas
+ 1 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/plugins_public/grapesjs/grapes.min.js


Diff do ficheiro suprimidas por serem muito extensas
+ 317 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/plugins_public/highlight/highlight.min.js


+ 50 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/studio/template/agents_agents_alerts_select.html

@@ -0,0 +1,50 @@
+<div id="bx-agents-agents-alert-cont" style="overflow:visible;">
+<select id="bx-agents-agents-alert" name="alert" class="bx-def-font-inputs bx-form-input-select">
+    <bx_repeat:alerts>
+        <option value="__value__" <bx_if:sel>__sel__</bx_if:sel> data-desc="__desc__" data-count="__count__" data-count_per_req="__count_per_req__">__name__</option>
+    </bx_repeat:alerts>
+</select>
+</div>
+<script>
+    function formatItem(state) {
+        if (!state.id) 
+            return state.text;
+        const desc = $(state.element).data('desc');
+        const count = $(state.element).data('count');
+        const count_per_req = $(state.element).data('count_per_req');
+        const stats = parseInt(count) !== 0 ? `${count}<br />${count_per_req}` : '__sys_stat_not_available__';
+        return $(`
+        <div class="bx-alert-wrap">
+            <div>
+                <div class="bx-alert-text">${state.text}</div>
+                <div class="bx-alert-desc">${desc || ''}</div>
+            </div>
+            <div class="bx-alert-counter">
+                ${stats}
+            </div>
+        </div>
+        `);
+    }
+    $('#bx-agents-agents-alert').select2({
+        templateResult: formatItem,
+        templateSelection: d => d.text,
+    });
+
+    $('#bx-agents-agents-alert').on('change', function () {
+        const value = $(this).val();
+        const sUrl = sUrlRoot + "grid.php?o=__grid_object__&a=get_alert&csrf_token=" + window.glGrids.__grid_object__._sCsrfToken;
+        $.post(sUrl, {alert: value}, function(oResult) {
+            if (typeof(oResult.code) !== 'undefined' && oResult.code == 200) {
+                $('#bx-agents-agents-alert-payload .payload-on i').html(oResult.desc);
+                $('#bx-agents-agents-alert-payload .payload-on pre').html(JSON.stringify(oResult.payload, null, 4));
+                $('#bx-agents-agents-alert-payload .payload-on').show();
+                $('#bx-agents-agents-alert-payload .payload-off').hide();
+            } else {
+                $('#bx-agents-agents-alert-payload .payload-on').hide();
+                $('#bx-agents-agents-alert-payload .payload-off').show();
+            }
+        }, 'json');
+    });
+
+
+</script>

+ 730 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/studio/template/scripts/BxBaseStudioAgentsAgents.php

@@ -0,0 +1,730 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaView UNA Studio Representation classes
+ * @ingroup     UnaStudio
+ * @{
+ */
+
+class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
+{
+    protected $_sUrlPage;
+    protected $_sFieldName;
+
+    public function __construct ($aOptions, $oTemplate = false)
+    {
+        parent::__construct ($aOptions, $oTemplate);
+
+        $this->_sUrlPage = BX_DOL_URL_STUDIO . 'agents.php?page=agents';
+
+        $this->_sFieldName = 'name';
+    }
+
+    public function getPageJsObject()
+    {
+        return 'oBxDolStudioPageAgents';
+    }
+
+    public function performActionGetAlert()
+    {
+        $aAlert = $this->_getAlertPayload(bx_get('alert'));
+        echoJson([
+            'code' => $aAlert ? 200 : 404,
+            'desc' => $aAlert ? $this->_oDb->getAlertDesc(bx_get('alert')) : '',
+            'payload' => $aAlert,
+        ]);
+    }
+
+    public function performActionManual()
+    {
+        $iId = $this->_getId();        
+        $aAgent = BxDolAiQuery::getAgentObject($iId);
+        if (!$aAgent) {
+            echoJson(['msg' => _t('_sys_txt_error_occured')]);
+            return;
+        }
+        if (!$aAgent['active']) {
+            echoJson(['msg' => _t('_sys_txt_agent_inactive')]);
+            return;
+        }
+
+        $oAi = BxDolAI::getInstance();
+        $sResponse = $oAi->callAgent('manual', $aAgent);
+
+        $oParsedown = new Parsedown();
+        $oParsedown->setSafeMode(true);
+        $sMessageHtml = $oParsedown->text($sResponse);
+
+        $oTemplate = BxDolStudioTemplate::getInstance();
+        $sHtml = $oTemplate->parseHtmlByName('agents_manual_response.html', [
+            'response' => $sMessageHtml
+        ]);
+        echoJson(['msg' => $sHtml]);
+    }
+
+    public function performActionWipeChatHistory()
+    {
+        $iId = $this->_getId();        
+        $aAgent = BxDolAiQuery::getAgentObject($iId);
+        if (!$aAgent) {
+            echoJson(['msg' => _t('_sys_txt_error_occured')]);
+            return;
+        }
+
+        $oDb = new BxDolAIQuery();
+        $oDb->wipeAgentChatHistory($aAgent);
+
+        $aRes = ['grid' => $this->getCode(false), 'blink' => $iId];
+        echoJson($aRes);
+    }
+
+    public function performActionAdd()
+    {
+        $sAction = 'add';
+
+        $oTemplate = BxDolStudioTemplate::getInstance();
+        $oAI = BxDolAI::getInstance();
+
+        $aForm = $this->_getForm($sAction);
+        $oForm = new BxTemplFormView($aForm);
+        $oForm->initChecker();
+
+        if ($oForm->isSubmitted() && $oForm->getCleanValue('trigger') == 'message') {
+            $aAgent = $this->_getAgentWithProfile($oForm->getCleanValue('profile_id'));
+            if ($aAgent) {
+                 $oForm->aInputs['profile_id']['error'] = _t('_sys_agents_form_field_err_profile_has_agent', $aAgent['name']);
+                 $oForm->setValid(false);
+            }
+        }
+
+        if($oForm->isSubmittedAndValid()) {
+            $aValsToAdd = [
+                'added' => time()
+            ];
+
+            $sName = $oForm->getCleanValue($this->_sFieldName);
+            $sName = $this->_getUniqName($sName);
+            BxDolForm::setSubmittedValue($this->_sFieldName, $sName, $oForm->aFormAttrs['method']);
+
+            $iProfileId = $oForm->getCleanValue('profile_id');
+            if(empty($iProfileId)) {
+                $iProfileId = (int)getParam('sys_agents_profile');
+                if(empty($iProfileId))
+                    $iProfileId = current(bx_srv('system', 'get_options_agents_profile', [false], 'TemplServices'))['key'];
+
+                $aValsToAdd['profile_id'] = $iProfileId;
+            }
+  
+            $iModel = $oForm->getCleanValue('model_id');
+            $sTrigger = $oForm->getCleanValue('trigger');
+
+            $sTools = is_array($oForm->getCleanValue('tools')) ? implode(',', $oForm->getCleanValue('tools')) : '';
+            $aValsToAdd['tools'] = $sTools;
+
+            $bIsValid = true;
+            if($bIsValid) {
+                if(($iId = $oForm->insert($aValsToAdd)) !== false) {
+                    $aRes = ['grid' => $this->getCode(false), 'blink' => $iId];
+                }
+                else
+                    $aRes = ['msg' => _t('_sys_txt_error_occured')];
+
+                return echoJson($aRes);
+            }
+        }
+
+        $sFormId = $oForm->getId();
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($sFormId . '_popup', _t('_sys_agents_agents_popup_add'), $this->_oTemplate->parseHtmlByName('agents_automator_form.html', [
+            'form_id' => $sFormId,
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        return echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+    }
+
+    public function performActionEdit()
+    {
+        $sAction = 'edit';
+
+        $oTemplate = BxDolStudioTemplate::getInstance();
+        $oAI = BxDolAI::getInstance();
+
+        $iId = $this->_getId();
+        $aAgent = BxDolAiQuery::getAgentObject($iId);
+
+        $aForm = $this->_getFormEdit($sAction, $aAgent);
+        $oForm = new BxTemplFormView($aForm);
+        $oForm->initChecker();
+
+        if ($oForm->isSubmitted() && $oForm->getCleanValue('trigger') == 'message') {
+            $aAgent = $this->_getAgentWithProfile($oForm->getCleanValue('profile_id'), $iId);
+            if ($aAgent) {
+                 $oForm->aInputs['profile_id']['error'] = _t('_sys_agents_form_field_err_profile_has_agent', $aAgent['name']);
+                 $oForm->setValid(false);
+            }
+        }
+
+        if($oForm->isSubmittedAndValid()) {
+            $aValsToAdd = [];
+
+            $sName = $oForm->getCleanValue($this->_sFieldName);
+            $sName = $this->_getUniqName($sName);
+            BxDolForm::setSubmittedValue($this->_sFieldName, $sName, $oForm->aFormAttrs['method']);
+
+            $iProfileId = $oForm->getCleanValue('profile_id');
+            if(empty($iProfileId)) {
+                $iProfileId = (int)getParam('sys_agents_profile');
+                if(empty($iProfileId))
+                    $iProfileId = current(bx_srv('system', 'get_options_agents_profile', [false], 'TemplServices'))['key'];
+
+                $aValsToAdd['profile_id'] = $iProfileId;
+            }
+  
+            $iModel = $oForm->getCleanValue('model_id');
+            $sTrigger = $oForm->getCleanValue('trigger');
+
+            $sTools = is_array($oForm->getCleanValue('tools')) ? implode(',', $oForm->getCleanValue('tools')) : '';
+            $aValsToAdd['tools'] = $sTools;
+
+            $bIsValid = true;
+            if($bIsValid) {
+                if(($iId = $oForm->update($iId, $aValsToAdd)) !== false) {
+                    $aRes = ['grid' => $this->getCode(false), 'blink' => $iId];
+                }
+                else
+                    $aRes = ['msg' => _t('_sys_txt_error_occured')];
+
+                return echoJson($aRes);
+            }
+        } 
+
+        $sFormId = $oForm->getId();
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($sFormId . '_popup', _t('_sys_agents_agents_popup_edit', $aAgent['name']), $this->_oTemplate->parseHtmlByName('agents_automator_form.html', [
+            'form_id' => $sFormId,
+            'form' => $oForm->getCode(true),
+            'object' => $this->_sObject,
+            'action' => $sAction
+        ]));
+
+        return echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+    }
+
+    public function performActionLogs()
+    {
+        $iId = $this->_getId();
+        $aAgent = BxDolAiQuery::getAgentObject($iId);
+        if (!$aAgent) {
+            echoJson(['msg' => _t('_sys_txt_error_occured')]);
+            return;
+        }
+
+        $oGrid = BxDolGrid::getObjectInstance('sys_studio_agents_logs');
+        $oGrid->addMarkers(['agent_id' => $iId]);
+        $oGrid->setBrowseParams(['agent_id' => $iId]);
+        $sGrid = $oGrid->getCode();
+
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox('popup_files_' . $iId, _t('_sys_agents_logs_popup', $aAgent['name']), $this->_oTemplate->parseHtmlByName('agents_popup_grid.html', [
+            'grid' => $sGrid,
+        ]));
+
+        return echoJson(['popup' => ['html' => $sContent, 'options' => ['closeOnOuterClick' => false]]]);
+    }
+
+    protected function _getCellSwitcher ($mixedValue, $sKey, $aField, $aRow)
+    {
+        // if(empty($aRow['code']) || $aRow['status'] != BX_DOL_AI_AUTOMATOR_STATUS_READY)
+        //    return parent::_getCellDefault('', $sKey, $aField, $aRow);
+
+        return parent::_getCellSwitcher ($mixedValue, $sKey, $aField, $aRow);
+    }
+
+    protected function _getCellType($mixedValue, $sKey, $aField, $aRow)
+    {
+        return parent::_getCellDefault($mixedValue, $sKey, $aField, $aRow);
+    }
+    
+    protected function _delete ($mixedId)
+    {
+        $mixedResult = parent::_delete($mixedId);
+        if($mixedResult) {
+            // $this->_oDb->deleteAutomatorProviders(['automator_id' => (int)$mixedId]);
+            // $this->_oDb->deleteAutomatorHelpers(['automator_id' => (int)$mixedId]);
+            // $this->_oDb->deleteAutomatorAssistants(['automator_id' => (int)$mixedId]);
+
+            // if(($oCmts = BxDolAI::getInstance()->getAutomatorCmtsObject($mixedId)) !== false)
+            //    $oCmts->onObjectDelete();
+        }
+
+        return $mixedResult;
+    }
+
+    protected function _getFormEdit($sAction, $aAgent = [])
+    {
+        $aForm = $this->_getForm($sAction, $aAgent);
+        $aForm['form_attrs']['action'] .= '&id=' . $aAgent['id'];
+
+        unset($aForm['inputs']['name']);
+
+        return $aForm;
+    }
+
+    protected function _getForm($sAction, $aAgent = [])
+    {
+        $aForm = array(
+            'form_attrs' => array(
+                'id' => 'bx_std_agents_' . $sAction,
+                'action' => BX_DOL_URL_ROOT . 'grid.php?o=sys_studio_agents_agents&a=' . $sAction,
+                'method' => 'post',
+            ),
+            'params' => array (
+                'db' => array(
+                    'table' => 'sys_agents_agents',
+                    'key' => 'id',
+                    'submit_name' => 'do_submit',
+                ),
+            ),
+            'inputs' => array(
+                'name' => [
+                    'type' => 'text',
+                    'name' => 'name',
+                    'caption' => _t('_sys_agents_field_name'),
+                    'info' => _t('_sys_agents_field_name_info'),
+                    'value' => isset($aAgent['name']) ? $aAgent['name'] : '',
+                    'required' => '1',
+                    'checker' => [
+                        'func' => 'Avail',
+                        'error' => _t('_sys_agents_form_field_err_enter'),
+                    ],
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+                'model_id' => [
+                    'type' => 'select',
+                    'name' => 'model_id',
+                    'caption' => _t('_sys_agents_field_model_id'),
+                    'info' => _t('_sys_agents_field_model_id_info'),
+                    'value' => isset($aAgent['model_id']) ? $aAgent['model_id'] : BxDolAI::getDefaultModel(),
+                    'values' => $this->_oDb->getModelsBy(['sample' => 'all_pairs', 'active' => 1, 'capabilities' => ['chatvlm', 'chatllm']]),
+                    'required' => '1',
+                    'checker' => [
+                        'func' => 'Avail',
+                        'error' => _t('_sys_agents_form_field_err_select'),
+                    ],
+                    'db' => [
+                        'pass' => 'Int',
+                    ],
+                ],
+                'profile_id' => [
+                    'type' => 'select',
+                    'name' => 'profile_id',
+                    'caption' => _t('_sys_agents_field_profile_id'),
+                    'info' => _t('_sys_agents_field_profile_id_inf'),
+                    'value' => isset($aAgent['profile_id']) ? $aAgent['profile_id'] : 0,
+                    'values' => bx_srv('system', 'get_options_agents_profile', [], 'TemplServices'),
+                    'required' => '1',
+                    'required' => '1',
+                    'checker' => [
+                        'func' => 'Avail',
+                        'error' => _t('_sys_agents_form_field_err_select'),
+                    ],
+                    'db' => [
+                        'pass' => 'Int',
+                    ]
+                ],
+                'trigger' => [
+                    'type' => 'select',
+                    'name' => 'trigger',
+                    'caption' => _t('_sys_agents_field_trigger'),
+                    'info' => _t('_sys_agents_field_trigger_info'),                    
+                    'value' => isset($aAgent['trigger']) ? $aAgent['trigger'] : 0,
+                    'values' => [
+                        'alert' => 'alert',
+                        'scheduler' => 'scheduler',
+                        'webhook' => 'webhook',
+                        'manual' => 'manual',
+                        'agent' => 'agent',
+                        'message' => 'message',
+                    ],
+                    'attrs' => [
+                        'onchange' => $this->getPageJsObject() . '.onChangeAgentTrigger(this)',
+                    ],
+                    'required' => '1',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ]
+                ],
+                'async' => [
+                    'type' => 'checkbox',
+                    'name' => 'async',
+                    'caption' => _t('_sys_agents_field_async'),
+                    'info' => _t('_sys_agents_field_async_inf'),                    
+                    'value' => 1,
+                    'checked' => isset($aAgent['async']) ? $aAgent['async'] : 0,
+                    'db' => [
+                        'pass' => 'Int',
+                    ]
+                ],
+
+                'alert_section' => array(
+                    'type' => 'block_header',
+                    'caption' => 'Trigger - alert',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ),
+
+                'alert_sample' => [
+                    'type' => 'custom',
+                    'name' => 'alert_sample',
+                    'caption' => _t('_sys_agents_field_alert_sample'),
+                    'content' => $this->_oTemplate->parseHtmlByName('agents_agents_alerts_payload.html', [
+                        'text-visibility' => empty($aAgent['alert']) ? 'block' : 'none',
+                        'text' => _t('_sys_agents_waiting_for_sample_data_alert'),
+
+                        'payload-visibility' => !empty($aAgent['alert']) ? 'block' : 'none',
+                        'desc' => $aAgent ? $this->_oDb->getAlertDesc($aAgent['alert']) : '',
+                        'payload' => !empty($aAgent['alert']) ? json_encode($this->_getAlertPayload($aAgent['alert']), JSON_PRETTY_PRINT) : '',
+                    ]),
+                ],
+
+                'alert' => [
+                    'type' => 'custom',
+                    'name' => 'alert',
+                    'caption' => _t('_sys_agents_field_alert'),
+                    'info' => _t('_sys_agents_field_alert_info'),
+                    'content' => $this->_oTemplate->parseHtmlByName('agents_agents_alerts_select.html', $this->_getAlertValues(isset($aAgent['alert']) ? $aAgent['alert'] : '')),
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+
+                'scheduler_section' => [
+                    'type' => 'block_header',
+                    'caption' => 'Trigger - scheduler',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ],
+
+                'scheduler_cron' => [
+                    'type' => 'text',
+                    'name' => 'scheduler_cron',
+                    'caption' => _t('_sys_agents_field_scheduler_cron'),
+                    'info' => _t('_sys_agents_field_scheduler_cron_info'),
+                    'value' => isset($aAgent['scheduler_cron']) ? $aAgent['scheduler_cron'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+
+                'webhook_section' => array(
+                    'type' => 'block_header',
+                    'caption' => 'Trigger - webhook',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ),
+
+                'webhook_sample' => [
+                    'type' => 'custom',
+                    'name' => 'webhook_sample',
+                    'caption' => _t('_sys_agents_field_webhook_sample'),
+                    'content' => isset($aAgent['webhook_sample']) ? $aAgent['webhook_sample'] : _t('_sys_agents_waiting_for_sample_data_webhook'),
+                ],
+                'webhook_key' => [
+                    'type' => 'text',
+                    'name' => 'webhook_key',
+                    'caption' => _t('_sys_agents_field_webhook_key'),
+                    'info' => _t('_sys_agents_field_webhook_key_info'),
+                    'value' => isset($aAgent['webhook_key']) ? $aAgent['webhook_key'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+
+                'message_section' => array(
+                    'type' => 'block_header',
+                    'caption' => 'Trigger - message',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ),
+
+                'message_profile_id' => [
+                    'type' => 'select',
+                    'name' => 'message_profile_id',
+                    'caption' => _t('_sys_agents_field_message_profile'),
+                    'info' => _t('_sys_agents_field_message_profile_info'),
+                    'value' => isset($aAgent['message_profile_id']) ? $aAgent['message_profile_id'] : '',
+                    'values' => bx_srv('system', 'get_options_agents_profile', [true, '_adm_nav_txt_items_visible_for_all'], 'TemplServices'),
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+
+                'message_section_end' => array(
+                    'type' => 'block_end',
+                ),
+
+                'vector_store_id' => [
+                    'type' => 'select',
+                    'name' => 'vector_store_id',
+                    'caption' => _t('_sys_agents_field_vector_store_id'),
+                    'info' => _t('_sys_agents_field_vector_store_id_info'),
+                    'value' => isset($aAgent['vector_store_id']) ? $aAgent['vector_store_id'] : 0,
+                    'values' => $this->_oDb->getVectorStores(),
+                    'required' => '1',
+                    'checker' => [
+                        'func' => 'Avail',
+                        'error' => _t('_sys_agents_form_field_err_select'),
+                    ],
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+                'chat_history_context' => [
+                    'type' => 'text',
+                    'name' => 'chat_history_context',
+                    'caption' => _t('_sys_agents_field_chat_history_context'),
+                    'info' => _t('_sys_agents_field_chat_history_context_info'),
+                    'value' => isset($aAgent['chat_history_context']) ? $aAgent['chat_history_context'] : 50000,
+                    'db' => [
+                        'pass' => 'Int',
+                    ],
+                ],
+                'tools' => [
+                    'type' => 'custom',
+                    'name' => 'tools',
+                    'caption' => _t('_sys_agents_field_tools'),
+                    'info' => _t('_sys_agents_field_tools_info'),
+                    'value' => '',
+                ],
+                'tools_max_run' => [
+                    'type' => 'text',
+                    'name' => 'tools_max_run',
+                    'caption' => _t('_sys_agents_field_tools_max_run'),
+                    'info' => _t('_sys_agents_field_tools_max_run_info'),
+                    'value' => isset($aAgent['tools_max_run']) ? $aAgent['tools_max_run'] : '10',
+                    'required' => '1',
+                    'db' => [
+                        'pass' => 'Int',
+                    ],
+                ],
+                'prompt_system' => [
+                    'type' => 'textarea',
+                    'name' => 'prompt_system',
+                    'caption' => _t('_sys_agents_field_prompt_system'),
+                    'info' => _t('_sys_agents_field_prompt_system_info'),
+                    'value' => isset($aAgent['prompt_system']) ? $aAgent['prompt_system'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+
+                'prompts_section' => array(
+                    'type' => 'block_header',
+                    'caption' => 'Other prompts',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ),
+
+                'prompt_steps' => [
+                    'type' => 'textarea',
+                    'name' => 'prompt_steps',
+                    'caption' => _t('_sys_agents_field_prompt_steps'),
+                    'info' => _t('_sys_agents_field_prompt_steps_info'),
+                    'value' => isset($aAgent['prompt_steps']) ? $aAgent['prompt_steps'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+                'prompt_output' => [
+                    'type' => 'textarea',
+                    'name' => 'prompt_output',
+                    'caption' => _t('_sys_agents_field_prompt_output'),
+                    'info' => _t('_sys_agents_field_prompt_output_info'),
+                    'value' => isset($aAgent['prompt_output']) ? $aAgent['prompt_output'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+                'prompt_tools' => [
+                    'type' => 'textarea',
+                    'name' => 'prompt_tools',
+                    'caption' => _t('_sys_agents_field_prompt_tools'),
+                    'info' => _t('_sys_agents_field_prompt_tools_info'),
+                    'value' => isset($aAgent['prompt_tools']) ? $aAgent['prompt_tools'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+                'prompts_section_end' => array(
+                    'type' => 'block_end',
+                ),
+                'submit' => $this->_getFormControls(),
+
+            ),
+        );
+        
+        $this->_getMultiField('tools', $aAgent, 'getTools', 'toolAdd', 'agents_agents_form_tools.html', $aForm);
+
+        return $aForm;
+    }
+
+    protected function _getAlertPayload($s)
+    {
+        $aAlert = $this->_oDb->getAlert($s);
+        if (!$aAlert)
+            return '';
+        return [
+            'object_id' => $aAlert['object'],
+            'sender_profile_id' => $aAlert['sender'],            
+            'extra' => json_decode($aAlert['extra'], true),
+            'extra_modifiable_keys' => json_decode($aAlert['extra_refs'], true),
+        ];
+    }
+
+    protected function _getAlertValues($sValue)
+    {
+        $aAlerts = $this->_oDb->getAlerts();
+        $a = [
+            'sys_stat_not_available' => _t('_sys_not_available'),
+            'grid_object' => $this->_sObject,
+            'bx_repeat:alerts' => [
+                [
+                    'name' => _t('_sys_please_select'), 
+                    'value' => '', 
+                    'desc' => '', 
+                    'count' => 0,
+                    'count_per_req' => 0,
+                    'bx_if:sel' => [
+                        'condition' => false,
+                        'content' => ['sel' => '']
+                    ]
+                ]
+            ]
+        ];
+        foreach ($aAlerts as $k => $r) {
+            $a['bx_repeat:alerts'][] = [
+                'name' => $r['name'],
+                'value' => $r['key'],
+                'desc' => bx_html_attribute($r['desc']),
+                'count' => bx_html_attribute(_t('_sys_agents_count_today', $r['counter_24h'])),
+                'count_per_req' => bx_html_attribute(_t('_sys_agents_count_per_req', $r['counter_per_request'])),
+                'bx_if:sel' => [
+                    'condition' => $sValue == $r['key'],
+                    'content' => [
+                        'sel' => $sValue == $r['key'] ? 'selected' : '',
+                    ]
+                ]
+            ];
+        }
+        return $a;
+    }
+
+    protected function _getUniqName($sName)
+    {
+        return uriGenerate($sName, 'sys_agents_automators', 'name', ['lowercase' => false]);
+    }
+
+    protected function _getMultiField($sField, $aAgent, $sGetValuesMethod, $sJsAddMethod, $sTemplateName, &$aForm)
+    {
+        if(!isset($aForm['inputs']['tools'])) 
+            return;
+
+        $sJsObject = $this->getPageJsObject();
+
+        $oForm = new BxTemplFormView([]);
+
+        $aValues = ['0' => _t('_sys_please_select')];
+        $a = $this->_oDb->$sGetValuesMethod();
+        foreach ($a as $k => $v) {
+            $aValues[$k] = $v;
+        }
+
+        $aTmplVars = [];
+        if(!empty($aAgent[$sField])) {
+            $a = explode(',', $aAgent[$sField]);
+            foreach($a as $v) {
+                $aInputSelect = [
+                    'type' => 'select',
+                    'name' => $sField . '[]',
+                    'values' => $aValues,
+                    'value' => $v,
+                    'attrs' => [
+                        'class' => 'bx-def-margin-sec-top-auto'
+                    ]
+                ];
+                $sInput = $oForm->genInput($aInputSelect);
+
+                $aInputHidden = [
+                    'type' => 'hidden',
+                    'name' => $sField . '_ids[]',
+                    'value' => $v,
+                ];
+                $sInput .= $oForm->genInput($aInputHidden);
+
+                $aTmplVars[] = ['js_object' => $sJsObject, 'input_select' => $sInput];
+            }
+        }
+        else  {
+            $aInputSelect = [
+                'type' => 'select',
+                'name' => $sField . '[]',
+                'values' => $aValues,
+                'value' => '',
+                'attrs' => [
+                    'class' => 'bx-def-margin-sec-top-auto'
+                ]
+            ];
+
+            $aTmplVars = [
+                ['js_object' => $sJsObject, 'input_select' => $oForm->genInput($aInputSelect)],
+            ];
+        }
+        
+        $aInputButton = [
+            'type' => 'button',
+            'name' => $sField . '_add',
+            'value' => _t('_adm_nav_btn_items_add'),
+            'attrs' => [
+                'class' => 'bx-def-margin-sec-top',
+                'onclick' => $sJsObject . ".$sJsAddMethod(this, '" . $sField . "');"
+            ]
+        ];
+
+        $aForm['inputs'][$sField]['content'] = $this->_oTemplate->parseHtmlByName($sTemplateName, [
+            "bx_repeat:$sField" => $aTmplVars,
+            'btn_add' => $oForm->genInputButton($aInputButton)
+        ]);
+    }
+
+    protected function _getActionManual ($sType, $sKey, $a, $isSmall = false, $isDisabled = false, $aRow = array())
+    {
+        if ($aRow['trigger'] != 'manual' && $aRow['trigger'] != 'scheduler')
+            return '';
+        return parent::_getActionDefault ($sType, $sKey, $a, $isSmall, $isDisabled, $aRow);
+    }
+
+    protected function _getAgentWithProfile($iProfileId, $iExcludeAgentId = 0)
+    {
+        $aAgents = BxDolAi::getInstance()->getAgentsByProfileId($iProfileId);
+        foreach($aAgents as $aAgent) {
+            if($aAgent['id'] != $iExcludeAgentId)
+                return $aAgent;
+        }
+
+        return null;
+    }
+
+    protected function _addJsCss()
+    {
+        parent::_addJsCss();
+
+        $this->_oTemplate->addJs(['select2/js/select2.min.js']);
+        $this->_oTemplate->addCss([BX_DIRECTORY_PATH_PLUGINS_PUBLIC . 'select2/css/|select2.min.css']);
+    }
+}
+
+/** @} */

+ 993 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/template/css/common.css

@@ -0,0 +1,993 @@
+* {
+    -webkit-font-smoothing: antialiased;
+    -webkit-text-size-adjust: 100%;
+    -moz-text-size-adjust: 100%;
+    -ms-text-size-adjust: 100%;
+    text-size-adjust: 100%;
+}
+
+a {
+    text-decoration:none;
+    outline:none;
+    color:#3e8685; /* 26.13.13: prev val #387877; */
+}
+
+a:hover {
+    text-decoration:none;
+}
+
+a > img {
+    border:none;
+}
+
+html {
+    height:100%;
+}
+/*-- Tailwind overwrite --*/
+html.hidden {
+    display: block !important;
+}
+body {
+	min-height: 100%;
+    min-height: 100vh;
+    padding: 0;
+    margin: 0;
+}
+
+blockquote {
+    /* NOTE: this style was copied to editor.css */
+    border-left: 2px solid #ccc;
+    padding-left: 10px;
+}
+
+.bx-tl-content pre,
+.bx-base-text-entity-content pre,
+.bx-content-description pre,
+.cmt-body pre {
+    /* NOTE: this style was copied to editor.css */
+    padding: 5px;
+    border: 1px solid green;
+    background-color: white;
+    color: #009900;
+    display: block;
+    margin: 0;
+    overflow-x: auto;
+}
+
+.bx-main {
+    position: relative;
+    display: -webkit-flex;
+    display: flex;
+    -webkit-flex-direction: column;
+    flex-direction: column;
+    min-height: 100%;
+    min-height: 100vh;
+}
+
+/*-- Application mode: Sidebar --*/
+.bx-main.bx-pt-application .bx-main-column .bx-menu-panel .bx-def-thumb-size {
+    width: 2rem;
+    height: 2rem;
+}
+.bx-main.bx-pt-application .bx-main-column.narrow.bx-mc-narrowed .bx-menu-panel .bx-profile-avatar-cnt {
+    -webkit-justify-content:center;
+    justify-content:center;
+}
+.bx-main.bx-pt-application .bx-main-column.narrow.bx-mc-narrowed .bx-mc-cnt {
+    padding-left: 0;
+    padding-right: 0;
+}
+
+/*-- Application mode: Toolbar --*/
+.bx-main.bx-pt-application .bx-toolbar-content {
+    position: relative;
+    display: -webkit-flex;
+    display: flex;
+    -webkit-align-items: center;
+    align-items: center;
+}
+.bx-main.bx-pt-application .bx-toolbar-content .bx-toolbar-item.bx-ti-left {
+    -webkit-flex: 0 0 auto;
+    -ms-flex: 0 0 auto;
+    flex: 0 0 auto;
+}
+.bx-main.bx-pt-application .bx-toolbar-content .bx-toolbar-item.bx-ti-left .bx-menu-item-main-menu {
+    display: none;
+}
+.bx-main.bx-pt-application .bx-toolbar-content .bx-toolbar-item.bx-ti-menu {
+    -webkit-flex: 2 1 auto;
+    -ms-flex: 2 1 auto;
+    flex: 2 1 auto;
+}
+.bx-main.bx-pt-application .bx-toolbar-content .bx-toolbar-item.bx-ti-right {
+    -webkit-flex: 0 1 0%;
+    -ms-flex: 0 1 0%;
+    flex: 0 1 0%;
+}
+
+.bx-clear {
+    clear:both;
+    float:none;
+    display:block;
+    width: 0px;
+    height: 0px;
+    font-size:1px;
+}
+
+.bx-clearfix:before,
+.bx-clearfix:after {
+    content:"";
+    display:table;
+}
+.bx-clearfix:after {
+    clear:both;
+}
+
+.bx-anchor-invisible,
+.bx-anchor {
+    -webkit-background-clip:content-box;
+    background-clip:content-box;
+}
+.bx-anchor-invisible {
+    display:block;
+    height:0px;
+    width:0px;
+    overflow:hidden;
+}
+
+.bx-bullet:before {
+    content: "\00A0\00b7\00A0";
+
+    color: #999999;
+}
+.bx-bullet-narrow:before {
+    content: "\00b7";
+
+    color: #999999;
+}
+
+.bx-tag-link b,
+.bx-tag-link s {
+    text-decoration: none;
+    font-weight: normal;
+}
+
+.sys-icon-a {
+    width:1.5rem;
+    height:1.5rem;
+}
+
+.sys-icon-emoji{
+    font-weight: normal !important;
+}
+
+.sys-icon-image,
+.sys-icon-svg{
+    display: inline-block;
+    width: 1.5rem;
+    height: 1.5rem;
+    background-size: contain;
+    background-position: center;
+    background-repeat: no-repeat;
+}
+
+.bx-search-site {
+    width:20rem;
+    margin-left:auto;
+    margin-right:auto;
+}
+.bx-search-site .sys-search-results-quick {
+    padding-top: 0.5rem
+}
+.bx-search-site .sys-search-results-quick .bx-btn.sys-agents-ask {
+    display: flex;
+    float: none;
+    
+    width: 100%;
+    margin-bottom: 0.5rem;
+}
+.bx-acl-err-msg {
+    padding: 0 10%;
+}
+
+/* --------------- Agents: Assistants */
+.bx-agents-popup .cmts {
+    max-height: calc(0.5 * 100vh);
+    overflow-y: auto;
+}
+.cmt.cmt-agents .cmt-body .cmt-agents-loading .bx-loading-ajax {
+    position: relative;
+    display: inline-block;
+    vertical-align: middle;
+    width: 2.0rem;
+    height: 2.0rem;
+    left: 0 !important;
+    top: 0 !important;
+    margin-left: 0.5rem;
+}
+.cmt-body.cmt-body-agents .bx-form-value .bx-form-input-files-uploaders .bx-btn {
+    margin: 0 0.5rem 0 0;
+}
+.cmt-body.cmt-body-agents .bx-form-value .bx-form-input-textarea {
+    height: 2.5rem;
+    line-height: 1.25rem;
+}
+
+/* --------------- design boxes */
+
+.bx-db-container {
+    position:relative;
+}
+.bx-db-header {
+    display: -webkit-box;
+    display: -webkit-flex;
+    display: -ms-flexbox;
+    display: flex;
+
+    -webkit-box-align: center;
+    -webkit-align-items: center;
+    -ms-flex-align: center;
+    align-items: center;
+
+    -webkit-flex-wrap: nowrap;
+    -ms-flex-wrap: nowrap;
+    flex-wrap: nowrap;
+}
+.bx-db-container.bx-dbc-nopadding > .bx-db-header {
+    padding-left: 0;
+    padding-right: 0;
+}
+.bx-db-container.bx-dbc-nobg > .bx-db-divider {
+    display: none;
+}
+.bx-db-title {
+    -webkit-flex: 1 1 auto;
+    -ms-flex: 1 1 auto;
+    flex: 1 1 auto;
+
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+
+    line-height: 1.5;
+    font-size: 1.0rem;
+    font-weight: 600;
+}
+
+.bx-dbt-cnt {
+    display: flex;
+    align-items: center;
+}
+
+.bx-db-menu {
+    -webkit-flex: 1 2 auto;
+    -ms-flex: 1 2 auto;
+    flex: 1 2 auto;
+
+    max-width: 75%;
+}
+
+.bx-media-phone .bx-db-menu {
+    max-width: 65%;
+}
+
+.bx-dbc-notitle .bx-db-menu,
+.bx-media-phone .bx-dbc-notitle .bx-db-menu {
+    max-width: none;
+}
+
+.bx-db-menu-tab {
+    margin-left: 5px;
+    padding-top: 1px;
+    padding-bottom: 1px;
+    text-align: right;
+    overflow-x: auto;
+}
+.bx-db-menu-tab.bx-db-menu-tab-more-auto {
+    overflow-x: visible;
+}
+
+.bx-db-menu-tab .bx-menu-custom-hor:not(.bx-menu-more-auto) {
+    width: max-content;
+}
+.bx-db-menu-tab-active {
+    text-decoration:underline;
+}
+
+.bx-db-menu-tab-btn a {
+    text-decoration:none;
+}
+
+.bx-db-menu-tab-btn a.bx-btn {
+    display: inline-block;
+    float: none;
+}
+
+.bx-db-menu .sys-icon-a {
+    vertical-align:middle;
+}
+
+.bx-db-menu-popup {
+    text-align: left;
+}
+
+.bx-db-content {
+    position: relative;
+}
+
+.bx-content-padding-block {
+    padding: 1rem;
+}
+
+.bx-db-content .bx-db-help {
+    position: absolute;
+    top: 0;
+    right: 0;
+    width: 2.0rem;
+    height: 2.0rem;
+    border-radius: 2rem;
+    background-color: rgba(0, 0, 0, 0.1);
+    line-height: 2rem;
+    text-align: center;
+}
+
+.bx-db-help-popup {
+    max-width: 25rem;
+    max-height: 25rem;
+    overflow: auto;
+}
+
+/*--- Design Boxes Menu ---*/
+.bx-db-menu ul li {
+    border-radius: 0;
+
+    white-space: nowrap;
+}
+.bx-db-menu ul.bx-menu-hor,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor.bx-menu-more-auto:not(.bx-menu-submenu-more-auto) {
+    display: -webkit-inline-flex;
+    display: inline-flex;
+    -webkit-justify-content: flex-end;
+    justify-content: flex-end;
+}
+.bx-db-menu ul.bx-menu-hor > li,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor > li {
+    display: block;
+
+    -webkit-flex: 0 1 auto; 
+    -ms-flex:  0 1 auto; 
+    flex:  0 1 auto;
+
+    padding: 0 0.5rem;
+
+    border-bottom: 1px solid transparent;
+}
+.bx-db-menu .bx-menu-custom.bx-menu-custom-hor > li > span {
+    display: block;
+
+    margin: 0;
+    padding: 0;
+}
+.bx-db-menu ul.bx-menu-hor > li a.bx-btn,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor > li a.bx-btn {
+    display: inline-block;
+    float: none;
+}
+.bx-db-menu ul.bx-menu-hor > li a > span,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor > li a > span {
+    vertical-align: middle;
+}
+.bx-db-menu ul > li:not(.bx-menu-item-more-auto) a .sys-icon,
+.bx-db-menu ul > li:not(.bx-menu-item-more-auto) a .sys-icon-image {
+    display: none;
+}
+.bx-db-menu ul.bx-menu-submenu-more-auto {
+    line-height: normal;
+}
+.bx-db-menu ul li:not(.bx-menu-item-more-auto) a span,
+.bx-db-menu .bx-menu-item-more-popup ul.bx-menu-custom.bx-menu-submenu-more-auto li:not(.bx-menu-item-more-auto) a span,
+.bx-db-menu .bx-popup .bx-db-menu-popup ul.bx-menu-custom.bx-menu-custom-ver li a > span,
+.bx-db-menu .bx-popup .bx-db-menu-popup ul.bx-menu-ver li a > div > span {
+    margin: 0;
+}
+.bx-db-menu ul.bx-menu-hor li.bx-menu-tab-active,
+.bx-db-menu ul.bx-menu-custom.bx-menu-custom-hor li.bx-menu-tab-active {
+    border-bottom: 0;
+}
+.bx-db-menu ul.bx-menu-custom-ver li.bx-menu-item-more-auto {
+    display: none;
+}
+
+/* --------------- popup boxes */
+
+html.bx-popup-lock, body.bx-popup-lock {
+    overflow: hidden;
+}
+
+body > .bx-popup-wrapper {
+    max-height:100%;
+    overflow-x:hidden;
+    overflow-y:auto;
+    /* -webkit-overflow-scrolling:touch; */
+}
+
+.bx-popup-transitions {
+    -webkit-transition: opacity 0.2s ease-in-out, -webkit-transform 0.2s ease-in-out;
+    transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
+}
+
+.bx-popup-inactive {
+    -webkit-opacity: 0;
+    opacity: 0;
+
+    -webkit-transform: scale(0.75);
+    transform: scale(0.75);
+}
+
+.bx-popup-active {
+    max-height:100%;
+    overflow-y:auto;
+
+    -webkit-opacity: 1;
+    opacity: 1;
+
+    -webkit-transform: scale(1);
+    transform: scale(1);
+
+    -webkit-transition: opacity 0.2s ease-in-out, -webkit-transform 0.2s ease-in-out;
+    transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
+}
+
+.bx-popup-width {
+    width: 500px;
+}
+
+.bx-popup-width-small {
+    width: 14rem;
+}
+
+.bx-popup-border {
+    border:1px solid #383d66;
+}
+
+.bx-popup-color-bg {
+    background-color:#fff;
+}
+
+.bx-popup-fog {
+    position: fixed;
+    top: 0;
+    left: 0;
+    opacity: 0.7;
+    background-color: #ffffff;
+    -webkit-backface-visibility: hidden;
+}
+
+.bx-popup {    
+    overflow:hidden;
+}
+
+.bx-popup-header {
+    position: relative;
+}
+.bx-popup-header-color-bg {
+    background-color: #3b8686;
+}
+
+.bx-popup-header .bx-db-menu-tab-btn {
+    padding-top:16px;
+}
+
+.bx-popup-header h2 {
+    margin:0;
+    text-align:center;
+    color:#FFFFFF;
+}
+
+.bx-popup-box-pointer {
+    display:none;
+    position:relative;
+    top:10px;
+    width:13px;
+    height:7px;
+    overflow:hidden;
+}
+
+.bx-popup-box-pointer > i {
+    display:block;
+    position:absolute;
+    top:-5px;
+    width:0px;
+    height:0px;
+    border-width:6px;
+    border-style:solid;
+    border-top-color:transparent;
+    border-right-color:transparent;
+    border-left-color:transparent;
+}
+
+.bx-popup-image-container {
+    position: relative;
+}
+
+.bx-popup-image-btn {
+    position: absolute;
+    right: 0;
+    top: 0;
+}
+
+.bx-popup-image-wrapper img {
+    display: block;
+}
+
+.bx-popup-element-close:hover,
+.bx-popup-element-close {
+    position: absolute;
+    top: 0;
+    right: 0;
+    text-decoration: none;
+    font-size: 1.75rem !important;
+}
+
+.bx-popup-slide-wrapper {
+    width:100%;
+
+    background-color: rgba(255, 255, 255, 0.9);
+}
+.bx-popup-slide-wrapper li {
+    text-align:right;
+}
+
+.bx-popup-trans-wrapper .bx-popup-close-wrapper {
+    display: -webkit-flex;
+    display: flex;
+    -webkit-justify-content: flex-end;
+    justify-content: flex-end;
+}
+.bx-popup-trans-wrapper .bx-popup-close-wrapper .bx-popup-element-close {
+    position: relative;
+    line-height: 1.2rem;
+}
+.bx-popup-image-wrapper .bx-def-border-radius-block {
+    border-top-left-radius: 0 !important;
+    border-top-right-radius: 0 !important;
+}
+
+#bx-popup-alert,
+#bx-popup-confirm,
+#bx-popup-prompt {
+	max-width: 500px;
+	max-width: 31.25rem;
+}
+
+.popup_alert_actions .bx-btn,
+.popup_confirm_actions .bx-btn,
+.popup_prompt_actions .bx-btn {
+    float: none;
+}
+
+/* --------------- full screen popup */
+.bx-popup-full-screen.bx-popup-trans-wrapper,
+.bx-popup-full-screen.bx-popup-box-wrapper,
+.bx-popup-full-screen .bx-popup-trans,
+.bx-popup-full-screen .bx-popup-box {
+    position:fixed !important;
+    top:0 !important;
+    left:0 !important;
+    bottom:0 !important;
+    right:0 !important;
+    width:100%;
+    width:100vw;
+    height:100%;
+    height:100vh;
+    overflow:auto;
+}
+
+.bx-popup-full-screen .bx-popup-trans > div,
+.bx-popup-full-screen .bx-popup-box > div {
+    border-radius:0;
+    -moz-border-radius:0;
+    -webkit-border-radius:0;
+}
+
+.bx-popup-full-screen.bx-popup-trans-wrapper .bx-popup-border,
+.bx-popup-full-screen.bx-popup-box-wrapper .bx-popup-border {
+    border:none;
+}
+
+.bx-popup-full-screen.bx-popup-trans-wrapper .bx-popup,
+.bx-popup-full-screen.bx-popup-box-wrapper .bx-popup {
+    -moz-box-shadow:none;
+    -webkit-box-shadow:none;
+    box-shadow:none;
+}
+
+.bx-popup-full-screen .bx-popup-width {
+    width:100%;
+}
+
+.bx-popup-full-screen .bx-popup .bx-popup-close-wrapper {
+    display: flex !important;
+}
+
+/* --------------- notification boxes */
+.bx-popup-inline-holder {
+	padding-top: 70px;
+   	padding-top: 4.375rem;
+}
+.bx-popup-inline {
+	width: 100%;
+}
+.bx-popup-inline .bx-popup {
+	position: relative;
+
+	width: 100%;
+    height: 4.25rem;
+
+    border-width: 1px 0px 1px 0px;
+	border-color: #000000;
+	border-radius: 0px;
+
+	background-color: rgba(85, 85, 85, .9);
+
+	color: #ffffff;
+}
+.bx-popup-inline .bx-popup .bx-popup-content {
+	position: relative;
+	display: flex;
+
+	height: 100%;
+
+	padding: 0px 3.75rem;
+
+	align-items: center; 
+}
+.bx-popup-inline .bx-popup .bx-popup-content > * {
+	max-height: 100%; 
+}
+.bx-popup-inline .bx-popup .bx-popup-actions {
+	position: absolute;
+	display: flex;
+
+	height: 100%;
+	top: 0px;
+
+	align-items: center;
+}
+.bx-popup-inline .bx-popup .bx-pa-left {
+	left: 0px;
+}
+.bx-popup-inline .bx-popup .bx-pa-right {
+	right: 0px;
+}
+.bx-popup-inline .bx-popup .bx-popup-actions a {
+	text-decoration: none;
+	font-size: 1.75rem;
+	color: #9c9c9c;
+}
+
+/* --------------- chain notifications */
+div.bx-popup-chain {
+    position: relative;
+    max-width: 22.5rem;
+}
+div.bx-popup-chain-cnt {
+    position: relative;
+}
+div.bx-popup-chain-item {
+    position: relative;
+    width: 100%;
+    overflow: hidden;
+}
+div.bx-popup-chain-item-actions {
+    position: absolute;
+    top: 0px;
+}
+div.bx-popup-cia-left {
+    left: 0px;
+}
+div.bx-popup-cia-right {
+    right: 0px;
+}
+div.bx-popup-chain-item-actions a {
+    line-height: initial;
+    text-decoration: none;
+}
+div.bx-popup-chain-item-actions .sys-icon {
+	display: inline-block;
+	width: 1.5rem;
+	height: 1.5rem;
+	line-height: 1.5rem;
+	text-align: center;
+}
+div.bx-popup-chain-item .bx-loading-ajax {
+    height: auto;
+}
+
+/* --------------- message box */
+.bx-msg-box-container {
+    padding:0px 5px;
+    text-align:center;
+}
+
+.bx-msg-box {
+    display:inline-block; 
+    margin-right:auto;
+    margin-left:auto;
+
+    text-align:center;
+    font-size:1rem;
+    padding:10px;
+    text-transform:uppercase;
+}
+
+/* --------------- loading indicator */
+
+.bx-loading-ajax {
+    position:absolute;
+    width:100px;
+    height:100px;
+    margin-left:auto;
+    margin-right:auto;
+
+    background-color: transparent;
+
+	border-radius:10px;
+	-moz-border-radius:10px;
+	-webkit-border-radius:10px;
+
+    text-align:center;
+    color:#CCCCCC;
+}
+.bx-loading-ajax.static {
+    position: static;
+}
+.bx-loading-ajax svg {
+    box-sizing: content-box;
+    width: 50px;
+    height: 50px;
+    padding: 25px;  
+}
+
+.bx-loading-ajax-btn {
+    position: relative;
+    display: inline-block;
+    vertical-align: top;
+
+    min-width: 2.0rem;
+
+    margin-left: 5px;
+}
+
+@-moz-document url-prefix() {
+    button.bx-btn .bx-loading-ajax-btn {
+        top: -1px;
+    }
+}
+
+button.bx-btn:focus,
+button.bx-btn:focus .bx-loading-ajax-btn {
+    outline: 0;	
+}
+
+.bx-form-element-wrapper .bx-loading-ajax {
+    width: 4rem;
+    height: 4rem;
+}
+
+
+/* --------------- SVG loading indicator */
+svg .outer, 
+svg .inner{
+	-webkit-transform-origin: 50% 50%;
+   	-moz-transform-origin: 50% 50%;
+    transform-origin: 50% 50%;
+}
+svg .outer{
+	-webkit-animation: clockwise 5s infinite linear;
+	-moz-animation: clockwise 5s infinite linear;
+    animation: clockwise 5s infinite linear;
+    
+
+}
+svg .inner{
+	-webkit-animation: counter-clockwise 2s infinite linear;
+	-moz-animation: counter-clockwise 2s infinite linear;
+    animation: counter-clockwise 2s infinite linear;
+    
+}
+svg .colored .inner .p1 {
+	fill:#2AB191;
+}
+svg .colored .inner .p2,
+svg .colored .outer .p1 {
+	fill:#4990E2;
+}
+svg .colored .outer .p2 {
+	fill:#EE6666;
+}
+svg .gray .inner .p1,
+svg .gray .inner .p2,
+svg .gray .outer .p1,
+svg .gray .outer .p2 {
+	fill:#4A4A4A;
+}
+
+@-webkit-keyframes counter-clockwise {
+    0% { -webkit-transform: rotate(0deg); }
+    100% { -webkit-transform: rotate(360deg); }
+}
+@-webkit-keyframes clockwise {
+    0% { -webkit-transform: rotate(360deg); }
+    100% { -webkit-transform: rotate(0deg); }
+}	
+@-moz-keyframes counter-clockwise {
+    0% { -moz-transform: rotate(0deg); }
+    100% { -moz-transform: rotate(360deg); }
+}
+@-moz-keyframes clockwise {
+    0% { -moz-transform: rotate(360deg); }
+    100% { -moz-transform: rotate(0deg); }
+}
+
+@keyframes counter-clockwise {
+    0% { transform: rotate(0deg); }
+    100% { transform: rotate(360deg); }
+}
+@keyframes clockwise {
+    0% { transform: rotate(360deg); }
+    100% { transform: rotate(0deg); }
+}
+	
+/* --------------- button */
+.bx-btn-height {
+    height: 2.2rem;
+    line-height: 2.2rem;
+}
+.bx-btn-border {
+	border: none;
+}
+.bx-btn:active,
+.bx-btn:hover {	
+    background-color: #79bd9a;
+    text-decoration:none;
+}
+.bx-btn img {
+    width:1.2rem;
+    height:1.2rem;
+    max-width: none;
+
+    background-size: contain;
+    background-repeat: no-repeat;
+    background-position:center center;
+}
+.bx-btn .bx-def-valign-center{
+    height: 100%;
+}
+.bx-btn u {
+    margin-left: 10px;
+    text-decoration: none;
+}
+.bx-btn u:first-child {
+	margin-left: 0px;
+}
+.bx-btn .sys-icon {
+    font-size:1.2em;
+}
+.bx-btn .sys-icon-bigger {
+    font-size:1.4em;
+}
+
+.bx-btn-small-height {
+    height: 1.6rem;
+    line-height: 1.5rem;
+}
+
+.bx-btn-small-border {
+	border: none;
+}
+
+.bx-btn.bx-btn-primary {
+    background-color: #357877;
+}
+.bx-btn.bx-btn-primary:hover,
+.bx-btn.bx-btn-primary:active { 
+    background-color: #3a8686;
+}
+
+.bx-btn.bx-btn-symbol {
+    font-family: "Times New Roman", Georgia, Serif;
+    font-size:1.3rem;
+    line-height:32px;
+}
+.bx-btn.bx-btn-symbol:hover {
+    text-decoration:none;
+}
+.bx-btn.bx-btn-symbol-small {
+    font-size:1rem;
+}
+
+.bx-btn-group .bx-btn {
+    margin-left:0px;
+    margin-right:0px;
+}
+.bx-btn-group .bx-btn:not(:first-child):after {
+    content:"";
+    position:absolute;
+    display:block;
+    top:0px;
+    left:-0.05em;
+    width:0.1em;
+    height:100%;
+    background-color:rgba(255,255,255,0.1);
+}
+.bx-btn-group .bx-btn + .bx-btn:not(:last-child) {
+	border-radius:0px;
+	-moz-border-radius:0px;
+	-webkit-border-radius:0px;    
+}
+.bx-btn-group .bx-btn:first-child {
+    -webkit-border-top-right-radius: 0px;
+    -webkit-border-bottom-right-radius: 0px;
+    -moz-border-radius-topright: 0px;
+    -moz-border-radius-bottomright: 0px;
+    border-top-right-radius: 0px;
+    border-bottom-right-radius: 0px;
+}
+.bx-btn-group .bx-btn:last-child {
+    -webkit-border-top-left-radius: 0px;
+    -webkit-border-bottom-left-radius: 0px;
+    -moz-border-radius-topleft: 0px;
+    -moz-border-radius-bottomleft: 0px;
+    border-top-left-radius: 0px;
+    border-bottom-left-radius: 0px;
+}
+
+/* --------------- button at the end of cut string */
+
+.bx-str-limit {
+    cursor: pointer;
+}
+
+/* --------------- categories */
+
+.bx-category-list-item {
+	line-height: 1.5rem;
+}
+.bx-category-list-item-selected {
+	font-weight: bold;
+}
+
+/* --------------- Show More */
+.bx-overflow-ready {
+    max-height: 250px;
+    overflow: hidden;
+}
+.bx-overflow-ready.bx-or-cmt {
+    max-height: 80px;
+}
+.bx-overflow:before {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+    height: 20%;
+
+    content: '';
+}
+
+.bx-show-more {
+    padding-top: 0.25rem;
+    padding-bottom: 0.25rem;
+}
+.bx-show-more a {
+    display: flex;
+    justify-content: center;
+}
+
+/* --------------- Flickity: Tailwind overwrite */
+.flickity-enabled .flickity-viewport .flickity-slider img,
+.flickity-enabled .flickity-viewport .flickity-slider video {
+    display: inline;
+    max-width: none;
+}

+ 192 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files/template/scripts/BxBaseIconsetLucide.php

@@ -0,0 +1,192 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaBaseView UNA Base Representation Classes
+ * @{
+ */
+
+/**
+ * Basic Lucide iconset representation.
+ * @see BxDolIconset
+ */
+class BxBaseIconsetLucide extends BxBaseIconset
+{
+    protected $_aMap;
+
+    public function __construct ($aObject, $oTemplate)
+    {
+        parent::__construct ($aObject, $oTemplate);
+
+        $this->_aMap = [
+            'ad' => 'circle-star',
+            'address-book' => 'contact-round',
+            'address-card' => 'id-card',
+            'angle-double-down' => 'chevrons-down',
+            'angle-double-left' => 'chevrons-left',
+            'angle-double-right' => 'chevrons-right',
+            'arrows-alt' => 'move',
+            'bars' => 'menu',
+            'book-reader' => 'book-open-text',
+            'buffer' => 'layers',
+            'building' => 'building',
+            'bullhorn' => 'megaphone',
+            'calendar-alt' => 'calendar-days',
+            'calendar-day' => 'calendar-days',
+            'calendar-plus' => 'calendar-plus',
+            'camera-retro' => 'camera',
+            'caret-right' => 'chevron-right',
+            'cart-arrow-down' => 'shopping-cart',
+            'cart-plus' => 'shopping-cart',
+            'cc-stripe' => 'credit-card',
+            'certificate' => 'award',
+            'chart-pie' => 'pie-chart',
+            'check-circle' => 'check-circle',
+            'check-double' => 'check-check',
+            'clipboard-check' => 'clipboard-check',
+            'cog' => 'settings',
+            'cogs' => 'settings',
+            'comment' => 'message-circle',
+            'comment-dots' => 'message-square-dashed',
+            'comments' => 'messages-square',
+            'desktop' => 'monitor',
+            'donate' => 'hand-coins',
+            'ellipsis-h' => 'more-horizontal',
+            'ellipsis-v' => 'more-vertical',
+            'envelope' => 'mail',
+            'envelope-open-text' => 'mail-open',
+            'exchange-alt' => 'repeat',
+            'exclamation-circle' => 'alert-circle',
+            'exclamation-triangle' => 'alert-triangle',
+            'fa-book' => 'book-text',
+            'fa-bookmark' => 'bookmark',
+            'fa-certificate' => 'badge',
+            'fa-check-circle' => 'circle-check', 
+            'fa-smile' => 'smile',
+            'fa-thumbs-up' => 'thumbs-up',
+            'facebook-square' => 'facebook',
+            'file-alt' => 'file',
+            'file-alt' => 'file-text',
+            'file-export' => 'file-output',
+            'file-invoice' => 'receipt',
+            'file-word' => 'file-text',
+            'fire' => 'flame',
+            'group' => 'users',
+            'google' => 'log-in', //TODO: Brand icon. Update later.
+            'hand-holding-usd' => 'hand-coins',
+            'hashtag' => 'hash',
+            'helpcircle' => 'help-circle',
+            'house' => 'home',
+            'industry' => 'factory',
+            'info-circle' => 'info',
+            'keyround' => 'key-round',
+            'language' => 'languages',
+            'linkedin-in' => 'linkedin',
+            'lockopen' => 'unlock',
+            'mail-bulk' => 'mails',
+            'map-marker' => 'map-pin',
+            'map-marker-alt' => 'map-pin',
+            'money-check-alt' => 'wallet-cards',
+            'object-group' => 'layout-dashboard',
+            'pencil-alt' => 'pencil-line',
+            'pencil-ruler' => 'ruler',
+            'photo-video' => 'image-play',
+            'plus-circle' => 'plus-circle',
+            'qrcode' => 'qr-code',
+            'question-circle' => 'circle-question-mark',
+            'quote-right' => 'quote',
+            'remove' => 'x',
+            'reply-all' => 'reply-all',
+            'search-location' => 'search-check',
+            'share-alt' => 'share-2',
+            'shield-alt' => 'shield',
+            'sign-in-alt' => 'log-in',
+            'sign-out-alt' => 'log-out',
+            'star-half-o' => 'star-half',
+            'stopwatch' => 'timer',
+            'swatchbook' => 'swatch-book',
+            'sync' => 'refresh-ccw',
+            'tachometer-alt' => 'gauge',
+            'tasks' => 'check-square',
+            'th-large' => 'grid-2x2',
+            'thumbtack' => 'pin',
+            'times' => 'x',
+            'times-circle' => 'x-circle',
+            'toolbox' => 'tool-case',
+            'trash2' => 'trash-2',
+            'unlock-alt' => 'unlock',
+            'user' => 'user-round',
+            'user-friends' => 'users',
+            'user-plus' => 'user-round-plus',
+            'user-secret' => 'user-key',
+            'user-slash' => 'user-x',
+            'user-shield' => 'user-round-search',
+            'user-check' => 'user-round-check',
+            'user-times' => 'user-x',
+            'users' => 'users-round',
+            'video-camera' => 'video',
+            'at' => 'at-sign',
+            'backspace' => 'delete',
+            'trash-alt' => 'trash',
+        ];
+    }
+
+    public function getPreloaderJs()
+    {
+        return 'https://unpkg.com/lucide@latest';
+    }
+
+    public function getIcon($sIcon)
+    {
+        $sIcon = trim(preg_replace('/(sys-icon|far|col-\w+)/i', '', $sIcon));
+        if(isset($this->_aMap[$sIcon]))
+            $sIcon = $this->_aMap[$sIcon];
+
+        return bx_gen_method_name($sIcon, ['_', '-']);
+    }
+
+    public function getCode()
+    {
+        $sMap = json_encode($this->_aMap);
+
+        $sCode = <<<BLAH
+        function bx_iconset_init_lucide() {
+            if(!window.lucide || !lucide.icons)
+                return;
+ 
+            const aMap = $sMap;
+ 
+            document.querySelectorAll('i.sys-icon').forEach(el => {
+                if(el.hasAttribute('data-lucide'))
+                    return;
+
+                const sName = el.getAttribute('class').replace(/(sys-icon-bigger|sys-icon|fab|far|fas|col-\w+)/gi, '').trim().split(' ').shift();
+                if(sName)
+                  el.setAttribute('data-lucide', aMap[sName] != undefined ? aMap[sName] : sName);
+                else
+                  console.warn('Lucide: no icon class found on', el);
+            });
+
+            lucide.createIcons({
+              attrs: { class: ['sys-icon'] },
+              nameAttr: 'data-lucide'
+            });
+        };
+
+        bx_iconset_init_lucide();
+
+        if (typeof glOnProcessHtml === 'undefined')
+            glOnProcessHtml = [];
+        if (glOnProcessHtml instanceof Array) {
+            glOnProcessHtml.push(function(e) {
+                bx_iconset_init_lucide();
+            });
+        }
+BLAH;
+
+        return $this->_oTemplate->_wrapInTagJsCode($sCode);
+    }
+}
+
+/** @} */

+ 6 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/files_delete.php

@@ -0,0 +1,6 @@
+<?php
+
+$aFilesDelete = [
+
+];
+

+ 3 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/script.php

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

+ 15 - 0
upgrade/files/15.0.0.B2-15.0.0.B3/sql.sql

@@ -0,0 +1,15 @@
+
+SET @sStorageEngine = (SELECT `value` FROM `sys_options` WHERE `name` = 'sys_storage_default');
+
+-- Options
+
+SET @iCategoryId = (SELECT `id` FROM `sys_options_categories` WHERE `name` = 'hidden');
+
+INSERT IGNORE INTO `sys_options`(`category_id`, `name`, `caption`, `value`, `type`, `extra`, `check`, `check_params`, `check_error`, `order`) VALUES
+(@iCategoryId, 'sys_alerts_logger', '_adm_stg_cpt_option_sys_alerts_logger', 'on', 'checkbox', '', '', '', '', 310),
+(@iCategoryId, 'sys_alerts_stats', '_adm_stg_cpt_option_sys_alerts_stats', '', 'checkbox', '', '', '', '', 312);
+
+-- Last step is to update current version
+
+UPDATE `sys_modules` SET `version` = '15.0.0-B3' WHERE (`version` = '15.0.0.B2' OR `version` = '15.0.0-B2') AND `name` = 'system';
+

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff