AntonLV 4 лет назад
Родитель
Сommit
83ced2aa66

+ 5 - 0
inc/classes/BxDolTranscoderImage.php

@@ -116,6 +116,11 @@ class BxDolTranscoderImage extends BxDolTranscoder implements iBxDolFactoryObjec
         return parent::getFileUrl($mixedHandler);
     }
 
+    public function getFileMimeType($mixedHandler)
+    {
+        return $this->_oStorage->getMimeTypeByFileName($this->getFileUrl($mixedHandler));
+    }
+
     /**
      * Get file url when file isn't transcoded yet
      */

+ 3 - 0
inc/params.inc.php

@@ -33,12 +33,15 @@ define('BX_DIRECTORY_STORAGE', BX_DIRECTORY_PATH_ROOT . 'storage/');
 define('BX_DOL_DIR_RIGHTS', 0777);
 define('BX_DOL_FILE_RIGHTS', 0666);
 
+define('BX_DOL_STORAGE_OBJ_FILES', 'sys_files');
 define('BX_DOL_STORAGE_OBJ_IMAGES', 'sys_images');
 
 if (!defined('BX_DOL_INT_MAX'))
     define('BX_DOL_INT_MAX', 2147483647);
 
 define('BX_DOL_TRANSCODER_OBJ_ICON_APPLE', 'sys_icon_apple');
+define('BX_DOL_TRANSCODER_OBJ_ICON_ANDROID', 'sys_icon_android');
+define('BX_DOL_TRANSCODER_OBJ_ICON_ANDROID_SPLASH', 'sys_icon_android_splash');
 define('BX_DOL_TRANSCODER_OBJ_ICON_FACEBOOK', 'sys_icon_facebook');
 define('BX_DOL_TRANSCODER_OBJ_ICON_FAVICON', 'sys_icon_favicon');
 define('BX_DOL_TRANSCODER_OBJ_COVER', 'sys_cover');

+ 12 - 3
install/sql/system.sql

@@ -369,10 +369,15 @@ INSERT INTO `sys_options`(`category_id`, `name`, `caption`, `value`, `type`, `ex
 (@iCategoryId, 'site_email_html_template_header', '_adm_stg_cpt_option_site_email_html_template_header', '<html>\r\n    <head></head>\r\n    <body bgcolor="#eee" style="margin:0; padding:0;">\r\n        <div style="padding:20px; background-color:#eee;">\r\n            <table width="100%" border="0" cellspacing="0" cellpadding="0" align="center">\r\n                <tr><td valign="top">\r\n                    <div style="color:#333; padding:20px; border:1px solid #ccc; border-radius:3px; background-color:#fff; font:14px Helvetica, Arial, sans-serif;">\r\n                        <div style="border-bottom:2px solid #eee; padding-bottom:10px; margin-bottom:20px; font-weight:bold; font-size:22px; color:#999;">{site_name}</div>', 'text', '', '', '', 1),
 (@iCategoryId, 'site_email_html_template_footer', '_adm_stg_cpt_option_site_email_html_template_footer', '\r\n                    </div>\r\n                </td></tr>\r\n                <tr><td valign="top">\r\n                    <div style="margin-top:5px; text-align:center; color:#999; font:11px Helvetica, Arial, sans-serif;">{about_us}&nbsp;&nbsp;&nbsp;{unsubscribe}</div>\r\n                </td></tr>\r\n            </table>\r\n        </div>\r\n    </body>\r\n</html>', 'text', '', '', '', 2),
 
+(@iCategoryId, 'sys_site_icon', '', '0', 'digit', '', '', '', 15),
+(@iCategoryId, 'sys_site_icon_svg', '', '0', 'digit', '', '', '', 16),
+(@iCategoryId, 'sys_site_icon_apple', '', '0', 'digit', '', '', '', 17),
+(@iCategoryId, 'sys_site_icon_android', '', '0', 'digit', '', '', '', 18),
+(@iCategoryId, 'sys_site_icon_android_splash', '', '0', 'digit', '', '', '', 19),
+
 (@iCategoryId, 'sys_site_logo', '', '0', 'digit', '', '', '', 20),
 (@iCategoryId, 'sys_site_logo_alt', '_adm_dsg_txt_alt_text', '0', 'text', '', '', '', 21),
-(@iCategoryId, 'sys_site_icon', '', '0', 'digit', '', '', '', 22),
-(@iCategoryId, 'sys_site_logo_aspect_ratio', '_adm_stg_cpt_option_sys_site_logo_aspect_ratio', '', 'digit', '', '', '', 23),
+(@iCategoryId, 'sys_site_logo_aspect_ratio', '_adm_stg_cpt_option_sys_site_logo_aspect_ratio', '', 'digit', '', '', '', 22),
 -- (@iCategoryId, 'sys_site_logo_width', '_adm_stg_cpt_option_sys_site_logo_width', '240', 'digit', '', '', '', 23),
 -- (@iCategoryId, 'sys_site_logo_height', '_adm_stg_cpt_option_sys_site_logo_height', '48', 'digit', '', '', '', 24),
 
@@ -5225,6 +5230,8 @@ CREATE TABLE IF NOT EXISTS `sys_objects_transcoder` (
 INSERT INTO `sys_objects_transcoder` (`object`, `storage_object`, `source_type`, `source_params`, `private`, `atime_tracking`, `atime_pruning`, `ts`, `override_class_name`, `override_class_file`) VALUES
 ('sys_image_resize', 'sys_images_resized', 'Storage', 'a:2:{s:6:"object";s:10:"sys_images";s:14:"disable_retina";b:1;}', 'no', '0', '0', '0', '', ''),
 ('sys_icon_apple', 'sys_images_resized', 'Storage', 'a:2:{s:6:"object";s:10:"sys_images";s:14:"disable_retina";b:1;}', 'no', '0', '0', '0', '', ''),
+('sys_icon_android', 'sys_images_resized', 'Storage', 'a:2:{s:6:"object";s:10:"sys_images";s:14:"disable_retina";b:1;}', 'no', '0', '0', '0', '', ''),
+('sys_icon_android_splash', 'sys_images_resized', 'Storage', 'a:2:{s:6:"object";s:10:"sys_images";s:14:"disable_retina";b:1;}', 'no', '0', '0', '0', '', ''),
 ('sys_icon_facebook', 'sys_images_resized', 'Storage', 'a:2:{s:6:"object";s:10:"sys_images";s:14:"disable_retina";b:1;}', 'no', '0', '0', '0', '', ''),
 ('sys_icon_favicon', 'sys_images_resized', 'Storage', 'a:1:{s:6:"object";s:10:"sys_images";}', 'no', '0', '0', '0', '', ''),
 ('sys_cover', 'sys_images_resized', 'Storage', 'a:1:{s:6:"object";s:10:"sys_images";}', 'no', '0', '0', '0', '', ''),
@@ -5290,7 +5297,9 @@ CREATE TABLE IF NOT EXISTS `sys_transcoder_filters` (
 
 INSERT INTO `sys_transcoder_filters` (`transcoder_object`, `filter`, `filter_params`, `order`) VALUES
 ('sys_image_resize', 'ResizeVar', '', '0'),
-('sys_icon_apple', 'Resize', 'a:4:{s:1:"w";s:3:"152";s:1:"h";s:3:"152";s:13:"square_resize";s:1:"1";s:10:"force_type";s:3:"png";}', '0'),
+('sys_icon_apple', 'Resize', 'a:3:{s:1:"w";s:3:"180";s:1:"h";s:3:"180";s:13:"square_resize";s:1:"1";}', '0'),
+('sys_icon_android', 'Resize', 'a:3:{s:1:"w";s:3:"192";s:1:"h";s:3:"192";s:13:"square_resize";s:1:"1";}', '0'),
+('sys_icon_android_splash', 'Resize', 'a:3:{s:1:"w";s:3:"512";s:1:"h";s:3:"512";s:13:"square_resize";s:1:"1";}', '0'),
 ('sys_icon_facebook', 'Resize', 'a:4:{s:1:"w";s:3:"100";s:1:"h";s:3:"100";s:13:"square_resize";s:1:"1";s:10:"force_type";s:3:"png";}', '0'),
 ('sys_icon_favicon', 'Resize', 'a:4:{s:1:"w";s:2:"16";s:1:"h";s:2:"16";s:13:"square_resize";s:1:"1";s:10:"force_type";s:3:"png";}', '0'),
 ('sys_cover', 'Resize', 'a:3:{s:1:"w";s:4:"1920";s:1:"h";s:3:"720";s:10:"force_type";s:3:"png";}', '0'),

+ 75 - 0
manifest.json.php

@@ -0,0 +1,75 @@
+<?php
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+/**
+* Note: This file is intended to be publicly accessible.
+*/
+
+require_once('./inc/header.inc.php');
+
+$aContent = [
+    'name' => '',
+    'short_name' => '',
+    'start_url' => '/',
+    'display' => 'standalone',
+];
+
+$sPattern = '/^[A-Za-z0-9\.\-]+$/';
+
+if(!empty($_GET['bx_name']) && preg_match($sPattern, $_GET['bx_name']))
+    $aContent['name'] = $_GET['bx_name'];
+
+if(!empty($_GET['bx_short_name']) && preg_match($sPattern, $_GET['bx_short_name']))
+    $aContent['short_name'] = $_GET['bx_short_name'];
+else
+    $aContent['short_name'] = $aContent['name'];
+
+if(isLogged())
+    $aContent['gcm_sender_id'] = '482941778795';
+
+/*
+ * Android device icons
+ */
+
+$aAdi = [];
+if(($iId = (int)getParam('sys_site_icon_android')) != 0) {
+    $oTranscoder = BxDolTranscoderImage::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_ANDROID);
+
+    $sSizes = '192x192';
+    if(($aTranscoderParams = $oTranscoder->getFilterParams('Resize')) !== false)
+        $sSizes = $aTranscoderParams['w'] . 'x' . $aTranscoderParams['h'];
+
+    $aAdi[] = [
+        'src' => $oTranscoder->getFileUrl($iId), 
+        'type' => $oTranscoder->getFileMimeType($iId), 
+        'sizes' => $sSizes
+    ];
+}
+
+if(($iId = (int)getParam('sys_site_icon_android_splash')) != 0) {
+    $oTranscoder = BxDolTranscoderImage::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_ANDROID_SPLASH);
+
+    $sSizes = '512x512';
+    if(($aTranscoderParams = $oTranscoder->getFilterParams('Resize')) !== false)
+        $sSizes = $aTranscoderParams['w'] . 'x' . $aTranscoderParams['h'];
+
+    $aAdi[] = [
+        'src' => $oTranscoder->getFileUrl($iId), 
+        'type' => $oTranscoder->getFileMimeType($iId), 
+        'sizes' => $sSizes
+    ];
+}
+
+if(!empty($aAdi) && is_array($aAdi))
+    $aContent['icons'] = $aAdi;
+
+header("Content-Type: application/json");
+header("X-Robots-Tag: none");
+echo json_encode($aContent);
+?>

+ 15 - 4
modules/boonex/english/data/langs/system/en.xml

@@ -670,12 +670,23 @@
 	<string name="_adm_dsg_txt_cover_disabled"><![CDATA[Disable pages covers]]></string>
 	<string name="_adm_dsg_txt_splash_enabled"><![CDATA[Enable splash page]]></string>
 	<string name="_adm_dsg_txt_default"><![CDATA[default]]></string>
-	<string name="_adm_dsg_txt_icon_apple"><![CDATA[Apple Touch Devices (152x152)]]></string>
-	<string name="_adm_dsg_txt_icon_facebook"><![CDATA[Facebook Share (100x100)]]></string>
-	<string name="_adm_dsg_txt_icon_favicon"><![CDATA[Favicon (16x16)]]></string>
+	<string name="_adm_dsg_txt_icon"><![CDATA[Favicon (32x32)]]></string>
+        <string name="_adm_dsg_txt_icon_svg"><![CDATA[SVG icon with light/dark styles]]></string>
+        <string name="_adm_dsg_txt_icon_apple"><![CDATA[Apple devices icon (180x180)]]></string>
+        <string name="_adm_dsg_txt_icon_android"><![CDATA[Android devices icon (192×192)]]></string>
+        <string name="_adm_dsg_txt_icon_android_splash"><![CDATA[Android devices splash (512×512)]]></string>
 	<string name="_adm_dsg_txt_upload_cover_common"><![CDATA[Upload new common pages cover]]></string>
 	<string name="_adm_dsg_txt_upload_cover_unit_profile"><![CDATA[Upload new profile unit cover]]></string>
-	<string name="_adm_dsg_txt_upload_icon"><![CDATA[Upload New Site Icon]]></string>
+	<string name="_adm_dsg_txt_upload_icon"><![CDATA[favicon.ico]]></string>
+        <string name="_adm_dsg_txt_upload_icon_inf"><![CDATA[single 32×32 ico image file is recommended]]></string>
+        <string name="_adm_dsg_txt_upload_icon_svg"><![CDATA[SVG icon]]></string>
+        <string name="_adm_dsg_txt_upload_icon_svg_inf"><![CDATA[svg image with dark and light styles embedded is recommended]]></string>
+        <string name="_adm_dsg_txt_upload_icon_apple"><![CDATA[Apple devices icon]]></string>
+        <string name="_adm_dsg_txt_upload_icon_apple_inf"><![CDATA[padding and background color is recommended for this image]]></string>
+        <string name="_adm_dsg_txt_upload_icon_android"><![CDATA[Android devices icon]]></string>
+        <string name="_adm_dsg_txt_upload_icon_android_inf"><![CDATA[for Android home screen]]></string>
+        <string name="_adm_dsg_txt_upload_icon_android_splash"><![CDATA[Android devices splash]]></string>
+        <string name="_adm_dsg_txt_upload_icon_android_splash_inf"><![CDATA[for Android splash screen]]></string>
 	<string name="_adm_dsg_txt_upload_image"><![CDATA[Upload New Logo Image]]></string>
 	<string name="_adm_dsg_txt_upload_image_preview"><![CDATA[Current Logo Image]]></string>
         <string name="_adm_dsg_txt_upload_mark"><![CDATA[Upload New Mark Image]]></string>

+ 15 - 4
modules/boonex/russian/data/langs/system/ru.xml

@@ -669,12 +669,23 @@
 	<string name="_adm_dsg_txt_cover_disabled"><![CDATA[Выключить обложки страниц]]></string>
 	<string name="_adm_dsg_txt_splash_enabled"><![CDATA[Включить титульную страницу]]></string>
 	<string name="_adm_dsg_txt_default"><![CDATA[по умолчанию]]></string>
-	<string name="_adm_dsg_txt_icon_apple"><![CDATA[Устройства Apple Touch (152x152)]]></string>
-	<string name="_adm_dsg_txt_icon_facebook"><![CDATA[Facebook (100x100)]]></string>
-	<string name="_adm_dsg_txt_icon_favicon"><![CDATA[Favicon (16x16)]]></string>
+        <string name="_adm_dsg_txt_icon"><![CDATA[Фавикон (32x32)]]></string>
+        <string name="_adm_dsg_txt_icon_svg"><![CDATA[Иконка SVG со светлым/темным стилями]]></string>
+        <string name="_adm_dsg_txt_icon_apple"><![CDATA[Иконка для устройств Apple (180x180)]]></string>
+        <string name="_adm_dsg_txt_icon_android"><![CDATA[Иконка для устройств Android (192×192)]]></string>
+        <string name="_adm_dsg_txt_icon_android_splash"><![CDATA[Заставка для устройств Android(512×512)]]></string>
 	<string name="_adm_dsg_txt_upload_cover_common"><![CDATA[Загрузить новое изображение обложки обычных страниц]]></string>
 	<string name="_adm_dsg_txt_upload_cover_unit_profile"><![CDATA[Загрузить новое изображение обложки миниатюры профиля]]></string>
-	<string name="_adm_dsg_txt_upload_icon"><![CDATA[Загрузить новую иконку сайта]]></string>
+	<string name="_adm_dsg_txt_upload_icon"><![CDATA[favicon.ico]]></string>
+        <string name="_adm_dsg_txt_upload_icon_inf"><![CDATA[Рекомендуется единичный ico файл размером 32×32]]></string>
+        <string name="_adm_dsg_txt_upload_icon_svg"><![CDATA[SVG иконка]]></string>
+        <string name="_adm_dsg_txt_upload_icon_svg_inf"><![CDATA[Рекомендуется SVG изображение с включенными темным и светлым стилями]]></string>
+        <string name="_adm_dsg_txt_upload_icon_apple"><![CDATA[Иконка для устройств Apple]]></string>
+        <string name="_adm_dsg_txt_upload_icon_apple_inf"><![CDATA[Рекомендуется  использовать изображение с отступами и цветным фоном]]></string>
+        <string name="_adm_dsg_txt_upload_icon_android"><![CDATA[Иконка для устройств Android]]></string>
+        <string name="_adm_dsg_txt_upload_icon_android_inf"><![CDATA[Для домашнего экрана Android]]></string>
+        <string name="_adm_dsg_txt_upload_icon_android_splash"><![CDATA[Заставка для устройств Android]]></string>
+        <string name="_adm_dsg_txt_upload_icon_android_splash_inf"><![CDATA[Для экрана-заставки Android]]></string>
 	<string name="_adm_dsg_txt_upload_image"><![CDATA[Загрузить новую лого-картинку]]></string>
 	<string name="_adm_dsg_txt_upload_image_preview"><![CDATA[Текущая лого-картинка]]></string>
         <string name="_adm_dsg_txt_upload_mark"><![CDATA[Загрузить новую марку]]></string>

+ 0 - 26
plugins_public/onesignal/manifest.json.php

@@ -1,26 +0,0 @@
-<?php
-/**
-* Note: This file is intended to be publicly accessible.
-*/
-
-$sPattern = '/^[A-Za-z0-9\.\-]+$/';
-
-$sName = '';
-if(!empty($_GET['bx_name']) && preg_match($sPattern, $_GET['bx_name']))
-    $sName = $_GET['bx_name'];
-
-$sShortName = '';
-if(!empty($_GET['bx_short_name']) && preg_match($sPattern, $_GET['bx_short_name']))
-    $sShortName = $_GET['bx_short_name'];
-
-header("Content-Type: application/json");
-header("X-Robots-Tag: none");
-?>
-{
-  "name": "<?php echo $sName; ?>",
-  "short_name": "<?php echo (!empty($sShortName) ? $sShortName : $sName); ?>",
-  "start_url": "/",
-  "display": "standalone",
-  "gcm_sender_id": "482941778795",
-  "DO_NOT_CHANGE_GCM_SENDER_ID": "Do not change the GCM Sender ID"
-}

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
plugins_public/tailwind/css/tailwind.min.css


+ 2 - 1
plugins_public/tailwind/js/tailwind.config.js

@@ -15,9 +15,10 @@ module.exports = {
     './modules/boonex/**/js/*.js',
     './modules/boonex/**/template/*.html',
 
-    // templates
     './modules/boonex/**/data/template/**/*.html',
 
+    './studio/template/*.html',
+
     './template/*.html',
   ],
   safelist: [

+ 68 - 12
studio/classes/BxDolStudioDesigner.php

@@ -28,6 +28,7 @@ class BxDolStudioDesigner extends BxTemplStudioWidget
     protected $sParamMark;
     protected $sParamLogoAlt;
 
+    protected $aIcons;
     protected $aCovers;
 
     function __construct($sPage = "")
@@ -47,6 +48,27 @@ class BxDolStudioDesigner extends BxTemplStudioWidget
         $this->sParamMark = '';
         $this->sParamLogoAlt = 'sys_site_logo_alt';
 
+        $this->aIcons = [
+            'icon' => [
+                'storage' => BX_DOL_STORAGE_OBJ_FILES
+            ],
+            'icon_svg' => [
+                'storage' => BX_DOL_STORAGE_OBJ_IMAGES
+            ],
+            'icon_apple' => [
+                'storage' => BX_DOL_STORAGE_OBJ_IMAGES,
+                'transcoder' => BX_DOL_TRANSCODER_OBJ_ICON_APPLE
+            ],
+            'icon_android' => [
+                'storage' => BX_DOL_STORAGE_OBJ_IMAGES,
+                'transcoder' => BX_DOL_TRANSCODER_OBJ_ICON_ANDROID
+            ],
+            'icon_android_splash' => [
+                'storage' => BX_DOL_STORAGE_OBJ_IMAGES,
+                'transcoder' => BX_DOL_TRANSCODER_OBJ_ICON_ANDROID_SPLASH
+            ]
+        ];
+
         $this->aCovers = array(
             'cover_common' => array(
                 'setting' => 'sys_site_cover_common',
@@ -85,6 +107,14 @@ class BxDolStudioDesigner extends BxTemplStudioWidget
                     $aResult = array('code' => 2, 'message' => _t('_adm_dsg_err_remove_old_mark'));
                 break;
 
+            case 'delete_icon':
+                $sValue = bx_process_input(bx_get($this->sParamPrefix . '_value'));
+
+                $aResult = ['code' => 0, 'message' => ''];
+                if(empty($sValue) || !$this->deleteIcon($sValue))
+                    $aResult = ['code' => 2, 'message' => _t('_adm_dsg_err_remove_old_icon')];
+                break;
+
             case 'delete_cover':
                 $sValue = bx_process_input(bx_get($this->sParamPrefix . '_value'));
 
@@ -211,26 +241,52 @@ class BxDolStudioDesigner extends BxTemplStudioWidget
 
     function submitIcon(&$oForm)
     {
-        $iProfileId = getLoggedId();
+        $iProfileId = bx_get_logged_profile_id();
 
-        $oStorage = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES);
+        foreach($this->aIcons as $sIcon => $aIcon) {
+            $bValue = bx_get($sIcon) !== false;
+            $bFile = isset($_FILES[$sIcon]) && !empty($_FILES[$sIcon]['tmp_name']);
 
-        $iId = (int)getParam('sys_site_icon');
-        if($iId != 0 && !$oStorage->deleteFile($iId, $iProfileId))
-            return $this->getJsResult('_adm_dsg_err_remove_old_icon');
+            if(!$bValue && !$bFile)
+                continue;
 
-        $iId = $oStorage->storeFileFromForm($_FILES['image'], true, $iProfileId);
-        if($iId === false) {
-            $this->oDb->setParam('sys_site_icon', 0);
-            return $this->getJsResult(_t('_adm_dsg_err_save') . $oStorage->getErrorString(), false);
-        }
+            $sSetting = 'sys_site_' . $sIcon;
+            $oStorage = BxDolStorage::getObjectInstance($aIcon['storage']);
+
+            if(($bValue || $bFile) && (int)getParam($sSetting) != 0)
+                $this->deleteIcon($sIcon);
+
+            $iId = 0;
+            if($bValue)
+                $iId = (int)bx_get($sIcon);
+            else if($bFile) {
+                $iId = $oStorage->storeFileFromForm($_FILES[$sIcon], true, $iProfileId);
+                if($iId === false)
+                    continue;
+            }
 
-        $this->oDb->setParam('sys_site_icon', $iId);
-        $oStorage->afterUploadCleanup($iId, $iProfileId);
+            $oStorage->afterUploadCleanup($iId, $iProfileId);
+            $this->oDb->setParam($sSetting, $iId);
+        }
 
         return $this->getJsResult('_adm_dsg_scs_save', true, true, BX_DOL_URL_STUDIO . 'designer.php?page=' . BX_DOL_STUDIO_DSG_TYPE_ICON);
     }
 
+    function deleteIcon($sIcon)
+    {
+        $iProfile = bx_get_logged_profile_id();
+
+        $sSetting = 'sys_site_' . $sIcon;
+        $oStorage = BxDolStorage::getObjectInstance($this->aIcons[$sIcon]['storage']);
+
+        $iId = (int)getParam($sSetting);
+        if($iId != 0 && !$oStorage->deleteFile($iId, $iProfile) && $oStorage->getErrorCode() != BX_DOL_STORAGE_ERR_FILE_NOT_FOUND)
+            return false;
+
+        $this->oDb->setParam($sSetting, 0);
+        return true;
+    }
+
     function submitCover(&$oForm)
     {
         $iProfile = bx_get_logged_profile_id();

+ 24 - 0
studio/js/designer.js

@@ -84,6 +84,30 @@ BxDolStudioDesigner.prototype.deleteMark = function() {
     );
 };
 
+BxDolStudioDesigner.prototype.deleteIcon = function(sName) {
+    var $this = this;
+    var oDate = new Date();
+    var aParams = {_t: oDate.getTime()};
+    aParams[this.sParamPrefix + '_action'] = 'delete_icon';
+    aParams[this.sParamPrefix + '_value'] = sName;
+
+    $.post(
+        this.sActionsUrl,
+        aParams,
+        function(oData) {
+            if(oData.code != 0 && oData.message.length > 0) {
+                bx_alert(oData.message);
+                return;
+            }
+
+            $('.adm-dsg-ip-' + sName).bx_anim('hide', $this.sAnimationEffect, $this.iAnimationSpeed, function() {
+                $(this).remove();
+            });
+        },
+        'json'
+    );
+};
+
 BxDolStudioDesigner.prototype.deleteCover = function(sType, iId) {
     var $this = this;
     var oDate = new Date();

+ 25 - 17
studio/template/css/designer.css

@@ -1,43 +1,51 @@
 /*--- Template Switcher ---*/
 div.bx-dsg-template {
-	position: relative;
-	width: 100%;
+    position: relative;
+    width: 100%;
 }
 
 div.bx-dsg-icon {
-	position: absolute;
-	top: 0px;
-	left: 0px;
-	overflow: hidden;
-	line-height: 8rem;
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    overflow: hidden;
+    line-height: 8rem;
 }
 
 div.bx-dsg-icon i.sys-icon {
-	position: absolute;
-	top: 0px;
-	left: 0px;
-	height: auto;
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    height: auto;
 
     font-size: 96px;
     text-align: center;
     text-shadow: none;
-	color: white;
+    color: white;
 }
 
 div.bx-dsg-template-info {
-	overflow: hidden;
+    overflow: hidden;
 }
 
 span.bx-dsg-template-version {
-	font-weight: bold;
+    font-weight: bold;
+}
+
+/*--- Icon Manager---*/
+.adm-dsg-icon .bx-form-input-files-uploaders {
+    margin-top: 0;
+}
+.adm-dsg-icon .adm-dsg-ip-icon_svg img {
+    max-height: 2rem;
 }
 
 /*--- Cover Manager---*/
 div.bx-dsg-cover {
-	position: relative;
+    position: relative;
 }
 
 div.bx-dsg-cover-actions .bx-btn {
-	float: none;
-	display: inline-block;
+    float: none;
+    display: inline-block;
 }

+ 11 - 2
studio/template/dsr_icon.html

@@ -1,2 +1,11 @@
-<iframe name="__icon_iframe_id__" class="bx-std-hidden"></iframe>
-__form__
+<div class="adm-dsg-icon flex flex-col md:flex-row-reverse">
+    <div class="adm-dsg-icon-preview md:w-1/2">
+        <div class="adm-dsg-ip-cnf md:pl-2">__preview__</div>
+    </div>
+    <div class="adm-dsg-icon-form md:w-1/2 mt-4 md:mt-0">
+        <div class="adm-dsg-if-cnf md:pr-2">
+            <iframe name="__icon_iframe_id__" class="bx-std-hidden"></iframe>
+            __form__
+        </div>
+    </div>
+</div>

+ 8 - 5
studio/template/dsr_icon_preview.html

@@ -1,8 +1,11 @@
 <bx_repeat:images>
-    <div class="adm-dsg-icon-preview">
-        <p>
-            <b>__caption__</b>
-        </p>
-        <img src="__url__"<bx_if:show_width> width="__width__"</bx_if:show_width><bx_if:show_height> height="__height__"</bx_if:show_height> />
+    <div class="adm-dsg-icon-preview adm-dsg-ip-__name__ mt-4 first:mt-0">
+        <div class="adm-dsg-ip-title font-bold">__caption__</div>
+        <div class="adm-dsg-ip-image pt-1 pb-2">
+            <img src="__url__"<bx_if:show_width> width="__width__"</bx_if:show_width><bx_if:show_height> height="__height__"</bx_if:show_height> />
+        </div>
+        <div class="adm-dsg-ip-action">
+            <a class="bx-btn bx-btn-small" href="javascript:void(0)" onclick="__js_object__.deleteIcon('__name__');"><bx_text:_adm_dsg_btn_delete /></a>
+        </div>
     </div>
 </bx_repeat:images>

+ 110 - 49
studio/template/scripts/BxBaseStudioDesigner.php

@@ -220,49 +220,60 @@ class BxBaseStudioDesigner extends BxDolStudioDesigner
     protected function getIcon()
     {
         $oTemplate = BxDolStudioTemplate::getInstance();
+        $oDbSettings = new BxDolStudioSettingsQuery();
 
-        $sPreview = "";
-        $aTmplVars = array('bx_repeat:images' => array());
+        $aTmplVarsPreview = ['bx_repeat:images' => []];
+        foreach($this->aIcons as $sIcon => $aIcon) {
+            $sSetting = 'sys_site_' . $sIcon;
+            $iIconValue = (int)getParam($sSetting);
 
-        if(($iId = (int)getParam('sys_site_icon')) != 0) {
-            $aTranscoders = array(
-                BX_DOL_TRANSCODER_OBJ_ICON_APPLE => '_adm_dsg_txt_icon_apple',
-                BX_DOL_TRANSCODER_OBJ_ICON_FACEBOOK => '_adm_dsg_txt_icon_facebook',
-                BX_DOL_TRANSCODER_OBJ_ICON_FAVICON => '_adm_dsg_txt_icon_favicon'
-            );
+            $aSetting = [];
+            $oDbSettings->getOptions(['type' => 'by_name', 'value' => $sSetting], $aSetting, false);
+            if(!empty($aSetting) && is_array($aSetting))
+                $this->aIcons[$sIcon]['id'] = (int)$aSetting['id'];
+
+            if(empty($iIconValue))
+                continue;
 
-            foreach($aTranscoders as $sTranscoder => $sTitle) {
-                $oTranscoder = BxDolTranscoderImage::getObjectInstance($sTranscoder);
-
-                $sImageUrl = $oTranscoder->getFileUrl($iId);
-                if($sImageUrl === false) {
-                    setParam('sys_site_icon', 0);
-                    break;
-                }
-
-                $aFilterParams = $oTranscoder->getFilterParams('Resize');
-				$bFilterWidth = !empty($aFilterParams['w']);
-				$bFilterHeight = !empty($aFilterParams['h']);
-
-                $aTmplVars['bx_repeat:images'][] = array(
-                    'caption' => _t($sTitle),
-                    'url' => $sImageUrl,
-                	'bx_if:show_width' => array(
-                		'condition' => $bFilterWidth,
-                		'content' => array(
-                			'width' => $bFilterWidth ? (int)$aFilterParams['w'] : 0
-                		)
-                	),
-                	'bx_if:show_height' => array(
-                		'condition' => $bFilterHeight,
-                		'content' => array(
-                			'height' => $bFilterHeight ? (int)$aFilterParams['h'] : 0
-                		)
-                	), 
-                );
+            $sIconUrl = '';
+            $aIconParams = [];
+            if(!empty($aIcon['transcoder'])) {
+                $oTranscoder = BxDolTranscoderImage::getObjectInstance($aIcon['transcoder']);
+                $sIconUrl = $oTranscoder->getFileUrl($iIconValue);
+                $aIconParams = $oTranscoder->getFilterParams('Resize');
+            }
+            else {
+                $oStorage = BxDolStorage::getObjectInstance($aIcon['storage']);
+                $sIconUrl = $oStorage->getFileUrlById($iIconValue);
+            }
+
+            if($sIconUrl === false) {
+                setParam($sSetting, 0);
+                continue;
             }
 
-            $sPreview = $oTemplate->parseHtmlByName('dsr_icon_preview.html', $aTmplVars);
+            $bIconWidth = !empty($aIconParams['w']);
+            $bIconHeight = !empty($aIconParams['h']);
+
+            $aTmplVarsPreview['bx_repeat:images'][] = array(
+                'js_object' => $this->getPageJsObject(),
+                'id' => $iIconValue,
+                'name' => $sIcon,
+                'caption' => _t('_adm_dsg_txt_' . $sIcon),
+                'url' => $sIconUrl,
+                'bx_if:show_width' => array(
+                    'condition' => $bIconWidth,
+                    'content' => array(
+                        'width' => $bIconWidth ? (int)$aIconParams['w'] : 0
+                    )
+                ),
+                'bx_if:show_height' => array(
+                    'condition' => $bIconHeight,
+                    'content' => array(
+                        'height' => $bIconHeight ? (int)$aIconParams['h'] : 0
+                    )
+                ), 
+            );
         }
 
         $aForm = array(
@@ -289,15 +300,61 @@ class BxBaseStudioDesigner extends BxDolStudioDesigner
                     'name' => 'page',
                     'value' => $this->sPage
                 ),
-                'preview' => array(
-                    'type' => 'custom',
-                    'name' => 'preview',
-                    'content' => $sPreview
+                'icon' => array(
+                    'type' => 'file',
+                    'name' => 'icon',
+                    'caption' => _t('_adm_dsg_txt_upload_icon'),
+                    'info' => _t('_adm_dsg_txt_upload_icon_inf'),
+                    'attrs' => ['accept' => '.ico']
                 ),
-                'image' => array(
+                'icon_svg' => array(
                     'type' => 'file',
-                    'name' => 'image',
-                    'caption' => _t('_adm_dsg_txt_upload_icon')
+                    'name' => 'icon_svg',
+                    'caption' => _t('_adm_dsg_txt_upload_icon_svg'),
+                    'info' => _t('_adm_dsg_txt_upload_icon_svg_inf'),
+                    'attrs' => ['accept' => '.svg']
+                ),
+                'icon_apple' => array(
+                    'type' => 'files',
+                    'name' => 'icon_apple',
+                    'storage_object' => $this->aIcons['icon_apple']['storage'],
+                    'images_transcoder' => $this->aIcons['icon_apple']['transcoder'],
+                    'uploaders' => ['sys_html5'],
+                    'multiple' => false,
+                    'content_id' => $this->aIcons['icon_apple']['id'],
+                    'ghost_template' => $oTemplate->parseHtmlByName('uploader_fgt_icon.html', array(
+                        'name' => 'icon_apple',
+                    )),
+                    'caption' => _t('_adm_dsg_txt_upload_icon_apple'),
+                    'info' => _t('_adm_dsg_txt_upload_icon_apple_inf'),
+                ),
+                'icon_android' => array(
+                    'type' => 'files',
+                    'name' => 'icon_android',
+                    'storage_object' => $this->aIcons['icon_android']['storage'],
+                    'images_transcoder' => $this->aIcons['icon_android']['transcoder'],
+                    'uploaders' => ['sys_html5'],
+                    'multiple' => false,
+                    'content_id' => $this->aIcons['icon_android']['id'],
+                    'ghost_template' => $oTemplate->parseHtmlByName('uploader_fgt_icon.html', array(
+                        'name' => 'icon_android',
+                    )),
+                    'caption' => _t('_adm_dsg_txt_upload_icon_android'),
+                    'info' => _t('_adm_dsg_txt_upload_icon_android_inf'),
+                ),
+                'icon_android_splash' => array(
+                    'type' => 'files',
+                    'name' => 'icon_android_splash',
+                    'storage_object' => $this->aIcons['icon_android_splash']['storage'],
+                    'images_transcoder' => $this->aIcons['icon_android_splash']['transcoder'],
+                    'uploaders' => ['sys_html5'],
+                    'multiple' => false,
+                    'content_id' => $this->aIcons['icon_android_splash']['id'],
+                    'ghost_template' => $oTemplate->parseHtmlByName('uploader_fgt_icon.html', array(
+                        'name' => 'icon_android_splash',
+                    )),
+                    'caption' => _t('_adm_dsg_txt_upload_icon_android_splash'),
+                    'info' => _t('_adm_dsg_txt_upload_icon_android_splash_inf'),
                 ),
                 'save' => array(
                     'type' => 'submit',
@@ -315,13 +372,17 @@ class BxBaseStudioDesigner extends BxDolStudioDesigner
             exit;
         }
 
-        return $oTemplate->parseHtmlByName('designer.html', array(
-            'content' => $oTemplate->parseHtmlByName('dsr_icon.html', array('icon_iframe_id' => $this->sIconIframeId, 'form' => $oForm->getCode())),
+        return $oTemplate->parseHtmlByName('designer.html', [
+            'content' => $oTemplate->parseHtmlByName('dsr_icon.html', [
+                'preview' => $oTemplate->parseHtmlByName('dsr_icon_preview.html', $aTmplVarsPreview),
+                'icon_iframe_id' => $this->sIconIframeId, 
+                'form' => $oForm->getCode()
+            ]),
             'js_content' => $this->getPageJsCode()
-        ));
+        ]);
     }
 
-	protected function getCover()
+    protected function getCover()
     {
     	$sJsObject = $this->getPageJsObject();
         $oTemplate = BxDolStudioTemplate::getInstance();

+ 11 - 0
studio/template/uploader_fgt_icon.html

@@ -0,0 +1,11 @@
+<div id="bx-uploader-file-{storage_object}-{file_id}" class="bx-uploader-ghost bx-def-margin-sec-top bx-clearfix">
+    <input type="hidden" name="__name__" value="{file_id}" />
+    <div class="bx-ug-icon bx-def-margin-sec-right">
+        <img src="{file_icon}" class="bx-def-round-corners bx-base-text-icon bx-base-text-icon-{file_id}" />
+    </div>
+    <div class="bx-ug-content">
+        <div>
+            <a class="bx-btn bx-btn-small bx-def-margin-sec-right" href="javascript:void(0);" onclick="javascript: {js_instance_name}.deleteGhost('{file_id}');"><bx_text:_sys_txt_form_entry_input_picture_delete /></a>
+        </div>
+    </div>
+</div>

+ 1 - 0
template/scripts/BxBaseFormView.php

@@ -938,6 +938,7 @@ BLAH;
             'bx_if:show_caption' => array(
                 'condition' => !empty($sCaption),
                 'content' => array(
+                    'class_caption' => '',
                     'caption' => $sCaption,
                     'required' => $sRequired,
                     'help' => $sHelp

+ 39 - 38
template/scripts/BxBaseFunctions.php

@@ -578,23 +578,13 @@ class BxBaseFunctions extends BxDolFactory implements iBxDolSingleton
      */
     function getManifests()
     {
-        $sRet = '';
-        $bLogged = isLogged();
-
-        /*
-         * OneSignal manifest must appear before any other <link rel="manifest" ...> in <head>
-         */
-        $sPushAppId = getParam('sys_push_app_id');
-        if($bLogged && !empty($sPushAppId)) {
-            $aUrlRoot = parse_url(BX_DOL_URL_ROOT);
-
-            $sUrl = BX_DOL_URL_PLUGINS_PUBLIC .  'onesignal/manifest.json.php';
-            $sUrl = bx_append_url_params($sUrl, array('bx_name' => $aUrlRoot['host']));
+        $aUrlRoot = parse_url(BX_DOL_URL_ROOT);
 
-            $sRet .= '<link rel="manifest" href="' . $sUrl . '" />';
-        }
+        $sUrl = bx_append_url_params(BX_DOL_URL_ROOT .  'manifest.json.php', [
+            'bx_name' => $aUrlRoot['host']
+        ]);
 
-        return $sRet;
+        return '<link rel="manifest" href="' . $sUrl . '" />';
     }
 
     /**
@@ -603,37 +593,48 @@ class BxBaseFunctions extends BxDolFactory implements iBxDolSingleton
      */
     function getMetaIcons()
     {
-        $iId = (int)getParam('sys_site_icon');
-        $sImageUrlFav = $sImageUrlFcb = $sImageUrlApl = '';
-
-        if(!empty($iId)) {
-
-            // favicon icon
-            $oTranscoder = BxDolTranscoderImage::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_FAVICON);
-            $sImageUrlFav = $oTranscoder->getFileUrl($iId);
-
-            // facebook icon
-            $oTranscoder = BxDolTranscoderImage::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_FACEBOOK);
-            $sImageUrlFcb = $oTranscoder->getFileUrl($iId);
-
-            // apple touch icon
-            $oTranscoder = BxDolTranscoderImage::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_APPLE);
-            $sImageUrlApl = $oTranscoder->getFileUrl($iId);
-        }
-
+        // favicon icon
+        $sImageUrlFav = '';
+        if(($iId = (int)getParam('sys_site_icon')) != 0)
+            $sImageUrlFav = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_FILES)->getFileUrlById($iId);
         if(empty($sImageUrlFav))
             $sImageUrlFav = $this->_oTemplate->getIconUrl('favicon.svg');
 
-        if(empty($sImageUrlFcb))
-            $sImageUrlFcb = $this->_oTemplate->getIconUrl('facebook-icon.png');
-
+        // svg icon
+        $sImageUrlSvg = '';
+        if(($iId = (int)getParam('sys_site_icon_svg')) != 0)
+            $sImageUrlSvg = BxDolStorage::getObjectInstance(BX_DOL_STORAGE_OBJ_IMAGES)->getFileUrlById($iId);
+        if(empty($sImageUrlSvg))
+            $sImageUrlSvg = $this->_oTemplate->getIconUrl('favicon.svg');
+
+        // apple device icon
+        $sImageUrlApl = '';
+        if(($iId = (int)getParam('sys_site_icon_apple')) != 0)
+            $sImageUrlApl = BxDolTranscoderImage::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_APPLE)->getFileUrl($iId);
         if(empty($sImageUrlApl))
             $sImageUrlApl = $this->_oTemplate->getIconUrl('apple-touch-icon.png');
 
+/* 
+ * TODO: 
+ * 1. Remove commented code later if it won't be used.
+ * 2. Remove 'sys_icon_favicon' and 'sys_icon_facebook' transcoders and related transcoder filters.
+ * 
+        // facebook icon
+        $sImageUrlFcb = '';
+        $oTranscoder = BxDolTranscoderImage::getObjectInstance(BX_DOL_TRANSCODER_OBJ_ICON_FACEBOOK);
+        $sImageUrlFcb = $oTranscoder->getFileUrl($iId);
+        if(empty($sImageUrlFcb))
+            $sImageUrlFcb = $this->_oTemplate->getIconUrl('facebook-icon.png');
+*/
+
         $sRet = '';
-        $sRet .= '<link rel="shortcut icon" type="image/x-icon" href="' . $sImageUrlFav . '" />';
+        $sRet .= '<link rel="icon" href="' . $sImageUrlFav . '" sizes="any" />';
+        $sRet .= '<link rel="icon" href="' . $sImageUrlSvg . '" type="image/svg+xml" />';
+        $sRet .= '<link rel="apple-touch-icon" href="' . $sImageUrlApl . '" />';
+
+/*
         $sRet .= '<link rel="image_src" sizes="100x100" href="' . $sImageUrlFcb . '" />';
-        $sRet .= '<link rel="apple-touch-icon" sizes="152x152" href="' . $sImageUrlApl . '" />';
+*/
 
         return $sRet;
     }

Некоторые файлы не были показаны из-за большого количества измененных файлов