Parcourir la source

Merge branch 'master' of https://github.com/unaio/una

roman.lesnikov@gmail.com il y a 3 ans
Parent
commit
b091e56d3f
33 fichiers modifiés avec 519 ajouts et 482 suppressions
  1. 1 0
      .gitignore
  2. 1 1
      inc/classes/BxDolForm.php
  3. 32 15
      inc/classes/BxDolPage.php
  4. 22 0
      inc/classes/BxDolSearch.php
  5. 2 0
      inc/classes/BxDolStorageS3.php
  6. 2 0
      inc/classes/BxDolStorageS3v4.php
  7. 1 7
      inc/classes/BxDolTemplate.php
  8. 95 48
      inc/js/classes/BxDolCreatePost.js
  9. 3 1
      install/sql/system.sql
  10. 7 3
      modules/base/general/classes/BxBaseModGeneralModule.php
  11. 45 0
      modules/base/general/classes/BxBaseModGeneralSearchResult.php
  12. 1 1
      modules/boonex/artificer/data/template/system/css/common.css
  13. 28 0
      modules/boonex/artificer/data/template/system/css/general.css
  14. 0 21
      modules/boonex/artificer/data/template/system/menu_interactive_vertical.html
  15. 5 15
      modules/boonex/artificer/data/template/system/scripts/BxTemplConfig.php
  16. 0 324
      modules/boonex/artificer/js/jquery.menu-aim.js
  17. 1 1
      modules/boonex/contact/classes/BxContactModule.php
  18. 1 0
      modules/boonex/english/data/langs/system/en.xml
  19. 17 2
      modules/boonex/payment/classes/BxPaymentProviderStripeBasic.php
  20. 69 4
      modules/boonex/payment/classes/BxPaymentProviderStripeV3.php
  21. 1 0
      modules/boonex/russian/data/langs/system/ru.xml
  22. 0 4
      modules/boonex/timeline/install/sql/disable.sql
  23. 0 6
      modules/boonex/timeline/install/sql/enable.sql
  24. 2 1
      plugins_public/tailwind/css/tailwind.css
  25. 0 0
      plugins_public/tailwind/css/tailwind.min.css
  26. 8 0
      template/create_post_form.html
  27. 25 0
      template/css/general.css
  28. 12 8
      template/menu_interactive_vertical.html
  29. 2 2
      template/menu_main_in_panel.html
  30. 37 0
      template/scripts/BxBaseCmts.php
  31. 9 3
      template/scripts/BxBaseFormView.php
  32. 39 5
      template/scripts/BxBaseServicePages.php
  33. 51 10
      template/scripts/BxBaseServices.php

+ 1 - 0
.gitignore

@@ -28,6 +28,7 @@ composer.phar
 tests/vendor
 tests/composer.phar
 samples/img/*
+modules/boonex/acl_api
 modules/boonex/ether
 modules/boonex/market_api
 modules/boonex/media

+ 1 - 1
inc/classes/BxDolForm.php

@@ -1629,7 +1629,7 @@ class BxDolFormCheckerHelper
     }
     static public function checkDateRange ($s, $iMin, $iMax, $bRequired = false)
     {
-        if (!$bRequired && (empty($s) || '0000-00-00' == $s))
+        if (!$bRequired && (empty($s) || '0000-00-00' == $s || '0000-00-00Z' == $s))
             return true;   
         if (self::checkPreg ($s, '#^\d+\-\d+\-\d+$#')){
             $dDate = new DateTime($s);

+ 32 - 15
inc/classes/BxDolPage.php

@@ -343,12 +343,13 @@ class BxDolPage extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
     }
 
 	/**
-     * Process SEO links. It takes request part from SEO link and process it 
-     * to make it work as regular page link
+     * Get page object by SEO link. It takes request part from SEO link and returns page object
      * @param $sRequest request URI with SEO link
-     * @return true - if page was found and processed correctly, false - if page wasn't found
+     * @return false - if page wasn't found,  
+     *         page object - on success,  
+     *         string URL - if permanent redirect is needed
      */
-    static public function processSeoLink ($sRequest)
+    static public function getPageBySeoLink ($sRequest)
     {
         if (!$sRequest || '/' === $sRequest || !getParam('permalinks_seo_links'))
             return false;
@@ -357,8 +358,7 @@ class BxDolPage extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
         if ('/' === $sRequest[-1] || $sRequest != mb_strtolower($sRequest)) {
             unset($_GET['_q']);
             $sUrl = BX_DOL_URL_ROOT . bx_append_url_params(mb_strtolower(trim($sRequest, '/')), $_GET);
-            header('Location:' . $sUrl, true, 301);
-            exit;            
+            return $sUrl;    
         }
 
         // parse URL
@@ -372,8 +372,7 @@ class BxDolPage extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
             unset($_GET['_q']);
             $a[0] = $aSeoUriRewrites[$a[0]];
             $sUrl = BX_DOL_URL_ROOT . bx_append_url_params(mb_strtolower(implode('/', $a)), $_GET);
-            header('Location:' . $sUrl, true, 301);
-            exit;            
+            return $sUrl;
         }
 
         // check page URI rewrite
@@ -403,18 +402,36 @@ class BxDolPage extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
                 $sLink = substr($sLink, strlen(BX_DOL_URL_ROOT));
 
             $s = BxDolPage::multisiteLinkCheck ('', $a[0], $aPage['module'], $_GET);
-            if (false !== $s) {
-                header('Location:' . $s . $sLink, true, 301);
-                exit;
-            }
+            if (false !== $s)
+                return $s . $sLink;
         }
 
         // display page
         $_REQUEST['i'] = $_GET['i'] = $a[0];
         $oPage = BxDolPage::getObjectInstanceByURI($a[0], false, true);
-        $oPage->displayPage();
-
-        return true;
+        return $oPage;
+    }
+    
+	/**
+     * Process SEO links. It takes request part from SEO link and process it 
+     * to make it work as regular page link
+     * @param $sRequest request URI with SEO link
+     * @return true - if page was found and processed correctly, false - if page wasn't found
+     */
+    static public function processSeoLink ($sRequest)
+    {
+        $mixed = self::getPageBySeoLink($sRequest);
+        if (($sUrl = $mixed) && is_string($sUrl)) {
+            header('Location:' . $sUrl, true, 301);
+            exit;
+        }
+        elseif (($oPage = $mixed) && is_object($oPage)) {
+            $oPage->displayPage();
+            return true;
+        }
+        else {
+            return false;
+        }
     }
 
 	/**

+ 22 - 0
inc/classes/BxDolSearch.php

@@ -479,6 +479,10 @@ class BxDolSearchResult implements iBxDolReplaceable
      */
     function processing ()
     {
+        if (defined('BX_API')) {
+            return $this->processingAPI ();
+        }
+
         $sCode = $this->displayResultBlock();
         if ($this->aCurrent['paginate']['num'] > 0) {
             $sPaginate = $this->showPagination();
@@ -489,6 +493,24 @@ class BxDolSearchResult implements iBxDolReplaceable
         return $sCode;
     }
 
+    function processingAPI () 
+    {
+        return [
+            'unit' => 'general',
+            'data' => $this->decodeData($this->getSearchData()),
+            'paginate' => [
+                'num' => $this->aCurrent['paginate']['num'],
+                'per_page' => $this->aCurrent['paginate']['perPage'],
+                'start' => $this->aCurrent['paginate']['start'],
+            ]
+        ];
+    }
+
+    function decodeData ($a)
+    {
+        return $a;
+    }
+
     /**
      * Get html output of search result
      * @return html code

+ 2 - 0
inc/classes/BxDolStorageS3.php

@@ -49,6 +49,8 @@ class BxDolStorageS3 extends BxDolStorage
             $this->_bSSL, 
             $this->_sEndpoint ? $this->_sEndpoint : 's3.amazonaws.com'
         );
+        if ($this->_bSSL && getParam('sys_curl_ssl_allow_untrusted'))
+             $this->_s3->setSSL($this->_bSSL, false);
     }
 
     /**

+ 2 - 0
inc/classes/BxDolStorageS3v4.php

@@ -24,6 +24,8 @@ class BxDolStorageS3v4 extends BxDolStorageS3
             $this->_sEndpoint ? $this->_sEndpoint : 's3.amazonaws.com',
             getParam('sys_storage_s3_region')
         );
+        if ($this->_bSSL && getParam('sys_curl_ssl_allow_untrusted'))
+             $this->_s3->setSSL($this->_bSSL, false);
     }
 }
 

+ 1 - 7
inc/classes/BxDolTemplate.php

@@ -1630,11 +1630,6 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
     function parseHtmlByName($sName, $aVariables, $mixedKeyWrapperHtml = null, $sCheckIn = BX_DOL_TEMPLATE_CHECK_IN_BOTH)
     {
         if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->beginTemplate($sName, $sRand = time().rand());
-        
-        if (defined('BX_API') && constant('BX_API')) {
-            if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->endTemplate($sName, $sRand, $sContent, false);
-            return $aVariables;
-        }
 
         if (($sContent = $this->getCached($sName, $aVariables, $mixedKeyWrapperHtml, $sCheckIn)) !== false) {
             if (isset($GLOBALS['bx_profiler'])) $GLOBALS['bx_profiler']->endTemplate($sName, $sRand, $sContent, true);
@@ -2939,8 +2934,7 @@ class BxDolTemplate extends BxDolFactory implements iBxDolSingleton
         ));
 
         //--- Parse Predefined Keys ---//
-        if (!defined('BX_API') || !constant('BX_API'))
-            $sContent = preg_replace($aKeys, $aValues, $sContent);
+        $sContent = preg_replace($aKeys, $aValues, $sContent);
 
         //--- Parse System Keys ---//
         try {

+ 95 - 48
inc/js/classes/BxDolCreatePost.js

@@ -11,17 +11,50 @@ function BxDolCreatePost(oOptions) {
     this._sRootUrl = oOptions.sRootUrl == undefined ? sUrlRoot : oOptions.sRootUrl;
     this._sDefault = oOptions.sDefault == undefined ? '' : oOptions.sDefault;
     this._iContextId = oOptions.iContextId == undefined ? 0 : oOptions.iContextId;
+    this._oPreloadingList = oOptions.oPreloadingList == undefined ? [] : oOptions.oPreloadingList;
     this._oCustom = oOptions.oCustom == undefined ? {} : oOptions.oCustom;
 
     this._sAnimationEffect = oOptions.sAnimationEffect == undefined ? 'fade' : oOptions.sAnimationEffect;
     this._iAnimationSpeed = oOptions.iAnimationSpeed == undefined ? 'slow' : oOptions.iAnimationSpeed;
 
     this._aHtmlIds = oOptions.aHtmlIds == undefined ? {} : oOptions.aHtmlIds;
+
+    var $this = this;
+    $(document).ready(function() {
+        $this.init();
+    });
 }
 
+BxDolCreatePost.prototype.init = function () {
+    var oDate = new Date();
+
+    for(var sKey in this._oPreloadingList) {
+        var sForm = '.sys-cpf-form.sys-cpf-' + sKey;
+        if($(sForm).length != 0)
+            continue;
+
+        $.get(
+            this._sRootUrl + 'modules/?r=' + this._oPreloadingList[sKey] + '/get_create_post_form/', {
+                ajax_mode: true,
+                dynamic_mode: true,
+                absolute_action_url: true,
+                context_id: this._iContextId,
+                custom: this._oCustom,
+                _t:oDate.getTime()
+            },
+            function(oData) {
+                if(!oData || !oData.content) 
+                    return;
+
+                $('.sys-cpf-close').before($('<div class="sys-cpf-form sys-cpf-' + oData.module + '">' + oData.content + '</div>').hide());
+            }, 
+            'json'
+        );
+    }
+};
+
 BxDolCreatePost.prototype.getForm = function (sModuleName, sModuleUri, oElement) {
     var $this = this;
-    var oDate = new Date();
 
     var oTab = $(oElement);
     var sTabActive = 'bx-menu-tab-active';
@@ -29,53 +62,50 @@ BxDolCreatePost.prototype.getForm = function (sModuleName, sModuleUri, oElement)
     oTab.parents('li:first').addClass(sTabActive);
     oTab.parents('.bx-popup-applied:visible').dolPopupHide();
 
-    if($('.sys-cpf-form.sys-cpf-' + sModuleName + ' form').length > 0) {
-        $('.sys-cpf-form:visible').bx_anim('hide', this._sAnimationEffect, this._iAnimationSpeed, function() {
-            $('.sys-cpf-form.sys-cpf-' + sModuleName).show().siblings('.sys-cpf-close:hidden').show();
-        });
-
-        return false;
-    }
-
-    this._loadingInBlock(oElement, true);
-
-    $.get(
-        this._sRootUrl + 'modules/?r=' + sModuleUri + '/get_create_post_form/', {
-            ajax_mode: true,
-            dynamic_mode: true,
-            absolute_action_url: true,
-            context_id: this._iContextId,
-            custom: this._oCustom,
-            _t:oDate.getTime()
-        },
-        function(oData) {
-            $this._loadingInBlock(oElement, false);
-
-            if(!oData || !oData.content) 
-                return;
-
-            $('.sys-cpf-form:visible').bx_anim('hide', this._sAnimationEffect, this._iAnimationSpeed, function() {
-                $this._loadingInBlock(oElement, true);
-
-                $('.sys-cpf-close:visible').hide();
-
-                var sForm = '.sys-cpf-form.sys-cpf-' + oData.module;
-                if($(sForm).length == 0)
-                    $('.sys-cpf-close').before($('<div class="sys-cpf-form sys-cpf-' + oData.module + '"></div>').hide());
-
-                $(sForm).html(oData.content).show().siblings('.sys-cpf-close:hidden').show();
-
-                $this._loadingInBlock(oElement, false);
-            });
-        }, 
-        'json'
-    );
+    $('.sys-cpf-form:visible').hide({
+        duration: 0,
+        complete: function() {
+            $('.sys-cpf-close:visible').hide();
+
+            if(!$('.sys-cpf-form.sys-cpf-' + sModuleName + ' form').length) {
+                var oDate = new Date();
+
+                $this._placeholderInBlock(oElement, true);
+
+                $.get(
+                    $this._sRootUrl + 'modules/?r=' + sModuleUri + '/get_create_post_form/', {
+                        ajax_mode: true,
+                        dynamic_mode: true,
+                        absolute_action_url: true,
+                        context_id: $this._iContextId,
+                        custom: $this._oCustom,
+                        _t: oDate.getTime()
+                    },
+                    function(oData) {
+                        $this._placeholderInBlock(oElement, false);
+
+                        if(!oData || !oData.content) 
+                            return;
+
+                        var sForm = '.sys-cpf-form.sys-cpf-' + oData.module;
+                        if($(sForm).length == 0)
+                            $('.sys-cpf-close').before($('<div class="sys-cpf-form sys-cpf-' + oData.module + '"></div>').hide());
+
+                        $(sForm).html(oData.content).show().siblings('.sys-cpf-close:hidden').show();
+                    }, 
+                    'json'
+                );
+            }
+            else 
+                $('.sys-cpf-form.sys-cpf-' + sModuleName).show().siblings('.sys-cpf-close:hidden').show();
+        }
+    });
 
     return false;
 };
 
 BxDolCreatePost.prototype.hideForm = function(oElement) {
-    $(oElement).parents('.sys-cpf-close:first').bx_anim('hide', this._sAnimationEffect, this._iAnimationSpeed);
+    $(oElement).parents('.sys-cpf-close:first').hide();
 
     var sTabActive = 'bx-menu-tab-active';
     var sTabDefault = 'bx-menu-item-' + this._sDefault;
@@ -86,11 +116,14 @@ BxDolCreatePost.prototype.hideForm = function(oElement) {
     if(bMiChange)
         oMiActive.removeClass(sTabActive);
 
-    $('.sys-cpf-form:visible').bx_anim('hide', this._sAnimationEffect, this._iAnimationSpeed, function() {
-        $('.sys-cpf-form.sys-cpf-default').show();
+    $('.sys-cpf-form:visible').hide({
+        duration: 0,
+        complete: function() {
+            $('.sys-cpf-form.sys-cpf-default').show();
 
-        if(bMiChange)
-            $('.' + sTabDefault).addClass(sTabActive);
+            if(bMiChange)
+                $('.' + sTabDefault).addClass(sTabActive);
+        }
     });
 };
 
@@ -100,8 +133,22 @@ BxDolCreatePost.prototype._loading = function(e, bShow) {
 };
 
 BxDolCreatePost.prototype._loadingInBlock = function(e, bShow) {
-    var oParent = $(e).length ? $(e).parents('.bx-db-content:first') : $('body'); 
+    var oParent = $(e).length ? $(e).parents('.bx-db-container:first') : $('body'); 
     bx_loading(oParent, bShow);
 };
 
+BxDolCreatePost.prototype._placeholderInBlock = function(e, bShow) {
+    var oPlaceholder = null;
+    if($(e).length) 
+        oPlaceholder = $(e).parents('.bx-db-container:first').find('.sys-cpf-form.sys-cpf-loading'); 
+
+    if(!oPlaceholder)
+        return _loadingInBlock(e, bShow);
+
+    if(bShow)
+        oPlaceholder.show();
+    else
+        oPlaceholder.hide();
+};
+
 /** @} */

+ 3 - 1
install/sql/system.sql

@@ -546,7 +546,9 @@ INSERT INTO `sys_options`(`category_id`, `name`, `caption`, `value`, `type`, `ex
 
 (@iCategoryId, 'sys_vote_reactions_quick_mode', '_adm_stg_cpt_option_sys_vote_reactions_quick_mode', 'on', 'checkbox', '', '', '', 60),
 
-(@iCategoryId, 'sys_cmts_enable_auto_approve', '_adm_stg_cpt_option_sys_cmts_enable_auto_approve', 'on', 'checkbox', '', '', '', 70);
+(@iCategoryId, 'sys_cmts_enable_auto_approve', '_adm_stg_cpt_option_sys_cmts_enable_auto_approve', 'on', 'checkbox', '', '', '', 70),
+
+(@iCategoryId, 'sys_create_post_form_preloading_list', '_adm_stg_cpt_option_sys_create_post_form_preloading_list', '', 'list', 'a:3:{s:6:"module";s:6:"system";s:6:"method";s:44:"get_options_create_post_form_preloading_list";s:5:"class";s:13:"TemplServices";}', '', '', 80);
 
 --
 -- CATEGORY: Storage

+ 7 - 3
modules/base/general/classes/BxBaseModGeneralModule.php

@@ -1562,7 +1562,7 @@ class BxBaseModGeneralModule extends BxDolModule
         if (!isset($CNF['OBJECT_REPORTS']) || !isset($CNF['OBJECT_NOTES']))
             return false;
 
-        if(!(BxDolAcl::getInstance()->isMemberLevelInSet(192) || (isset($CNF['FIELD_AUTHOR']) && bx_get_logged_profile_id() == $aContentInfo[$CNF['FIELD_AUTHOR']])))
+        if(!$aContentInfo || !(BxDolAcl::getInstance()->isMemberLevelInSet(192) || (isset($CNF['FIELD_AUTHOR']) && bx_get_logged_profile_id() == $aContentInfo[$CNF['FIELD_AUTHOR']])))
             return false;
 
         $oReport = BxDolReport::getObjectInstance($CNF['OBJECT_REPORTS'], $iContentId, true);
@@ -2761,7 +2761,7 @@ class BxBaseModGeneralModule extends BxDolModule
     public function _serviceBrowse ($sMode, $aParams = false, $iDesignBox = BX_DB_PADDING_DEF, $bDisplayEmptyMsg = false, $bAjaxPaginate = true, $sClassSearchResult = 'SearchResult')
     {
         if (CHECK_ACTION_RESULT_ALLOWED !== ($sMsg = $this->checkAllowedBrowse()))
-            return MsgBox($sMsg);
+            return defined('BX_API') ? [['id' => 1, 'type' => 'msg', 'data' => $sMsg]] : MsgBox($sMsg);
 
         bx_import($sClassSearchResult, $this->_aModule);
         $sClass = $this->_aModule['class_prefix'] . $sClassSearchResult;
@@ -2778,7 +2778,7 @@ class BxBaseModGeneralModule extends BxDolModule
             return '';
 
         if ($s = $o->processing())
-            return $s;
+            return defined('BX_API') ? [['id' => 1, 'type' => 'browse', 'data' => $s]] : $s;
         else
             return '';
     }
@@ -3446,6 +3446,10 @@ class BxBaseModGeneralModule extends BxDolModule
         if (!$oCmts || !$oCmts->isEnabled())
             return false;
 
+        
+        if (defined('BX_API'))
+            return [['id' => 1, 'type' => 'browse', 'data' => $oCmts->getCommentsBlockAPI(array(), array('in_designbox' => false, 'show_empty' => false))]];
+
         return $oCmts->getCommentsBlock(array(), array('in_designbox' => false, 'show_empty' => false));
     }
 

+ 45 - 0
modules/base/general/classes/BxBaseModGeneralSearchResult.php

@@ -29,6 +29,11 @@ class BxBaseModGeneralSearchResult extends BxTemplSearchResult
         return BxDolModule::getInstance($this->aCurrent['module_name']);
     }
 
+    function getContentInfoObject()
+    {
+        return BxDolContentInfo::getObjectInstance($this->aCurrent['name']);
+    }
+
     function getRssUnitLink (&$a)
     {
         $CNF = &$this->oModule->_oConfig->CNF;
@@ -205,6 +210,46 @@ class BxBaseModGeneralSearchResult extends BxTemplSearchResult
 
         return parent::displayResultBlock();
     }
+
+    function decodeData ($a)
+    {
+        $oContentInfo = $this->getContentInfoObject();
+
+        foreach ($a as $i => $r) {
+            if (isset($r['author']))
+                $a[$i]['author_data'] = $this->decodeDataAuthor($oContentInfo, $r);
+
+            $a[$i]['url'] = $this->decodeDataUrl($oContentInfo, $r);
+            $a[$i]['url_thumb'] = $oContentInfo->getContentThumb($r['id']);
+            $a[$i]['summary_plain'] = $this->decodeDataSummaryPlain($oContentInfo, $r);
+        }
+
+        return $a;
+    }
+
+    function decodeDataUrl ($oContentInfo, $r)
+    {
+        return bx_ltrim_str($oContentInfo->getContentLink($r['id']), BX_DOL_URL_ROOT);
+    }
+
+    function decodeDataAuthor ($oContentInfo, $r) 
+    {
+        $CNF = $this->oModule->_oConfig->CNF;
+        $oProfile = BxDolProfile::getInstanceMagic($r[$CNF['FIELD_AUTHOR']]);
+        return [
+            'display_name' => $oProfile->getDisplayName(),
+            'url' => $oProfile->getUrl(),
+            'url_avatar' => $oProfile->getAvatar(),
+        ];
+    }
+
+    function decodeDataSummaryPlain ($oContentInfo, $r)
+    {
+        $CNF = $this->oModule->_oConfig->CNF;
+        $sSummary = $this->oModule->_oTemplate->getText($r);
+        return isset($CNF['PARAM_CHARS_SUMMARY_PLAIN']) && $CNF['PARAM_CHARS_SUMMARY_PLAIN'] ? BxTemplFunctions::getInstance()->getStringWithLimitedLength(strip_tags($sSummary), (int)getParam($CNF['PARAM_CHARS_SUMMARY_PLAIN'])) : '';
+    }
+
 }
 
 /** @} */

+ 1 - 1
modules/boonex/artificer/data/template/system/css/common.css

@@ -89,7 +89,7 @@ import url(../../../../../../../template/css/common.css);
     text-decoration:none;
 }
 
-.bx-db-menu-tab-btn ul > li {
+.bx-db-menu-tab-btn > ul > li {
     display: flex;
 }
 

+ 28 - 0
modules/boonex/artificer/data/template/system/css/general.css

@@ -107,4 +107,32 @@ import url(../../../../../../../template/css/general.css);
 /*--- Page Type: Application ---*/
 .bx-menu-panel-item .bx-profile-avatar .bx-profile-avatar-cnt {
     min-height: 2.5rem;
+}
+
+/*--- Create Post Form ---*/
+.sys-cpf-form:not(.sys-cpf-loading) {
+    -webkit-animation: sys-cpf-scale-in 0.4s cubic-bezier(0.37, 0.82, 0.2, 1);
+    animation: sys-cpf-scale-in 0.4s cubic-bezier(0.37, 0.82, 0.2, 1);
+}
+
+@keyframes sys-cpf-scale-in {
+    0% {
+        opacity: 0;
+        transform: scale(0.9);
+    }
+    100% {
+        opacity: 1;
+        transform: scale(1.0);
+    }
+}
+
+@-webkit-keyframes sys-cpf-scale-in {
+    0% {
+        opacity: 0;
+        transform: scale(0.9);
+    }
+    100% {
+        opacity: 1;
+        transform: scale(1.0);
+    }
 }

+ 0 - 21
modules/boonex/artificer/data/template/system/menu_interactive_vertical.html

@@ -1,21 +0,0 @@
-<ul id="bx-menu-inter-__id__" class="bx-menu-inter-vert bx-menu-object-__object__">
-    <bx_repeat:menu_items>
-        <li id="__name__-act" class="bx-menu-inter-act __class_wrp_act__ group flex items-center bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-white text-sm font-normal shadow-sm">
-            <div class="__class__ px-4 py-2" title="__title_attr__">__title__</div>
-        </li>
-        <li id="__name__-pas" class="bx-menu-inter-pas __class_wrp_pas__ group flex items-center text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:text-gray-100 dark:hover:bg-gray-700 hover:text-gray-900 text-sm font-normal">
-            <a id="__name__" class="__class__" href="__link__" onclick="__onclick__" title="__title_attr__"__attrs__>
-                <div class="px-4 py-2">__title__</div>
-            </a>
-        </li>
-    </bx_repeat:menu_items>
-</ul>
-<script language="javascript">
-    $(document).ready(function() {
-    	$('#bx-menu-inter-__id__').find('a').bind('click', function() {
-            var sId = $(this).attr('id');
-            var sClassHidden = 'bx-menu-inter-hidden';
-            $(this).parent().siblings('.bx-menu-inter-act:not(.' + sClassHidden + ')').addClass(sClassHidden).siblings('.bx-menu-inter-pas.' + sClassHidden).removeClass(sClassHidden).siblings('#' + sId + '-pas:not(.' + sClassHidden + ')').addClass(sClassHidden).siblings('#' + sId + '-act.' + sClassHidden).removeClass(sClassHidden);
-    	});
-    });
-</script>

+ 5 - 15
modules/boonex/artificer/data/template/system/scripts/BxTemplConfig.php

@@ -19,7 +19,7 @@ class BxTemplConfig extends BxBaseConfig
 
         parent::__construct();
 
-        $this->_aConfig['aLessConfig'] = array_merge($this->_aConfig['aLessConfig'], array(
+        $this->_aConfig['aLessConfig'] = array_merge($this->_aConfig['aLessConfig'], [
             'bx-font-family' => 'Helvetica, Arial, sans-serif',
 
             'bx-color-page' => '#edf1f7',
@@ -31,26 +31,16 @@ class BxTemplConfig extends BxBaseConfig
             'bx-color-active' => 'rgba(202, 242, 252, 0.4)',
 
             'bx-border-color' => '#00a0ce',
-        ));
-        
+        ]);
+
         if($this->_bModule) {
             $this->setPageWidth('bx_artificer_page_width');
-
-            $oModule = BxDolModule::getInstance($this->_sModule);
-            if($oModule)
-                $oModule->_oTemplate->addJs(array(
-                    'jquery.menu-aim.js',
-                ));
         }
 
-        BxDolTemplate::getInstance()->addJs(array(
-            'modernizr.min.js'
-        ));
-
-        BxDolTemplate::getInstance()->addCss(array(
+        BxDolTemplate::getInstance()->addCss([
             'https://rsms.me/inter/inter.css',
             'menu-sidebar.css',
-        ));
+        ]);
     }
 }
 

+ 0 - 324
modules/boonex/artificer/js/jquery.menu-aim.js

@@ -1,324 +0,0 @@
-/**
- * menu-aim is a jQuery plugin for dropdown menus that can differentiate
- * between a user trying hover over a dropdown item vs trying to navigate into
- * a submenu's contents.
- *
- * menu-aim assumes that you have are using a menu with submenus that expand
- * to the menu's right. It will fire events when the user's mouse enters a new
- * dropdown item *and* when that item is being intentionally hovered over.
- *
- * __________________________
- * | Monkeys  >|   Gorilla  |
- * | Gorillas >|   Content  |
- * | Chimps   >|   Here     |
- * |___________|____________|
- *
- * In the above example, "Gorillas" is selected and its submenu content is
- * being shown on the right. Imagine that the user's cursor is hovering over
- * "Gorillas." When they move their mouse into the "Gorilla Content" area, they
- * may briefly hover over "Chimps." This shouldn't close the "Gorilla Content"
- * area.
- *
- * This problem is normally solved using timeouts and delays. menu-aim tries to
- * solve this by detecting the direction of the user's mouse movement. This can
- * make for quicker transitions when navigating up and down the menu. The
- * experience is hopefully similar to amazon.com/'s "Shop by Department"
- * dropdown.
- *
- * Use like so:
- *
- *      $("#menu").menuAim({
- *          activate: $.noop,  // fired on row activation
- *          deactivate: $.noop  // fired on row deactivation
- *      });
- *
- *  ...to receive events when a menu's row has been purposefully (de)activated.
- *
- * The following options can be passed to menuAim. All functions execute with
- * the relevant row's HTML element as the execution context ('this'):
- *
- *      .menuAim({
- *          // Function to call when a row is purposefully activated. Use this
- *          // to show a submenu's content for the activated row.
- *          activate: function() {},
- *
- *          // Function to call when a row is deactivated.
- *          deactivate: function() {},
- *
- *          // Function to call when mouse enters a menu row. Entering a row
- *          // does not mean the row has been activated, as the user may be
- *          // mousing over to a submenu.
- *          enter: function() {},
- *
- *          // Function to call when mouse exits a menu row.
- *          exit: function() {},
- *
- *          // Selector for identifying which elements in the menu are rows
- *          // that can trigger the above events. Defaults to "> li".
- *          rowSelector: "> li",
- *
- *          // You may have some menu rows that aren't submenus and therefore
- *          // shouldn't ever need to "activate." If so, filter submenu rows w/
- *          // this selector. Defaults to "*" (all elements).
- *          submenuSelector: "*",
- *
- *          // Direction the submenu opens relative to the main menu. Can be
- *          // left, right, above, or below. Defaults to "right".
- *          submenuDirection: "right"
- *      });
- *
- * https://github.com/kamens/jQuery-menu-aim
- * MIT License
-*/
-(function($) {
-
-    $.fn.menuAim = function(opts) {
-        // Initialize menu-aim for all elements in jQuery collection
-        this.each(function() {
-            init.call(this, opts);
-        });
-
-        return this;
-    };
-
-    function init(opts) {
-        var $menu = $(this),
-            activeRow = null,
-            mouseLocs = [],
-            lastDelayLoc = null,
-            timeoutId = null,
-            options = $.extend({
-                rowSelector: "> li",
-                submenuSelector: "*",
-                submenuDirection: "right",
-                tolerance: 75,  // bigger = more forgivey when entering submenu
-                enter: $.noop,
-                exit: $.noop,
-                activate: $.noop,
-                deactivate: $.noop,
-                exitMenu: $.noop
-            }, opts);
-
-        var MOUSE_LOCS_TRACKED = 3,  // number of past mouse locations to track
-            DELAY = 300;  // ms delay when user appears to be entering submenu
-
-        /**
-         * Keep track of the last few locations of the mouse.
-         */
-        var mousemoveDocument = function(e) {
-                mouseLocs.push({x: e.pageX, y: e.pageY});
-
-                if (mouseLocs.length > MOUSE_LOCS_TRACKED) {
-                    mouseLocs.shift();
-                }
-            };
-
-        /**
-         * Cancel possible row activations when leaving the menu entirely
-         */
-        var mouseleaveMenu = function() {
-                if (timeoutId) {
-                    clearTimeout(timeoutId);
-                }
-
-                // If exitMenu is supplied and returns true, deactivate the
-                // currently active row on menu exit.
-                if (options.exitMenu(this)) {
-                    if (activeRow) {
-                        options.deactivate(activeRow);
-                    }
-
-                    activeRow = null;
-                }
-            };
-
-        /**
-         * Trigger a possible row activation whenever entering a new row.
-         */
-        var mouseenterRow = function() {
-                if (timeoutId) {
-                    // Cancel any previous activation delays
-                    clearTimeout(timeoutId);
-                }
-
-                options.enter(this);
-                possiblyActivate(this);
-            },
-            mouseleaveRow = function() {
-                options.exit(this);
-            };
-
-        /*
-         * Immediately activate a row if the user clicks on it.
-         */
-        var clickRow = function() {
-                activate(this);
-            };
-
-        /**
-         * Activate a menu row.
-         */
-        var activate = function(row) {
-                if (row == activeRow) {
-                    return;
-                }
-
-                if (activeRow) {
-                    options.deactivate(activeRow);
-                }
-
-                options.activate(row);
-                activeRow = row;
-            };
-
-        /**
-         * Possibly activate a menu row. If mouse movement indicates that we
-         * shouldn't activate yet because user may be trying to enter
-         * a submenu's content, then delay and check again later.
-         */
-        var possiblyActivate = function(row) {
-                var delay = activationDelay();
-
-                if (delay) {
-                    timeoutId = setTimeout(function() {
-                        possiblyActivate(row);
-                    }, delay);
-                } else {
-                    activate(row);
-                }
-            };
-
-        /**
-         * Return the amount of time that should be used as a delay before the
-         * currently hovered row is activated.
-         *
-         * Returns 0 if the activation should happen immediately. Otherwise,
-         * returns the number of milliseconds that should be delayed before
-         * checking again to see if the row should be activated.
-         */
-        var activationDelay = function() {
-                if (!activeRow || !$(activeRow).is(options.submenuSelector)) {
-                    // If there is no other submenu row already active, then
-                    // go ahead and activate immediately.
-                    return 0;
-                }
-
-                var offset = $menu.offset(),
-                    upperLeft = {
-                        x: offset.left,
-                        y: offset.top - options.tolerance
-                    },
-                    upperRight = {
-                        x: offset.left + $menu.outerWidth(),
-                        y: upperLeft.y
-                    },
-                    lowerLeft = {
-                        x: offset.left,
-                        y: offset.top + $menu.outerHeight() + options.tolerance
-                    },
-                    lowerRight = {
-                        x: offset.left + $menu.outerWidth(),
-                        y: lowerLeft.y
-                    },
-                    loc = mouseLocs[mouseLocs.length - 1],
-                    prevLoc = mouseLocs[0];
-
-                if (!loc) {
-                    return 0;
-                }
-
-                if (!prevLoc) {
-                    prevLoc = loc;
-                }
-
-                if (prevLoc.x < offset.left || prevLoc.x > lowerRight.x ||
-                    prevLoc.y < offset.top || prevLoc.y > lowerRight.y) {
-                    // If the previous mouse location was outside of the entire
-                    // menu's bounds, immediately activate.
-                    return 0;
-                }
-
-                if (lastDelayLoc &&
-                        loc.x == lastDelayLoc.x && loc.y == lastDelayLoc.y) {
-                    // If the mouse hasn't moved since the last time we checked
-                    // for activation status, immediately activate.
-                    return 0;
-                }
-
-                // Detect if the user is moving towards the currently activated
-                // submenu.
-                //
-                // If the mouse is heading relatively clearly towards
-                // the submenu's content, we should wait and give the user more
-                // time before activating a new row. If the mouse is heading
-                // elsewhere, we can immediately activate a new row.
-                //
-                // We detect this by calculating the slope formed between the
-                // current mouse location and the upper/lower right points of
-                // the menu. We do the same for the previous mouse location.
-                // If the current mouse location's slopes are
-                // increasing/decreasing appropriately compared to the
-                // previous's, we know the user is moving toward the submenu.
-                //
-                // Note that since the y-axis increases as the cursor moves
-                // down the screen, we are looking for the slope between the
-                // cursor and the upper right corner to decrease over time, not
-                // increase (somewhat counterintuitively).
-                function slope(a, b) {
-                    return (b.y - a.y) / (b.x - a.x);
-                };
-
-                var decreasingCorner = upperRight,
-                    increasingCorner = lowerRight;
-
-                // Our expectations for decreasing or increasing slope values
-                // depends on which direction the submenu opens relative to the
-                // main menu. By default, if the menu opens on the right, we
-                // expect the slope between the cursor and the upper right
-                // corner to decrease over time, as explained above. If the
-                // submenu opens in a different direction, we change our slope
-                // expectations.
-                if (options.submenuDirection == "left") {
-                    decreasingCorner = lowerLeft;
-                    increasingCorner = upperLeft;
-                } else if (options.submenuDirection == "below") {
-                    decreasingCorner = lowerRight;
-                    increasingCorner = lowerLeft;
-                } else if (options.submenuDirection == "above") {
-                    decreasingCorner = upperLeft;
-                    increasingCorner = upperRight;
-                }
-
-                var decreasingSlope = slope(loc, decreasingCorner),
-                    increasingSlope = slope(loc, increasingCorner),
-                    prevDecreasingSlope = slope(prevLoc, decreasingCorner),
-                    prevIncreasingSlope = slope(prevLoc, increasingCorner);
-
-                if (decreasingSlope < prevDecreasingSlope &&
-                        increasingSlope > prevIncreasingSlope) {
-                    // Mouse is moving from previous location towards the
-                    // currently activated submenu. Delay before activating a
-                    // new menu row, because user may be moving into submenu.
-                    lastDelayLoc = loc;
-                    return DELAY;
-                }
-
-                lastDelayLoc = null;
-                return 0;
-            };
-
-        /**
-         * Hook up initial menu events
-         */
-        $menu
-            .mouseleave(mouseleaveMenu)
-            .find(options.rowSelector)
-                .mouseenter(mouseenterRow)
-                .mouseleave(mouseleaveRow)
-                .click(clickRow);
-
-        $(document).mousemove(mousemoveDocument);
-
-    };
-})(jQuery);
-

+ 1 - 1
modules/boonex/contact/classes/BxContactModule.php

@@ -125,7 +125,7 @@ class BxContactModule extends BxDolModule
 
         if (defined('BX_API'))
             return [
-                ['id' => 1, 'type' => 'form', 'data' => $oForm->getCode(), 'request' => ['url' => '/api.php?r=bx_contact/get_block_form', 'immutable' => true]],
+                ['id' => 1, 'type' => 'form', 'data' => $oForm->getCodeAPI(), 'request' => ['url' => '/api.php?r=bx_contact/get_block_form', 'immutable' => true]],
                 ['id' => 2, 'type' => 'msg', 'data' => $sResult],
             ];
 

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

@@ -1698,6 +1698,7 @@
     <string name="_adm_stg_cpt_option_sys_logs_storage_default"><![CDATA[Default logs storage]]></string>
     <string name="_adm_stg_cpt_option_sys_vote_reactions_quick_mode"><![CDATA[Enable 'Quick Reactions' mode - vote with default reaction when clicked]]></string>
     <string name="_adm_stg_cpt_option_sys_cmts_enable_auto_approve"><![CDATA[Auto approve a comment after creation]]></string>
+    <string name="_adm_stg_cpt_option_sys_create_post_form_preloading_list"><![CDATA[Preload forms asynchronously in "Create Post" blocks]]></string>
     <string name="_adm_stg_cpt_option_sys_a2a_enable"><![CDATA[Use Add2Any code instead of default sharing menu]]></string>
     <string name="_adm_stg_cpt_option_sys_a2a_code"><![CDATA[Add2Any code]]></string>
 

+ 17 - 2
modules/boonex/payment/classes/BxPaymentProviderStripeBasic.php

@@ -246,7 +246,17 @@ class BxPaymentProviderStripeBasic extends BxBaseModPaymentProvider
 
     public function getBillingRecurring($iPendingId, $sCustomerId, $sSubscriptionId)
     {
-        $aCard = $this->_retrieveCard($sCustomerId)->jsonSerialize();
+        $aCard = ['brand' => '', 'country' => '', 'funding' => '', 'last4' => '', 'exp_month' => '', 'exp_year' => '', 'cvc_check' => ''];
+
+        $oCard = $this->_retrieveCard($sCustomerId);
+        if($oCard === false && ($oSubscription = $this->_retrieveSubscription($sCustomerId, $sSubscriptionId)) !== false) {
+            $sPaymentMethodId = $oSubscription->default_payment_method;
+            if(!empty($sPaymentMethodId) && ($oPaymentMethod = $this->_retrievePaymentMethod($sPaymentMethodId)) !== false)
+                $oCard = $oPaymentMethod->card;
+        }
+
+        if(!empty($oCard))
+            $aCard = $oCard->jsonSerialize();
 
         return $this->_oModule->_oTemplate->parseHtmlByName('strp_billing_recurring.html', array(
             'brand' => $aCard['brand'],
@@ -254,7 +264,7 @@ class BxPaymentProviderStripeBasic extends BxBaseModPaymentProvider
             'type' => $aCard['funding'],
             'number' => _t('_bx_payment_strp_txt_card_number_mask', $aCard['last4']),
             'expires' => _t('_bx_payment_strp_txt_card_expires_mask', $aCard['exp_month'], $aCard['exp_year']),
-            'cvc' => _t(strcmp($aCard['cvc_check'], 'pass') === 0 ? '_bx_payment_strp_txt_card_cvc_passed' : '_bx_payment_txt_none'),
+            'cvc' => _t(!empty($aCard['cvc_check']) && strcmp($aCard['cvc_check'], 'pass') === 0 ? '_bx_payment_strp_txt_card_cvc_passed' : '_bx_payment_txt_none'),
         ));
     }
 
@@ -416,6 +426,11 @@ class BxPaymentProviderStripeBasic extends BxBaseModPaymentProvider
         return $oSubscription;
     }
 
+    protected function _retrievePaymentMethod($sPaymentMethodId)
+    {
+        return false;
+    }
+
     protected function _retrieveProduct($sId)
     {
         $oProduct = null;

+ 69 - 4
modules/boonex/payment/classes/BxPaymentProviderStripeV3.php

@@ -270,7 +270,7 @@ class BxPaymentProviderStripeV3 extends BxPaymentProviderStripeBasic implements
                         $sMessage = $this->_sLangsPrefix . 'strp_msg_authorized';
                         $bAuthorized = $aSetupIntent['status'] == 'succeeded';
                         break;
-                    
+
                     case 'payment':
                         $oPaymentIntent = $this->_retrievePaymentIntent($sOrderId);
                         if($oPaymentIntent === false)
@@ -279,7 +279,7 @@ class BxPaymentProviderStripeV3 extends BxPaymentProviderStripeBasic implements
                         $aPaymentIntent = $oPaymentIntent->jsonSerialize();
                         if(empty($aPaymentIntent) || !is_array($aPaymentIntent))
                             return array('code' => 4, 'message' => $this->_sLangsPrefix . 'err_cannot_perform');
-                        
+
                         $sMessage = $this->_sLangsPrefix . 'strp_msg_charged';
                         $bPaid = $aPaymentIntent['status'] == 'succeeded';
                         break;
@@ -309,7 +309,6 @@ class BxPaymentProviderStripeV3 extends BxPaymentProviderStripeBasic implements
                     'customer_id' => $sCustomerId,
                     'subscription_id' => $sOrderId,
                     'client_email' => $aCustomer['email'],
-                    'paid' => $this->isSubscriptionStatus(BX_PAYMENT_SBS_STATUS_ACTIVE, $aSubscription),
                     'trial' => $this->isSubscriptionStatus(BX_PAYMENT_SBS_STATUS_TRIAL, $aSubscription)
                 ));
                 break;
@@ -467,7 +466,7 @@ class BxPaymentProviderStripeV3 extends BxPaymentProviderStripeBasic implements
 
         return $this->_oStripe;
     }
-            
+
     /*
      * Related Docs: https://stripe.com/docs/api/customers/retrieve
      */
@@ -793,6 +792,72 @@ class BxPaymentProviderStripeV3 extends BxPaymentProviderStripeBasic implements
         return $oSubscription;
     }
 
+    /*
+     * Related Docs: https://stripe.com/docs/api/tokens/create_card
+     */
+    protected function _createToken($aCard)
+    {
+        try {
+            $oToken = $this->_getStripe()->tokens->create(['card' => $aCard]);
+        }
+        catch (Stripe\Error\Base $oException) {
+            return $this->_processException('Create Token Error: ', $oException);
+        }
+
+        return $oToken->jsonSerialize();
+    }
+
+    /*
+     * Related Docs: https://stripe.com/docs/api/cards/create
+     */
+    protected function _createCard($sType, $sCustomerId, $sToken)
+    {
+        try {
+            $oCard = $this->_getStripe()->customers->createSource($sCustomerId, [
+                'source' => $sToken
+            ]);
+        }
+        catch (Stripe\Error\Base $oException) {
+            return $this->_processException('Create Card Error: ', $oException);
+        }
+
+        return $oCard->jsonSerialize();
+    }
+
+    protected function _retrieveCard($sCustomerId, $sCardId = '')
+    {
+        try {
+            $oCustomer = $this->_getStripe()->customers->retrieve($sCustomerId);
+
+            if(empty($sCardId))
+                $sCardId = $oCustomer->default_source;
+            if(empty($sCardId))
+                return false;
+
+            $oCard = $this->_getStripe()->customers->retrieveSource($sCustomerId, $sCardId);
+        }
+        catch (Exception $oException) {
+            return $this->_processException('Retrieve Card Error: ', $oException);
+        }
+
+        return $oCard;
+    }
+
+    /*
+     * Related Docs: https://stripe.com/docs/api/payment_methods/retrieve
+     */
+    protected function _retrievePaymentMethod($sPaymentMethodId)
+    {
+        try {
+            $oPaymentMethod = $this->_getStripe()->paymentMethods->retrieve($sPaymentMethodId);
+        }
+        catch (Exception $oException) {
+            return $this->_processException('Retrieve Payment Method Error: ', $oException);
+        }
+
+        return $oPaymentMethod;
+    }
+
     protected function _getButton($sType, $iClientId, $iVendorId, $aParams = array())
     {
         list($sJsCode, $sJsMethod) = $this->_getButtonJs($sType, $iClientId, $iVendorId, $aParams);

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

@@ -1677,6 +1677,7 @@
     <string name="_adm_stg_cpt_option_sys_logs_storage_default"><![CDATA[Система логирования по умолчанию]]></string>
     <string name="_adm_stg_cpt_option_sys_vote_reactions_quick_mode"><![CDATA[Включить режим 'Быстрых реакций' - голосовать при клике, используя реакцию по умолчанию]]></string>
     <string name="_adm_stg_cpt_option_sys_cmts_enable_auto_approve"><![CDATA[Автоматически активировать комментарий после создания]]></string>
+    <string name="_adm_stg_cpt_option_sys_create_post_form_preloading_list"><![CDATA[Предварительно загружать формы в асинхронном режиме в "Создать пост" блоках]]></string>
     <string name="_adm_stg_cpt_option_sys_a2a_enable"><![CDATA[Использовать Add2Any код вместо стандартного меню "Поделиться"]]></string>
     <string name="_adm_stg_cpt_option_sys_a2a_code"><![CDATA[Add2Any код]]></string>
     <string name="_adm_stg_cpt_type_system"><![CDATA[Система]]></string>

+ 0 - 4
modules/boonex/timeline/install/sql/disable.sql

@@ -58,7 +58,3 @@ DELETE FROM `sys_email_templates` WHERE `Module` = 'bx_timeline';
 
 -- CRON
 DELETE FROM `sys_cron_jobs` WHERE `name` LIKE 'bx_timeline%';
-
-
--- PRELOADER
-DELETE FROM `sys_preloader` WHERE `module` = 'bx_timeline';

+ 0 - 6
modules/boonex/timeline/install/sql/enable.sql

@@ -562,9 +562,3 @@ INSERT INTO `sys_cron_jobs` (`name`, `time`, `class`, `file`, `service_call`) VA
 ('bx_timeline_hot', '0 * * * *', 'BxTimelineCronHot', 'modules/boonex/timeline/classes/BxTimelineCronHot.php', ''),
 ('bx_timeline_publishing', '* * * * *', 'BxTimelineCronPublishing', 'modules/boonex/timeline/classes/BxTimelineCronPublishing.php', ''),
 ('bx_timeline_clean', '0 0 * * *', 'BxTimelineCronClean', 'modules/boonex/timeline/classes/BxTimelineCronClean.php', '');
-
-
--- PRELOADER
-SET @iOrderPreloader = (SELECT IFNULL(MAX(`order`), 9999) FROM `sys_preloader` WHERE `type`='js_system' AND `order` > 9999 LIMIT 1);
-INSERT INTO `sys_preloader`(`module`, `type`, `content`, `active`, `order`) VALUES
-('bx_timeline', 'js_system', 'modules/boonex/timeline/js/|modernizr.min.js', 1, @iOrderPreloader + 1);

+ 2 - 1
plugins_public/tailwind/css/tailwind.css

@@ -598,6 +598,7 @@
         @apply bx-btn-border;
         @apply bx-btn-text;
         @apply transform-gpu transition-all duration-150;
+        @apply cursor-pointer;
         @apply bx-btn-hover bx-btn-active;
     }
     .bx-btn i, 
@@ -691,7 +692,7 @@
     .bx-artificer .bx-form-input-time,
     .bx-artificer .bx-form-input-datepicker,
     .bx-artificer .bx-form-input-datetime,
-    .bx-artificer .bx-form-input-textarea:not(.bx-form-input-html),
+    .bx-artificer .bx-form-input-textarea,
     .bx-artificer .bx-form-input-text,
     .bx-artificer .bx-form-input-price,
     .bx-artificer .bx-form-input-wrapper-price span {

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
plugins_public/tailwind/css/tailwind.min.css


+ 8 - 0
template/create_post_form.html

@@ -1,6 +1,14 @@
 <div id="sys-create-post-form" class="sys-create-post-form relative">
     <div class="sys-cpf-cnt">
         <div id="sys-cpf-form">
+            <div class="sys-cpf-form sys-cpf-loading bx-def-bc-margin" style="display:none">
+                <div class="flex items-center p-4">
+                    <div class="flex-0 h-10 w-10 bg-gray-200 dark:bg-gray-700 rounded-full">&nbsp;</div>
+                    <div class="flex-1 ml-4 space-y-2 animate-pulse">
+                        <div class="h-8 w-full bg-gray-300 dark:bg-gray-600 rounded-md">&nbsp;</div>
+                    </div>
+                </div>
+            </div>
             <div class="sys-cpf-form sys-cpf-default flex items-center p-4 bg-gray-50 hover:bg-white dark:bg-gray-900/50 dark:hover:bg-gray-900 shadow hover:shadow-md ring-1 ring-gray-300 dark:ring-gray-700 rounded-md cursor-pointer" onclick="__js_object__.getForm('__default__')">
                 <div class="sys-cpf-user">__user_thumb__</div>
                 <div class="sys-cpf-content flex text-base ml-4 text-gray-500 dark:text-gray-400">

+ 25 - 0
template/css/general.css

@@ -517,6 +517,10 @@ div.bx-account-unit-online {
 .sys-cpf-form {
     position: relative;
 }
+.sys-cpf-form:not(.sys-cpf-loading) {
+    -webkit-animation: sys-cpf-scale-in 0.4s cubic-bezier(0.37, 0.82, 0.2, 1);
+    animation: sys-cpf-scale-in 0.4s cubic-bezier(0.37, 0.82, 0.2, 1);
+}
 .sys-cpf-form .bx-uploader-ghost .bx-base-general-icon-wrapper img {
     max-width: 9.375rem;
 }
@@ -528,6 +532,27 @@ div.bx-account-unit-online {
     bottom: 0;
 }
 
+@keyframes sys-cpf-scale-in {
+    0% {
+        opacity: 0;
+        transform: scale(0.9);
+    }
+    100% {
+        opacity: 1;
+        transform: scale(1.0);
+    }
+}
+
+@-webkit-keyframes sys-cpf-scale-in {
+    0% {
+        opacity: 0;
+        transform: scale(0.9);
+    }
+    100% {
+        opacity: 1;
+        transform: scale(1.0);
+    }
+}
 
 /* --------------- Account Unit */
 .bx-account-unit .bx-def-unit-img, 

+ 12 - 8
template/menu_interactive_vertical.html

@@ -1,12 +1,16 @@
 <ul id="bx-menu-inter-__id__" class="bx-menu-inter-vert bx-menu-object-__object__">
     <bx_repeat:menu_items>
-        <li id="__name__-act" class="bx-menu-inter-act __class_wrp_act__">
-            <div class="bx-def-padding-right bx-def-padding-sec-left bx-def-padding-sec-topbottom __class__" title="__title_attr__">__title__</div>
+        <li id="__name__-act" class="bx-menu-inter-act __class_wrp_act__ bx-menu-tab-active">
+            <div class="__class__ bx-stl-mil group flex flex-auto items-center truncate" title="__title_attr__">
+                <span class="bx-menu-item-title bx-stl-mit">__title__</span>
+            </div>
         </li>
-        <li id="__name__-pas" class="bx-menu-inter-pas bx-def-round-corners bx-def-color-bg-hl-hover __class_wrp_pas__">
-            <a id="__name__" class="__class__" href="__link__" onclick="__onclick__" title="__title_attr__"__attrs__>
-                <div class="bx-def-padding-right bx-def-padding-sec-left bx-def-padding-sec-topbottom">__title__</div>
-            </a>
+        <li id="__name__-pas" class="bx-menu-inter-pas __class_wrp_pas__">
+            <div class="bx-menu-item-cnt bx-stl-mil-wo-gaps">
+                <a id="__name__" class="__class__ bx-stl-mil-gaps group flex flex-auto items-center truncate" href="__link__" onclick="__onclick__" title="__title_attr__"__attrs__>
+                    <span class="bx-menu-item-title bx-stl-mit">__title__</span>
+                </a>
+            </div>
         </li>
     </bx_repeat:menu_items>
 </ul>
@@ -15,7 +19,7 @@
     	$('#bx-menu-inter-__id__').find('a').bind('click', function() {
             var sId = $(this).attr('id');
             var sClassHidden = 'bx-menu-inter-hidden';
-            $(this).parent().siblings('.bx-menu-inter-act:not(.' + sClassHidden + ')').addClass(sClassHidden).siblings('.bx-menu-inter-pas.' + sClassHidden).removeClass(sClassHidden).siblings('#' + sId + '-pas:not(.' + sClassHidden + ')').addClass(sClassHidden).siblings('#' + sId + '-act.' + sClassHidden).removeClass(sClassHidden);
+            $(this).parents('li:first').siblings('.bx-menu-inter-act:not(.' + sClassHidden + ')').addClass(sClassHidden).siblings('.bx-menu-inter-pas.' + sClassHidden).removeClass(sClassHidden).siblings('#' + sId + '-pas:not(.' + sClassHidden + ')').addClass(sClassHidden).siblings('#' + sId + '-act.' + sClassHidden).removeClass(sClassHidden);
     	});
     });
-</script>
+</script>

+ 2 - 2
template/menu_main_in_panel.html

@@ -1,8 +1,8 @@
 <ul class="bx-menu-main-in-panel bx-menu-ver bx-menu-object-__object__ bx-clearfix">
     <bx_repeat:menu_items>
         <li class="bx-menu-item __name__ __class_add__ rounded-md">
-            <div class="bx-menu-item-cnt bx-stl-mil group">
-                <a class="__name__ flex items-center justify-between truncate" href="__link__" onclick="__onclick__"__attrs__>
+            <div class="bx-menu-item-cnt bx-stl-mil-wo-gaps">
+                <a class="__name__ bx-stl-mil-gaps group flex flex-auto items-center truncate" href="__link__" onclick="__onclick__"__attrs__>
                     <span class="bx-menu-item-icon bx-stl-mii">
                         <bx_include_auto:menu_icon.html />
                     </span>

+ 37 - 0
template/scripts/BxBaseCmts.php

@@ -133,6 +133,43 @@ class BxBaseCmts extends BxDolCmts
         return $this->_oTemplate->_wrapInTagJsCode("if(window['" . $this->_sJsObjName . "'] == undefined) var " . $this->_sJsObjName . " = new " . $this->_sJsObjClass . "(" . json_encode($aParams) . "); " . $this->_sJsObjName . ".cmtInit();");
     }
 
+    function getCommentsBlockAPI($aBp = [], $aDp = [])
+    {
+        $mixedResult = $this->isViewAllowed();
+        if($mixedResult !== CHECK_ACTION_RESULT_ALLOWED)
+            return $mixedResult; // TODO: error checking
+
+        $this->_getParams($aBp, $aDp);
+        $this->_prepareParams($aBp, $aDp);
+
+        $aCmts = $this->getCommentsArray($aBp['vparent_id'], $aBp['filter'], $aBp['order'], $aBp['start'], $aBp[($aBp['init_view'] != -1 ? 'init' : 'per') . '_view']);
+
+        return [
+            'unit' => 'comments',
+            'data' => $this->decodeData($aCmts),
+        ];
+    }
+
+    function decodeData ($a)
+    {
+        foreach ($a as $i => $r) {
+            if (isset($r['cmt_author_id']))
+                $a[$i]['author_data'] = $this->decodeDataAuthor($r);
+        }
+
+        return $a;
+    }
+
+    function decodeDataAuthor ($r) // TODO: get rid of duplicate code here and in BxBaseModGeneralSearchResult
+    {
+        $oProfile = BxDolProfile::getInstanceMagic($r['cmt_author_id']);
+        return [
+            'display_name' => $oProfile->getDisplayName(),
+            'url' => $oProfile->getUrl(),
+            'url_avatar' => $oProfile->getAvatar(),
+        ];
+    }
+
     /**
      * get full comments block with initializations
      */

+ 9 - 3
template/scripts/BxBaseFormView.php

@@ -330,9 +330,6 @@ class BxBaseFormView extends BxDolForm
             'include' => &$sInclude
         ));
 
-        if (defined('BX_API'))
-            return ['inputs' => $this->aInputs, 'attrs' => $this->aFormAttrs, 'params' => $this->aParams];
-
         if($this->sCode === false)
             $this->sCode = $this->genForm();
 
@@ -341,6 +338,15 @@ class BxBaseFormView extends BxDolForm
         return $sInclude . $sDynamicCssJs . $this->sCode;
     }
 
+    function getCodeAPI()
+    {
+        $this->aFormAttrs = $this->_replaceMarkers($this->aFormAttrs);
+    
+        // TODO: process inputs to translate titles, alerts, etc
+    
+        return ['inputs' => $this->aInputs, 'attrs' => $this->aFormAttrs, 'params' => $this->aParams];
+    }
+
     public function getJsClassName()
     {
         return $this->_sJsClassName;

+ 39 - 5
template/scripts/BxBaseServicePages.php

@@ -21,10 +21,10 @@ class BxBaseServicePages extends BxDol
      * @page service Service Calls
      * @section bx_system_pages System Services 
      * @subsection bx_system_pages-pages Pages
-     * @subsubsection bx_system_pages-get_page Get page
+     * @subsubsection bx_system_pages-get_page_by_request Get page by page URI
      * 
-     * @code bx_srv('system', 'get_page', ['about'], 'TemplServicePages'); @endcode
-     * @code {{~system:get_page:TemplServicePages['about']~}} @endcode
+     * @code bx_srv('system', 'get_page_by_request', ['about'], 'TemplServicePages'); @endcode
+     * @code {{~system:get_page_by_request:TemplServicePages['about']~}} @endcode
      * 
      * Test method which page data
      * @param $sURI page URI 
@@ -32,9 +32,43 @@ class BxBaseServicePages extends BxDol
      * @see BxBaseServicePages::serviceGetPage
      */
     /** 
-     * @ref bx_system_general-get_page "get_page"
+     * @ref bx_system_general-get_page_by_request "get_page_by_request"
      */
-    public function serviceGetPage ($sURI)
+    public function serviceGetPageByRequest ($sRequest)
+    {
+        $mixed = BxDolPage::getPageBySeoLink($sRequest);
+
+        if (($sUrl = $mixed) && is_string($sUrl)) {
+            $aRes = ['redirect' => $sUrl];
+        }
+        elseif (($oPage = $mixed) && is_object($oPage)) {
+            return $oPage->getPage();
+        }
+        else {
+            $aRes = ['error' => _t("_sys_request_page_not_found_cpt")];
+        }
+        
+        return $aRes;
+    }
+
+    /**
+     * @page service Service Calls
+     * @section bx_system_pages System Services 
+     * @subsection bx_system_pages-pages Pages
+     * @subsubsection bx_system_pages-get_page_by_uri Get page by page URI
+     * 
+     * @code bx_srv('system', 'get_page_by_uri', ['about'], 'TemplServicePages'); @endcode
+     * @code {{~system:get_page_by_uri:TemplServicePages['about']~}} @endcode
+     * 
+     * Test method which page data
+     * @param $sURI page URI 
+     * 
+     * @see BxBaseServicePages::serviceGetPage
+     */
+    /** 
+     * @ref bx_system_general-get_page_by_uri "get_page_by_uri"
+     */
+    public function serviceGetPageByUri ($sURI)
     {
         $oPage = BxDolPage::getObjectInstanceByURI($sURI, false, true);
         if (!$oPage) {

+ 51 - 10
template/scripts/BxBaseServices.php

@@ -611,10 +611,10 @@ class BxBaseServices extends BxDol implements iBxDolProfileService
             $mixedContextId = (int)(!empty($mixedContextId) ? -$mixedContextId : $oProfile->id());
 
     	$oMenu = BxDolMenu::getObjectInstance('sys_create_post');
-        $oMenu->setContextId($mixedContextId);
         if(!$oMenu)
             return '';
 
+        $oMenu->setContextId($mixedContextId);
     	$aMenuItems = $oMenu->getMenuItems();
     	if(empty($aMenuItems) || !is_array($aMenuItems))
             return '';
@@ -633,30 +633,71 @@ class BxBaseServices extends BxDol implements iBxDolProfileService
         $sTitle = _t('_sys_page_block_title_create_post' . (!$bContext ? '_public' : ($mixedContextId < 0 ? '_context' : '')));
         $sPlaceholder = _t('_sys_txt_create_post_placeholder', $oProfile->getDisplayName());
 
+        $oDbModules = BxDolModuleQuery::getInstance();
     	$oTemplate = BxDolTemplate::getInstance();
-    	$oTemplate->addJs(array('BxDolCreatePost.js'));
+    	$oTemplate->addJs(['BxDolCreatePost.js']);
+
+        $aPreloadingList = [];
+        $aModules = explode(',', getParam('sys_create_post_form_preloading_list'));
+        if(!empty($aModules))
+            foreach($aModules as $sModule) {
+                $aModule = $oDbModules->getModuleByName($sModule);
+                if(empty($aModule) || !is_array($aModule))
+                    continue;
+
+                $aPreloadingList[$sModule] = $aModule['uri'];
+            }
 
     	$sJsObject = 'oBxDolCreatePost';
-        $sJsContent = $oTemplate->_wrapInTagJsCode("var " . $sJsObject . " = new BxDolCreatePost(" . json_encode(array(
+        $sJsContent = $oTemplate->_wrapInTagJsCode("var " . $sJsObject . " = new BxDolCreatePost(" . json_encode([
             'sObjName' => $sJsObject,
             'sRootUrl' => BX_DOL_URL_ROOT,
             'sDefault' => $sDefault,
             'iContextId' => $bContext ? $mixedContextId : 0,
+            'oPreloadingList' => $aPreloadingList,
             'oCustom' => $aCustom
-        )) . ");");
+        ]) . ");");
 
-    	return array(
+    	return [
             'title' => $sTitle,
-            'content' => BxDolTemplate::getInstance()->parseHtmlByName('create_post_form.html', array(
+            'content' => $oTemplate->parseHtmlByName('create_post_form.html', [
                 'default' => $sDefault,
                 'placeholder' => $sPlaceholder,
-                'user_thumb' => $oProfile->getUnit(0, array('template' => 'unit_wo_info_links')),
-                'form' => BxDolService::call($sDefault, 'get_create_post_form', array(array('context_id' => $mixedContextId, 'ajax_mode' => true, 'absolute_action_url' => true, 'custom' => $aCustom))),
+                'user_thumb' => $oProfile->getUnit(0, ['template' => 'unit_wo_info_links']),
+                'form' => bx_srv($sDefault, 'get_create_post_form', [[
+                    'context_id' => $mixedContextId, 
+                    'ajax_mode' => true, 
+                    'absolute_action_url' => true, 
+                    'custom' => $aCustom
+                ]]),
                 'js_object' => $sJsObject,
                 'js_content' => $sJsContent
-            )),
+            ]),
             'menu' => $oMenu
-        );
+        ];
+    }
+
+    public function serviceGetOptionsCreatePostFormPreloadingList()
+    {
+        $oMenu = BxDolMenu::getObjectInstance('sys_create_post');
+        if(!$oMenu)
+            return [];
+
+        $aMenuItems = $oMenu->getMenuItems();
+
+        $aResult = [];
+        foreach($aMenuItems as $aMenuItem) {
+            $sModule = !empty($aMenuItem['module']) ? $aMenuItem['module'] : $aMenuItem['name'];
+            if(!bx_srv('system', 'is_module_enabled', [$sModule]))
+                continue;
+
+            $aResult[] = [
+                'key' => $sModule, 
+                'value' => _t('_' . $sModule)
+            ];
+        }
+
+        return $aResult;
     }
 
     public function serviceGetBlockAuthor($sModule, $iContentId = 0)

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff