소스 검색

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

AntonLV 4 년 전
부모
커밋
22146aadb8

+ 10 - 8
modules/boonex/artificer/data/template/bx_forum/unit_gallery.html

@@ -1,21 +1,23 @@
 <div onclick="location.href='__content_url__'" class="__class__ w-full bx-base-text-unit bx-base-text-unit-gallery bx-def-gal-unit-size group p-1 -m-1">
     <div class="bg-white dark:bg-gray-800 shadow ring-1 ring-gray-300 dark:ring-gray-700 rounded-md xl:mx-auto h-full">
-        <div class="h-full space-y-4 p-1 pb-4 flex flex-col justify-between">
+        <div class="h-full space-y-4 p-1 pb-4 flex flex-col">
             <bx_if:thumb>
                 <a href="__content_url__" title="__summary_attr__">   <img class="flex grow-0 w-full aspect-video overflow-y-auto align-top  object-cover rounded" src="__gallery_url__" alt=""></a>  
+                <div class="grow px-3 mt-4 text-lg leading-tight font-semibold text-gray-700 group-hover:text-gray-900 dark:text-gray-200 dark:group-hover:text-white">
+                    <a href="__content_url__" title="__summary_attr__" class=" text-lg leading-tight font-semibold text-gray-600 group-hover:text-gray-900 dark:text-gray-300 dark:group-hover:text-white">__title__ __badges__</a>
+                </div>
             </bx_if:thumb>
-            <div class="grow-none px-3 mt-4 text-lg leading-tight font-semibold text-gray-700 group-hover:text-gray-900 dark:text-gray-200 dark:group-hover:text-white">
-                <a href="__content_url__" title="__summary_attr__" class=" text-lg leading-tight font-semibold text-gray-600 group-hover:text-gray-900 dark:text-gray-300 dark:group-hover:text-white">__title__</a>
-            </div>               
-            <bx_if:no_thumb>  
+                           
+            <bx_if:no_thumb>
+                <div class="grow-none px-3 mt-4 text-lg leading-tight font-semibold text-gray-700 group-hover:text-gray-900 dark:text-gray-200 dark:group-hover:text-white">
+                    <a href="__content_url__" title="__summary_attr__" class=" text-lg leading-tight font-semibold text-gray-600 group-hover:text-gray-900 dark:text-gray-300 dark:group-hover:text-white">__title__ __badges__</a>
+                </div>
                 <div class="grow relative rounded w-full max-w-full sm:aspect-video overflow-hidden font-normal text-sm  text-gray-500 group-hover:text-gray-600 dark:text-gray-400 dark:group-hover:text-gray-300">
                     <div class="absolute top-auto bottom-0  w-full h-1/4 bg-gradient-to-b from-white/0 to-white dark:from-gray-900/0 dark:to-gray-800"></div>
                     <div class=" px-3">__summary_plain__</div>
                 </div>
             </bx_if:no_thumb>
-            <div class="px-3">
-                __badges__
-            </div>
+           
             <bx_if:meta>
                 <div class="bx-base-general-unit-meta flex grow-0 px-2">
                     <div class="inline-flex flex-wrap text-xs font-medium">

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

@@ -164,7 +164,6 @@ div.bx-popup-menu-main.sys-add-content ul {
 /*--- Profile Stats/Followings Menu ---*/
 .bx-db-content .bx-profile-stats .bx-profile-avatar-cnt,
 .bx-db-content .bx-profile-stats .bx-menu-vertical-more-less,
-.bx-db-content.bx-content-padding-block .bx-menu-vertical-more-less,
 .bx-db-content.bx-content-padding-block .bx-menu-profile-followings {
     margin: -0.75rem;
 }

+ 5 - 0
modules/boonex/forum/template/css/main.css

@@ -546,6 +546,11 @@
     display: none;
 }
 
+.bx-base-unit-showcase-wrapper.bx-forum-wrapper{
+    display: flex;
+    flex-wrap: nowrap;
+}
+
 
 /* ---------------new meta menu need to move incore*/
 

+ 4 - 1
upgrade/files/13.0.0.B1-13.0.0.B2/files/inc/classes/BxDolPage.php

@@ -339,7 +339,10 @@ class BxDolPage extends BxDolFactory implements iBxDolFactoryObject, iBxDolRepla
         if (false === $sQuery)
             return false;
 
-        $aQueryParams = bx_parse_str($sQuery);
+        if (function_exists('bx_parse_str'))
+            $aQueryParams = bx_parse_str($sQuery);
+        else
+            parse_str($sQuery, $aQueryParams);
 
         $sPageUri = !empty($aQueryParams['i']) ? $aQueryParams['i'] : false;
         unset($aQueryParams['i']);

+ 8 - 3
upgrade/files/13.0.0.B1-13.0.0.B2/files/inc/js/editor.quill.js

@@ -196,9 +196,14 @@ function bx_editor_init(oEditor, oParams){
                 dataAttributes: ['id', 'value', 'denotationChar', 'link', 'target','disabled', 'dataProfileId'], 
                 listItemClass: 'bx-menu-item bx-def-color-bg-hl-hover',
                 source: function (searchTerm, renderList, mentionChar) {
-                  $.getJSON(oParams.root_url + 'searchExtended.php?action=get_mention', $.extend({}, {symbol: mentionChar, term: searchTerm}, oParams.query_params), function(data){
-                      renderList(data, searchTerm);
-                  });
+                    var aFlds = oParams.query_params['fi'].split(",");
+                    aFlds.forEach(fld => {
+                        oParams.query_params[fld] = $('#' + oParams.query_params['f'] + ' [name="' + fld + '"]').val()
+                    });
+                        
+                    $.getJSON(oParams.root_url + 'searchExtended.php?action=get_mention', $.extend({}, {symbol: mentionChar, term: searchTerm}, oParams.query_params), function(data){
+                        renderList(data, searchTerm);
+                    });
                 },
                 renderItem: function(item, searchTerm){
                     item.id = item.url;

+ 1110 - 0
upgrade/files/13.0.0.B1-13.0.0.B2/files/modules/base/general/classes/BxBaseModGeneralFormEntry.php

@@ -0,0 +1,1110 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    BaseGeneral Base classes for modules
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+/**
+ * Create/edit entry form
+ */
+class BxBaseModGeneralFormEntry extends BxTemplFormView
+{
+    protected static $_isCssJsGeneralModuleAdded = false;
+        
+    protected $MODULE;
+
+    protected $_oModule;
+
+    protected $_aMetatagsFieldsWithKeywords = array();
+    protected $_oMetatagsObject = null;
+    protected $_oMetatagsContentId = 0;
+
+    protected $_sGhostTemplate = 'form_ghost_template.html';
+    
+    protected $_aTrackFieldsChanges;
+    
+    protected $_iContentId;
+    
+    protected $_bAllowChangeUserForAdmins;
+
+    public function __construct($aInfo, $oTemplate = false)
+    {
+        if (!isset($this->_bAllowChangeUserForAdmins))
+            $this->_bAllowChangeUserForAdmins = true;
+        
+        parent::__construct($aInfo, $oTemplate);
+
+        $this->_oModule = BxDolModule::getInstance($this->MODULE);
+
+        $this->_aTrackFieldsChanges = array();
+
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        
+        if (isset($CNF['FIELD_ADDED']) && isset($this->aInputs[$CNF['FIELD_ADDED']])) {
+            $this->aInputs[$CNF['FIELD_ADDED']]['date_filter'] = BX_DATA_INT;
+            $this->aInputs[$CNF['FIELD_ADDED']]['date_format'] = BX_FORMAT_DATE;
+        }
+
+        if (isset($CNF['FIELD_CHANGED']) && isset($this->aInputs[$CNF['FIELD_CHANGED']])) {
+            $this->aInputs[$CNF['FIELD_CHANGED']]['date_filter'] = BX_DATA_INT;
+            $this->aInputs[$CNF['FIELD_CHANGED']]['date_format'] = BX_FORMAT_DATE;
+        }
+
+        if (isset($CNF['FIELD_TEXT']) && isset($this->aInputs[$CNF['FIELD_TEXT']]) && isset($CNF['FIELD_TEXT_ID'])) {
+            $this->aInputs[$CNF['FIELD_TEXT']]['attrs'] = array_merge (
+                array ('id' => $CNF['FIELD_TEXT_ID']),
+                is_array($this->aInputs[$CNF['FIELD_TEXT']]['attrs']) ? $this->aInputs[$CNF['FIELD_TEXT']]['attrs'] : array ()
+            );
+        }
+
+        if (isset($CNF['FIELD_LABELS']) && isset($this->aInputs[$CNF['FIELD_LABELS']]) && !empty($CNF['OBJECT_METATAGS']) && ($oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS'])) && $oMetatags->keywordsIsEnabled()) {
+            $this->aInputs[$CNF['FIELD_LABELS']]['meta_object'] = $CNF['OBJECT_METATAGS'];
+        }
+
+        if (isset($CNF['FIELD_LOCATION_PREFIX']) && isset($this->aInputs[$CNF['FIELD_LOCATION_PREFIX']])) {
+            $this->aInputs[$CNF['FIELD_LOCATION_PREFIX']]['manual_input'] = true;
+        }
+
+        // add ability to change author by admins in some apps
+        if (!empty($CNF['FIELD_AUTHOR']) && ($this->_oModule->_isModerator() || $this->_oModule->_isAdministrator()) && $this->_isChangeUserForAdmins($this->aParams['display']))
+            $this->aInputs = array_merge([
+                $CNF['FIELD_AUTHOR'] => [
+                    'type' => 'custom',
+                    'name' => $CNF['FIELD_AUTHOR'],
+                    'db' => ['pass' => 'Xss'],
+                    'caption' => _t('_sys_form_input_caption_author')
+                ],
+            ], $this->aInputs);
+
+        if (isset($CNF['FIELD_PHOTO']) && isset($this->aInputs[$CNF['FIELD_PHOTO']])) {
+            $this->aInputs[$CNF['FIELD_PHOTO']]['storage_object'] = $CNF['OBJECT_STORAGE'];
+            $this->aInputs[$CNF['FIELD_PHOTO']]['uploaders'] = !empty($this->aInputs[$CNF['FIELD_PHOTO']]['value']) ? unserialize($this->aInputs[$CNF['FIELD_PHOTO']]['value']) : $CNF['OBJECT_UPLOADERS'];
+            $this->aInputs[$CNF['FIELD_PHOTO']]['images_transcoder'] = $CNF['OBJECT_IMAGES_TRANSCODER_PREVIEW'];
+            $this->aInputs[$CNF['FIELD_PHOTO']]['storage_private'] = 0;
+            $this->aInputs[$CNF['FIELD_PHOTO']]['multiple'] = true;
+            $this->aInputs[$CNF['FIELD_PHOTO']]['content_id'] = 0;
+            $this->aInputs[$CNF['FIELD_PHOTO']]['ghost_template'] = '';
+        }
+
+        $aPrivacyFields = $this->_getPrivacyFields();
+        foreach($aPrivacyFields as $sField => $sObject)
+            $this->_preparePrivacyField($sField, $sObject);
+        
+        foreach($this->aInputs as $sKey => $aInput) {
+            if ($aInput['type'] == 'nested_form'){
+                $this->genGhostTemplateForInputNestedForm ($this->aInputs[$sKey]);
+            }
+        }
+    }
+
+    protected function _isChangeUserForAdmins($sDisplay)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(!$this->_bAllowChangeUserForAdmins)
+            return false;
+
+        if(isset($CNF['OBJECT_FORM_ENTRY_DISPLAY_ADD']) && $sDisplay == $CNF['OBJECT_FORM_ENTRY_DISPLAY_ADD'])
+            return true;
+
+        if(isset($CNF['OBJECT_FORM_ENTRY_DISPLAY_EDIT']) && $sDisplay == $CNF['OBJECT_FORM_ENTRY_DISPLAY_EDIT'])
+            return true;
+
+        return false;
+    }
+
+    function getCode($bDynamicMode = false)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        
+        if(!$bDynamicMode && bx_is_dynamic_request())
+            $bDynamicMode = true;
+       
+        $sResult = parent::getCode($bDynamicMode);
+
+        $aPrivacyFields = $this->_getPrivacyFields();
+        foreach($aPrivacyFields as $sField => $sObject)
+            $this->_addCssJsPrivacyField($sField, $sObject, $bDynamicMode);
+
+        if(isset($CNF['PARAM_MULTICAT_ENABLED']) && $CNF['PARAM_MULTICAT_ENABLED'] === true) {
+            $sInclude2 = '';
+            $sInclude2 .= $this->_oModule->_oTemplate->addJs(array('BxDolCategories.js'), $bDynamicMode);
+            $sInclude2 .= $this->_oModule->_oTemplate->addCss(array('categories.css'), $bDynamicMode);
+
+            $sResult .= ($bDynamicMode ? $sInclude2 : '') . $this->_oModule->_oTemplate->getJsCode('categories');
+        }
+        
+        return $sResult;
+    }
+    
+    /**
+     * Nested forms processing
+     */
+    function genInputNestedForm (&$aInput, $sInfo = '', $sError = '')
+    {
+        $sUniqId = genRndPwd (8, false);
+        $aNestedForms = array();
+        if (!$this->isSubmitted()){
+            $aGhostTemplateParams = is_object($aInput['ghost_template']) ? $aInput['ghost_template']->aParams : $aInput['ghost_template']['params'];
+
+            $aNestedValues = $this->_oModule->_oDb->getNestedBy(array(
+                'type' => 'content_id', 
+                'id' => $this->_iContentId, 
+                'key_name' => $aGhostTemplateParams['db']['key']
+            ), $aGhostTemplateParams['db']['table']);
+
+            foreach($aNestedValues as $aNestedValue) {
+                $aNestedValuesRv = array();
+                $oForm = $this->getNestedFormObject($aInput);  
+                
+                foreach($aNestedValue as $aNestedKey => $aNestedItem) {
+                    if ($oForm->aParams['db']['key'] == $aNestedKey)
+                        $aNestedValuesRv[$aNestedKey . '[]'] = $aNestedItem;
+                    else
+                        $aNestedValuesRv[$aNestedKey] = $aNestedItem;
+                }
+                
+                $oForm->initChecker($aNestedValuesRv);
+                array_push(
+				    $aNestedForms,
+				    array(
+					    'key_value' => $aNestedValuesRv[$oForm->aParams['db']['key'] . '[]'],
+                        'key_name' => $aInput['name'],
+					    'form_code' => $oForm->genRows(),
+                        'js_instance_name' => 'oBxNestedForm_' . $sUniqId,
+                        'nested_type' => $aInput['name'],
+				    )
+			    );
+            }
+        }
+        else{
+            $aInput['ghost_templates'] = $aInput['ghost_template'];
+            $this->genGhostTemplateForInputNestedForm ($aInput);
+            foreach($aInput['ghost_templates'] as $oForm) {
+                if (isset($oForm->aInputs)){
+                    array_push(
+                        $aNestedForms,
+                        array(
+                            'key_value' => $oForm->aInputs[$aInput['name']]['value'],
+                            'key_name' => $aInput['name'],
+                            'form_code' => $oForm->genRows(),
+                            'js_instance_name' => 'oBxNestedForm_' . $sUniqId,
+                            'nested_type' => $aInput['name'],
+                        )
+                    );
+                }
+            }
+        }
+
+        return $this->oTemplate->parseHtmlByName('form_field_nested_form.html', array(
+            'bx_repeat:items' => $aNestedForms,
+            'info' => $sInfo,
+            'error' => $sError,
+            'uniq_id' => $sUniqId,
+            'js_instance_name' => 'oBxNestedForm_' . $sUniqId,
+            'options' => json_encode(array(
+                'uniq_id' => $sUniqId,
+                'js_instance_name' => 'oBxNestedForm_' . $sUniqId,
+                'template_ghost' => $this->genGhostTemplate($aInput),
+                'form_name' => $aInput['value'],
+                'action_uri' =>  $this->_oModule->_oConfig->getBaseUri(),
+                'nested_type' => $aInput['name'],
+            )),
+        ));
+    }
+    
+    function genNestedForm (&$aInput)
+    {
+        $sResult = '';
+        $aGhostTemplateParams = is_object($aInput['ghost_template']) ? $aInput['ghost_template']->aParams : $aInput['ghost_template']['params'];
+
+        $aNestedValues = $this->_oModule->_oDb->getNestedBy(array(
+            'type' => 'content_id', 
+            'id' => $this->_iContentId, 
+            'key_name' => $aGhostTemplateParams['db']['key']
+        ), $aGhostTemplateParams['db']['table']);
+
+        foreach($aNestedValues as $aNestedValue) {
+            $sValue = '';
+			$aNestedValuesRv = array();
+            $oForm = $this->getNestedFormObject($aInput, true);
+            
+            foreach($aNestedValue as $aNestedKey => $aNestedItem) {
+                if ($oForm->aParams['db']['key'] == $aNestedKey)
+                    $aNestedValuesRv[$aNestedKey . '[]'] = $aNestedItem;
+                else
+                    $aNestedValuesRv[$aNestedKey] = $aNestedItem;
+            }
+              
+            $oForm->initChecker($aNestedValuesRv);
+            
+            if ($aInput['rateable']){
+                $sVote = '';
+				$sVoteBtn = '';
+                $iId = BxDolFormQuery::getFormField($this->id, $aInput['name'], $this->_iContentId,  $aNestedValuesRv[$oForm->aParams['db']['key'] . '[]']);
+                $oVote = BxDolVote::getObjectInstance($aInput['rateable'], $iId, true, BxDolTemplate::getInstance());
+                if ($oVote){
+					$sVote = $oVote->getCounter(array('show_counter_empty' => true, 'show_counter' => true, 'show_counter_style' => 'simple', 'dynamic_mode' => $this->_bDynamicMode));
+					$sVoteBtn = $oVote->getElementInline(array('show_counter_empty' => true, 'show_counter' => false, 'show_counter_style' => 'simple', 'dynamic_mode' => $this->_bDynamicMode));
+				}
+				$sValue = $this->oTemplate->parseHtmlByName('form_view_rateable_row.html', array(
+					'value' =>  $oForm->getCode(),
+					'rate' => $sVote,
+					'rate_btn' => $sVoteBtn
+				));
+            }
+			else{
+				$sValue =  $oForm->getCode();
+			}
+			$sResult .= $this->oTemplate->parseHtmlByName('form_view_nested_row.html', array(
+				'value' =>  $sValue
+			));
+        }
+        if ($sResult != ''){
+            return $this->oTemplate->parseHtmlByName('form_view_row.html', array(
+                'type' => $aInput['type'], 
+                'caption' => isset($aInput['caption']) ? bx_process_output($aInput['caption']) : '',
+                'value' => $sResult
+            ));
+        }
+    }
+    
+    protected function genGhostTemplateForInputNestedForm (&$aInput)
+    {
+        $oForm = $this->getNestedFormObject($aInput);  
+        $aInput['ghost_template'] = array ( 
+            'params' => array(
+                'nested_form_template' => 'form_field_nested_form_wrapper.html',
+                'db' => array(
+                    'table' => $oForm->aParams['db']['table'],
+                    'key' => $oForm->aParams['db']['key'],
+                    'submit_name' =>  $oForm->aParams['db']['key'],
+                ),
+            ),
+            'inputs' => $oForm->aInputs
+        );
+    }
+    
+    function getNestedFormObject (&$aInput, $bViewMode = false)
+    {
+        $oForm = BxDolForm::getObjectInstance($aInput['value'], $aInput['value'] . ($bViewMode ? '_view' : ''));
+        foreach($oForm->aInputs as $sKey => $aInput2) {
+            $sName = $oForm->aInputs[$sKey]['name'];
+            if (strpos($sName,'[]') === false)
+                $oForm->aInputs[$sKey]['name'] = $sName . '[]';
+        }
+        return $oForm;
+    }
+
+    /**
+     * Add field(s) which will be tracked during content update. 
+     */
+    public function addTrackFields($mixedFields, $mixedContent)
+    {
+        if(empty($mixedContent) || empty($mixedFields))
+            return;
+
+        if(!is_array($mixedContent)) {
+            $mixedContent = $this->_oModule->_oDb->getContentInfoById((int)$mixedContent);
+            if(empty($mixedContent) || !is_array($mixedContent))
+                return;
+        }
+
+        if(!is_array($mixedFields))
+            $mixedFields = array($mixedFields);
+
+        foreach($mixedFields as $sField) {
+            if(!isset($mixedContent[$sField]))
+                continue;
+
+            $this->_aTrackFieldsChanges[$sField] = array(
+                'old' => $mixedContent[$sField],
+            );
+        }
+    }
+
+    /**
+     * Checks if the field was changed.
+     */
+    public function isTrackFieldChanged($sField, $bReturnValues = false)
+    {
+        if(!isset($this->_aTrackFieldsChanges[$sField]) || $this->_aTrackFieldsChanges[$sField] === false)
+            return false;
+
+        return $bReturnValues ? $this->_aTrackFieldsChanges[$sField] : true;
+    }
+
+    function initChecker ($aValues = array (), $aSpecificValues = array())
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        
+        $bValues = $aValues && !empty($aValues['id']);
+        
+        $this->_iContentId = isset($aValues['id']) ? $aValues['id'] : false;
+        
+        if (!empty($CNF['FIELD_LOCATION_PREFIX']) && isset($this->aInputs[$CNF['FIELD_LOCATION_PREFIX']]) && isset($aValues[$CNF['FIELD_ID']]) && !empty($CNF['OBJECT_METATAGS']) && ($oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS'])) && $oMetatags->locationsIsEnabled())
+            $this->aInputs[$CNF['FIELD_LOCATION_PREFIX']]['value'] = $oMetatags->locationsString($aValues[$CNF['FIELD_ID']], false);
+
+        if (isset($CNF['FIELD_PHOTO']) && isset($this->aInputs[$CNF['FIELD_PHOTO']])) {
+
+            $aContentInfo = false;
+            if ($aValues && !empty($aValues['id'])) {
+                $aContentInfo = $this->_oModule->_oDb->getContentInfoById ($aValues['id']);
+                $this->aInputs[$CNF['FIELD_PHOTO']]['content_id'] = $aValues['id'];
+            }
+
+            $this->aInputs[$CNF['FIELD_PHOTO']]['ghost_template'] = $this->_oModule->_oTemplate->parseHtmlByName($this->_sGhostTemplate, $this->_getPhotoGhostTmplVars($aContentInfo));
+        }
+
+        if (isset($CNF['FIELD_LABELS']) && isset($this->aInputs[$CNF['FIELD_LABELS']]) && !empty($CNF['OBJECT_METATAGS']) && ($oMetatags = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS'])) && $oMetatags->keywordsIsEnabled() && !empty($aValues['id'])) {
+            $this->aInputs[$CNF['FIELD_LABELS']]['content_id'] = $aValues['id'];
+            if(($aLabels = $oMetatags->keywordsGet($aValues['id'])))
+                $this->aInputs[$CNF['FIELD_LABELS']]['value'] = array_intersect($aLabels, BxDolLabel::getInstance()->getLabels(array('type' => 'values')));
+        }
+
+        if (isset($CNF['FIELD_ANONYMOUS']) && isset($this->aInputs[$CNF['FIELD_ANONYMOUS']]) && isset($CNF['FIELD_AUTHOR']) && isset($aValues[$CNF['FIELD_AUTHOR']])) {
+            $this->aInputs[$CNF['FIELD_ANONYMOUS']]['checked'] = $aValues[$CNF['FIELD_AUTHOR']] < 0;
+        }
+        
+        if (isset($CNF['FIELD_MULTICAT']) && isset($this->aInputs[$CNF['FIELD_MULTICAT']]) && $CNF['PARAM_MULTICAT_ENABLED']) {
+            if ($bValues)
+                $this->aInputs[$CNF['FIELD_MULTICAT']]['content_id'] = $aValues['id'];
+        }
+
+        $aPrivacyFields = $this->_getPrivacyFields();
+        foreach($aPrivacyFields as $sField => $sObject)
+            $this->_preloadPrivacyField($sField, $sObject, $aValues);
+        
+        if (isset($CNF['FIELD_AUTHOR']) && isset($this->aInputs[$CNF['FIELD_AUTHOR']])){
+            if (isset($aValues[$CNF['FIELD_AUTHOR']]) && $aValues[$CNF['FIELD_AUTHOR']])
+                $aValues[$CNF['FIELD_AUTHOR']] = $aValues[$CNF['FIELD_AUTHOR']];
+            else
+                $aValues[$CNF['FIELD_AUTHOR']] = bx_get_logged_profile_id();
+        }
+        
+        parent::initChecker ($aValues, $aSpecificValues);
+
+        foreach($aPrivacyFields as $sField => $sObject)
+            $this->_validatePrivacyField($sField, $sObject, $aValues);
+    }
+    
+    public function insert ($aValsToAdd = array(), $isIgnore = false)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        
+        $iAuthor = -1;
+        
+        if (isset($CNF['FIELD_AUTHOR'])){
+            if (empty($aValsToAdd[$CNF['FIELD_AUTHOR']]) && (!isset($this->aInputs[$CNF['FIELD_AUTHOR']]) || empty($this->getCleanValue($CNF['FIELD_AUTHOR'])))){
+                $aValsToAdd[$CNF['FIELD_AUTHOR']] = (isset($CNF['FIELD_ANONYMOUS']) && isset($this->aInputs[$CNF['FIELD_ANONYMOUS']]) && $this->getCleanValue($CNF['FIELD_ANONYMOUS']) ? -1 : 1) * bx_get_logged_profile_id ();
+                $iAuthor = $aValsToAdd[$CNF['FIELD_AUTHOR']] ;
+            }
+            else{
+                if(isset($this->aInputs[$CNF['FIELD_AUTHOR']]) && empty($this->getCleanValue($CNF['FIELD_AUTHOR']))){
+                     $aValsToAdd[$CNF['FIELD_AUTHOR']] = bx_get_logged_profile_id();
+                     $iAuthor = $aValsToAdd[$CNF['FIELD_AUTHOR']];
+                }
+                if(isset($this->aInputs[$CNF['FIELD_AUTHOR']]) && !empty($this->getCleanValue($CNF['FIELD_AUTHOR']))){
+                    $iAuthor = $this->getCleanValue($CNF['FIELD_AUTHOR']);
+                }
+            }
+        }
+            
+
+        if (isset($CNF['FIELD_ADDED']) && empty($aValsToAdd[$CNF['FIELD_ADDED']]) && empty($this->getCleanValue($CNF['FIELD_ADDED'])))
+            $aValsToAdd[$CNF['FIELD_ADDED']] = time();
+
+        if (isset($CNF['FIELD_CHANGED']) && empty($aValsToAdd[$CNF['FIELD_CHANGED']]) && empty($this->getCleanValue($CNF['FIELD_CHANGED'])))
+            $aValsToAdd[$CNF['FIELD_CHANGED']] = time();
+
+        if (CHECK_ACTION_RESULT_ALLOWED === $this->_oModule->checkAllowedSetThumb() && isset($CNF['FIELD_THUMB'])) {
+            $aThumb = isset($_POST[$CNF['FIELD_THUMB']]) ? bx_process_input ($_POST[$CNF['FIELD_THUMB']], BX_DATA_INT) : false;
+            $aValsToAdd[$CNF['FIELD_THUMB']] = 0;
+            if (!empty($aThumb) && is_array($aThumb) && ($iFileThumb = array_pop($aThumb)))
+                $aValsToAdd[$CNF['FIELD_THUMB']] = $iFileThumb;
+        }
+
+        if(!empty($CNF['OBJECT_METATAGS']))
+            $this->_processMetas($aValsToAdd);
+
+        if(isset($CNF['FIELD_STATUS']) && !empty($CNF['FIELDS_DELAYED_PROCESSING']) && !empty($CNF['OBJECT_VIDEOS_TRANSCODERS']['mp4'])) {
+            $oTranscoder = BxDolTranscoder::getObjectInstance($CNF['OBJECT_VIDEOS_TRANSCODERS']['mp4']);
+
+            $aFields = $CNF['FIELDS_DELAYED_PROCESSING'];
+            if(is_string($aFields))
+                $aFields = explode(',', $aFields);
+
+            foreach($aFields as $sField) {
+                $mixedFieldValues = $this->getCleanValue($sField);
+                if(empty($mixedFieldValues)) 
+                    continue;
+
+                $oStorage = BxDolStorage::getObjectInstance($this->aInputs[$sField]['storage_object']);
+                if(!$oStorage)
+                    continue;
+
+                if(!is_array($mixedFieldValues))
+                    $mixedFieldValues = array($mixedFieldValues);
+
+                foreach($mixedFieldValues as $mixedFieldValue) {
+                    $aInfo = $oStorage->getFile((int)$mixedFieldValue);
+                    if(empty($aInfo) || !is_array($aInfo))
+                        continue;
+
+                    if(strncmp($aInfo['mime_type'], 'video/', 6) != 0) 
+                        continue;
+
+                    if($oTranscoder->isFileReady((int)$mixedFieldValue))
+                        continue;
+
+                    $aValsToAdd[$CNF['FIELD_STATUS']] = 'awaiting';
+                    break 2;
+                }
+            }
+        }
+
+        if(!$this->_oModule->_isModerator() && !$this->_oModule->_oConfig->isAutoApproveEnabled())
+            $aValsToAdd[$CNF['FIELD_STATUS_ADMIN']] = BX_BASE_MOD_GENERAL_STATUS_PENDING;
+
+        $bMulticatEnabled = $this->_isMulticatEnabled();
+        if ($bMulticatEnabled)
+            $this->processMulticatBefore($CNF['FIELD_MULTICAT'], $aValsToAdd);
+
+        $iContentId = parent::insert ($aValsToAdd, $isIgnore);
+        
+        if(!empty($iContentId)) {
+            foreach($this->aInputs as $aInput) {
+                if ($aInput['type'] == 'nested_form') {
+                    if (is_array($aInput['ghost_template']) && !isset($aInput['ghost_template']['inputs'])) {
+                        foreach ($aInput['ghost_template'] as $oFormNested) {
+                            $iNestedContentId = $oFormNested->insert(array('content_id' => $iContentId));
+
+                            if ($aInput['rateable']) {
+                                BxDolFormQuery::addFormField($this->id, $aInput['name'], $iContentId, $iAuthor, $this->_oModule->getName(), $iNestedContentId);
+                            }
+                        }
+                    }
+                }
+            }
+            
+            if ($bMulticatEnabled)
+                $this->processMulticatAfter($CNF['FIELD_MULTICAT'], $iContentId);
+        }
+        
+        
+        foreach($this->aInputs as $aInput) {
+            if (isset($aInput['rateable']) && $aInput['rateable'] && $aInput['type'] != 'nested_form'){
+                BxDolFormQuery::addFormField($this->id, $aInput['name'], $iContentId, $iAuthor, $this->_oModule->getName());
+            }
+        }
+        
+        return $iContentId;
+    }
+
+    function update ($iContentId, $aValsToAdd = array(), &$aTrackTextFieldsChanges = null)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $aContentInfo = $this->_oModule->_oDb->getContentInfoById($iContentId);
+        $iAuthor = -1;
+        
+        $bFieldAuthor = isset($CNF['FIELD_AUTHOR']);
+        if($bFieldAuthor && isset($aContentInfo[$CNF['FIELD_AUTHOR']])){
+            $iAuthor = $aContentInfo[$CNF['FIELD_AUTHOR']];
+        }
+
+        if($bFieldAuthor) {
+            if(isset($CNF['FIELD_ANONYMOUS']) && isset($this->aInputs[$CNF['FIELD_ANONYMOUS']])){
+                $aValsToAdd[$CNF['FIELD_AUTHOR']] = ($this->getCleanValue($CNF['FIELD_ANONYMOUS']) ? -1 : 1) * abs($aContentInfo[$CNF['FIELD_AUTHOR']]);
+                $iAuthor = $aValsToAdd[$CNF['FIELD_AUTHOR']];
+            }
+            else {
+                if(isset($this->aInputs[$CNF['FIELD_AUTHOR']]) && empty($this->getCleanValue($CNF['FIELD_AUTHOR']))){
+                    $aValsToAdd[$CNF['FIELD_AUTHOR']] = bx_get_logged_profile_id();
+                    $iAuthor = $aValsToAdd[$CNF['FIELD_AUTHOR']];
+                }
+
+                if(isset($this->aInputs[$CNF['FIELD_AUTHOR']]) && !empty($this->getCleanValue($CNF['FIELD_AUTHOR']))){
+                    $iAuthor = $this->getCleanValue($CNF['FIELD_AUTHOR']);
+                }
+            }
+        }
+
+        if(isset($CNF['FIELD_CHANGED']) && empty($aValsToAdd[$CNF['FIELD_CHANGED']]) && empty($this->getCleanValue($CNF['FIELD_CHANGED'])))
+            $aValsToAdd[$CNF['FIELD_CHANGED']] = time();
+
+        if(CHECK_ACTION_RESULT_ALLOWED === $this->_oModule->checkAllowedSetThumb($iContentId) && isset($CNF['FIELD_THUMB'])) {
+            $aThumb = bx_process_input (bx_get($CNF['FIELD_THUMB']), BX_DATA_INT);
+            $aValsToAdd[$CNF['FIELD_THUMB']] = 0;
+            if (!empty($aThumb) && is_array($aThumb) && ($iFileThumb = array_pop($aThumb)))
+                $aValsToAdd[$CNF['FIELD_THUMB']] = $iFileThumb;
+        }
+
+        if(!empty($CNF['OBJECT_METATAGS']))
+            $this->_processMetas($aValsToAdd);
+
+        if(isset($CNF['FIELD_STATUS']) && isset($aContentInfo[$CNF['FIELD_STATUS']]) && $aContentInfo[$CNF['FIELD_STATUS']] == 'failed')
+            $aValsToAdd[$CNF['FIELD_STATUS']] = 'active';
+
+        $bMulticatEnabled = $this->_isMulticatEnabled();
+        if ($bMulticatEnabled)
+            $this->processMulticatBefore($CNF['FIELD_MULTICAT'], $aValsToAdd);
+
+        $mixedResult = parent::update($iContentId, $aValsToAdd, $aTrackTextFieldsChanges);
+
+        foreach($this->aInputs as $aInput) {
+            if ($aInput['type'] == 'nested_form'){
+                if (is_array($aInput['ghost_template']) && !isset($aInput['ghost_template']['inputs'])) {
+                    foreach ($aInput['ghost_template'] as $oFormNested) {
+                        $aSpecificValues = $oFormNested->getSpecificValues();
+                        $iNestedContentId = $oFormNested->getSubmittedValue($aInput['name'], BX_DOL_FORM_METHOD_SPECIFIC, $aSpecificValues);
+
+                        if (empty($iNestedContentId)){
+                            $iNestedContentId = $oFormNested->insert(array('content_id' => $iContentId));
+                            if ($aInput['rateable']){
+                                BxDolFormQuery::addFormField($this->id, $aInput['name'], $iContentId, $iAuthor, $this->_oModule->getName(), $iNestedContentId);
+                            }
+                        }
+                        else{
+                            $oFormNested->update($iNestedContentId, array('content_id' => $iContentId));
+                        }
+                    }
+                }
+            }
+        }
+
+        if ($bMulticatEnabled)
+            $this->processMulticatAfter($CNF['FIELD_MULTICAT'], $iContentId);
+        
+		foreach($this->aInputs as $aInput) {
+            if (isset($aInput['rateable']) && $aInput['rateable'] && $aInput['type'] != 'nested_form'){
+                BxDolFormQuery::addFormField($this->id, $aInput['name'], $iContentId, $iAuthor, $this->_oModule->getName());
+            }
+        }
+		
+        if($mixedResult !== false)
+            $this->_processTrackFields($iContentId);
+
+        return $mixedResult;
+    }
+    
+    function getHtmlEditorQueryParams($aInput)
+    {
+        $aQueryParams = parent::getHtmlEditorQueryParams($aInput);
+        if (isset($this->MODULE)){
+            $aQueryParams['m'] = $this->MODULE;
+        }
+        if (isset($this->_iContentId) && $this->_iContentId){
+            $aQueryParams['cid'] = $this->_iContentId;
+        }
+        $aQueryParams['fi'] = '';
+        
+        bx_alert('system', 'editor_query_params', 0, 0, array(
+            'form' => $this,
+            'override_result' => &$aQueryParams
+        ));
+        
+        return $aQueryParams;
+    }
+
+    protected function _associalFileWithContent($oStorage, $iFileId, $iProfileId, $iContentId, $sPictureField = '')
+    {
+        $oStorage->updateGhostsContentId ($iFileId, $iProfileId, $iContentId, $this->_isAdmin($iContentId));
+    }
+
+    protected function _getPhotoGhostTmplVars($aContentInfo = array())
+    {
+    	$CNF = &$this->_oModule->_oConfig->CNF;
+
+    	return array (
+			'name' => $this->aInputs[$CNF['FIELD_PHOTO']]['name'],
+			'content_id' => (int)$this->aInputs[$CNF['FIELD_PHOTO']]['content_id'],
+			'editor_id' => isset($CNF['FIELD_TEXT_ID']) ? $CNF['FIELD_TEXT_ID'] : '',
+            'thumb_id' => isset($CNF['FIELD_THUMB']) && isset($aContentInfo[$CNF['FIELD_THUMB']]) ? $aContentInfo[$CNF['FIELD_THUMB']] : 0,
+            'name_thumb' => isset($CNF['FIELD_THUMB']) ? $CNF['FIELD_THUMB'] : '',
+			'bx_if:set_thumb' => array (
+				'condition' => CHECK_ACTION_RESULT_ALLOWED === $this->_oModule->checkAllowedSetThumb($this->aInputs[$CNF['FIELD_PHOTO']]['content_id']),
+				'content' => array (
+					'name_thumb' => isset($CNF['FIELD_THUMB']) ? $CNF['FIELD_THUMB'] : '',
+    				'txt_pict_use_as_thumb' => _t(!empty($CNF['T']['txt_pict_use_as_thumb']) ? $CNF['T']['txt_pict_use_as_thumb'] : '_sys_txt_form_entry_input_picture_use_as_thumb')
+				),
+			),
+		);
+    }
+    
+    function delete ($iContentId, $aContentInfo = array())
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        // delete associated files
+
+        $a = array('OBJECT_STORAGE', 'OBJECT_STORAGE_FILES', 'OBJECT_STORAGE_PHOTOS', 'OBJECT_STORAGE_VIDEOS', 'OBJECT_STORAGE_SOUNDS');
+        foreach ($a as $k) {
+            if (!empty($CNF[$k])) {
+                $oStorage = BxDolStorage::getObjectInstance($CNF[$k]);
+                if ($oStorage)
+                    $oStorage->queueFilesForDeletionFromGhosts($aContentInfo[$CNF['FIELD_AUTHOR']], $iContentId);
+            }
+        }
+
+        // delete associated objects data
+
+        if (!empty($CNF['OBJECT_VIEWS'])) {
+            $o = BxDolView::getObjectInstance($CNF['OBJECT_VIEWS'], $iContentId);
+            if ($o) $o->onObjectDelete();
+        }
+
+        if (!empty($CNF['OBJECT_VOTES'])) {
+            $o = BxDolVote::getObjectInstance($CNF['OBJECT_VOTES'], $iContentId);
+            if ($o) $o->onObjectDelete();
+        }
+
+        if (!empty($CNF['OBJECT_SCORES'])) {
+            $o = BxDolScore::getObjectInstance($CNF['OBJECT_SCORES'], $iContentId);
+            if ($o) $o->onObjectDelete();
+        }
+
+        if (!empty($CNF['OBJECT_COMMENTS'])) {
+            $o = BxDolCmts::getObjectInstance($CNF['OBJECT_COMMENTS'], $iContentId);
+            if ($o) $o->onObjectDelete();
+        }
+
+        if (!empty($CNF['OBJECT_REPORTS'])) {
+            $o = BxDolReport::getObjectInstance($CNF['OBJECT_REPORTS'], $iContentId);
+            if ($o) $o->onObjectDelete();
+        }
+
+        if (!empty($CNF['OBJECT_METATAGS'])) {
+            $o = BxDolMetatags::getObjectInstance($CNF['OBJECT_METATAGS']);
+            if ($o) $o->onDeleteContent($iContentId);
+        }
+
+        // delete SEO links
+
+        BxDolPage::deleteSeoLink ($this->_oModule->getName(), $this->_oModule->getName(), $iContentId);
+
+        // delete db record
+        
+        BxDolFormQuery::removeFormField($this->id, $iContentId);
+
+        return parent::delete($iContentId);
+    }
+
+    protected function _isAdmin ($iContentId = 0)
+    {
+        return $this->_oModule->_isModerator();
+    }
+
+    public function processFiles ($sFieldFile, $iContentId = 0, $isAssociateWithContent = false)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if (!isset($this->aInputs[$sFieldFile]))
+            return true;
+
+        $mixedFileIds = $this->getCleanValue($sFieldFile);
+        if(!$mixedFileIds)
+            return true;
+
+        $oStorage = BxDolStorage::getObjectInstance($this->aInputs[$sFieldFile]['storage_object']);
+        if (!$oStorage)
+            return false;
+
+        $iProfileId = $this->getContentOwnerProfileId($iContentId);
+
+        $aGhostFiles = $oStorage->getGhosts ($iProfileId, $isAssociateWithContent ? 0 : $iContentId, true, $this->_isAdmin($iContentId));
+        if (!$aGhostFiles)
+            return true;
+
+        foreach ($aGhostFiles as $aFile) {
+            if (is_array($mixedFileIds) && !in_array($aFile['id'], $mixedFileIds))
+                continue;
+
+            if ($aFile['private'])
+                $oStorage->setFilePrivate ($aFile['id'], 1);
+
+            if ($iContentId)
+                $this->_associalFileWithContent($oStorage, $aFile['id'], $iProfileId, $iContentId, $sFieldFile);
+        }
+
+        return true;
+    }
+
+    function _deleteFile ($iFileId, $sStorage = '')
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if (!$iFileId)
+            return true;
+
+		$sStorage = !empty($sStorage) ? $sStorage : $CNF['OBJECT_STORAGE'];
+		$oStorage = BxDolStorage::getObjectInstance($sStorage);
+        if (!$oStorage)
+            return false;
+
+        if (!$oStorage->getFile($iFileId))
+            return true;
+
+        $iProfileId = bx_get_logged_profile_id();
+        return $oStorage->deleteFile($iFileId, $iProfileId);
+    }
+
+    function addCssJs ()
+    {
+        if ((!isset($this->aParams['view_mode']) || !$this->aParams['view_mode']) && !self::$_isCssJsGeneralModuleAdded) {
+            $this->_oModule->_oTemplate->addCss('forms.css');
+            $this->_oModule->_oTemplate->addJs('modules/base/general/js/|forms.js');
+            self::$_isCssJsGeneralModuleAdded = true;
+        }
+
+        return parent::addCssJs ();
+    }
+    
+    function genViewRowValue(&$aInput)
+    {
+        $s = parent::genViewRowValue($aInput);
+
+        if ($this->_oMetatagsObject && in_array($aInput['name'], $this->_aMetatagsFieldsWithKeywords) && $s)
+            $s = $this->_oMetatagsObject->metaParse($this->_oMetatagsContentId, $s);
+
+        return $s;
+    }
+    
+    function genViewRowWrapped(&$aInput)
+    {
+        $sResult =  parent::genViewRowWrapped($aInput);
+        if (!$aInput['rateable']){
+            return $sResult;
+        }
+        
+        $sValue = $this->genViewRowValue($aInput);
+        if (null === $sValue)
+            return '';
+        
+        // process rateable fields
+        $iId = BxDolFormQuery::getFormField($this->id, $aInput['name'], $this->_iContentId);
+        $sVote = '';
+        $oVote = BxDolVote::getObjectInstance($aInput['rateable'], $iId, true, BxDolTemplate::getInstance());
+        if ($oVote){
+            $sVote = $oVote->getCounter(array('show_counter_empty' => true, 'show_counter' => true, 'show_counter_style' => 'simple', 'dynamic_mode' => $this->_bDynamicMode));
+			$sVoteBtn = $oVote->getElementInline(array('show_counter_empty' => true, 'show_counter' => false, 'show_counter_style' => 'simple', 'dynamic_mode' => $this->_bDynamicMode));
+			return $this->oTemplate->parseHtmlByName('form_view_rateable_row.html', array(
+				'value' => $sResult,
+				'rate' => $sVote,
+				'rate_btn' => $sVoteBtn
+			));
+        }
+		return $sResult;
+    }
+
+    function setMetatagsKeywordsData($iId, $a, $o)
+    {
+        $this->_oMetatagsContentId = $iId;
+        $this->_aMetatagsFieldsWithKeywords = $a;
+        $this->_oMetatagsObject = $o;
+    }
+
+    function getContentOwnerProfileId ($iContentId)
+    {
+        return $this->_oModule->serviceGetContentOwnerProfileId($iContentId);
+    }
+
+    protected function _processTrackFields($mixedContent)
+    {
+        if(empty($mixedContent) || empty($this->_aTrackFieldsChanges))
+            return;
+
+        if(!is_array($mixedContent)) {
+            $mixedContent = $this->_oModule->_oDb->getContentInfoById((int)$mixedContent);
+            if(empty($mixedContent) || !is_array($mixedContent))
+                return;
+        }
+
+        foreach($this->_aTrackFieldsChanges as $sField => $aValues)
+            if($mixedContent[$sField] == $aValues['old'])
+                $this->_aTrackFieldsChanges[$sField] = false;
+            else
+                $this->_aTrackFieldsChanges[$sField]['new'] = $mixedContent[$sField];
+    }
+
+    protected function _processMetas(&$aValsToAdd)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $sKey = 'FIELD_LABELS';
+        if(isset($CNF[$sKey]) && isset($this->aInputs[$CNF[$sKey]]) && empty($aValsToAdd[$CNF[$sKey]])) {
+            $aLabels = $this->getCleanValue($CNF[$sKey]);
+            if(!empty($aLabels) && is_array($aLabels))
+                $aValsToAdd[$CNF[$sKey]] = serialize($aLabels);
+            else
+                $aValsToAdd[$CNF[$sKey]] = '';
+        }
+
+        $sKey1 = 'FIELD_LOCATION';
+        $sKey2 = 'FIELD_LOCATION_PREFIX';
+        if(isset($CNF[$sKey1]) && isset($CNF[$sKey2]) && isset($this->aInputs[$CNF[$sKey1]]) && empty($aValsToAdd[$CNF[$sKey1]])) {               
+            $aLocation = BxDolMetatags::locationsRetrieveFromForm($CNF[$sKey2], $this);
+            if(!empty($aLocation) && is_array($aLocation))
+                $aValsToAdd[$CNF[$sKey1]] = serialize(BxDolMetatags::locationsParseComponents($aLocation));
+        }
+    }
+
+    protected function _getPrivacyFields($aKeysF2O = array())
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        if(isset($CNF['PRIVACY_FIELD_TO_OBJECT']) && is_array($CNF['PRIVACY_FIELD_TO_OBJECT']))
+            return $CNF['PRIVACY_FIELD_TO_OBJECT'];
+
+        if(empty($aKeysF2O))
+            $aKeysF2O = array('FIELD_ALLOW_VIEW_TO' => 'OBJECT_PRIVACY_VIEW');
+
+        $aResult = array();
+        foreach($aKeysF2O as $sKeyField => $sKeyObject) {
+            if(!isset($CNF[$sKeyField]) || !isset($this->aInputs[$CNF[$sKeyField]]) || !isset($CNF[$sKeyObject]))
+                continue;
+
+            $aResult[$CNF[$sKeyField]] = $CNF[$sKeyObject];
+        }
+
+        return $aResult;
+    }
+
+    function _addCssJsPrivacyField($sField, $sPrivacyObject, $bDynamicMode = false)
+    {
+        $oPrivacy = BxDolPrivacy::getObjectInstance($sPrivacyObject);
+        if(!$oPrivacy) 
+            return;
+
+        $this->aInputs[$sField]['content'] = $oPrivacy->addCssJs($bDynamicMode) . $this->aInputs[$sField]['content'];
+    }
+
+    protected function _preparePrivacyField($sField, $sPrivacyObject)
+    {
+        $oPrivacy = BxDolPrivacy::getObjectInstance($sPrivacyObject);
+        if(!$oPrivacy) 
+            return;
+
+        $aSave = array('db' => array('pass' => 'Xss'));
+        array_walk($this->aInputs[$sField], function ($a, $k, $aSave) {
+            if (in_array($k, array('info', 'caption', 'value')))
+                $aSave[0][$k] = $a;
+        }, array(&$aSave));
+
+        $aGroupChooser = $oPrivacy->getGroupChooser($sPrivacyObject, 0, array(
+            'object' => $this->aParams['object'],
+            'display' => $this->aParams['display']
+        ));
+
+        $this->aInputs[$sField] = array_merge($this->aInputs[$sField], $aGroupChooser, $aSave);
+    }
+
+    protected function _preloadPrivacyField($sField, $sPrivacyObject, $aValues)
+    {
+        $CNF = &$this->_oModule->_oConfig->CNF;
+
+        $oPrivacy = BxDolPrivacy::getObjectInstance($sPrivacyObject);
+        if(!$oPrivacy) 
+            return;
+
+        $iContentId = !empty($aValues[$CNF['FIELD_ID']]) ? (int)$aValues[$CNF['FIELD_ID']] : 0;
+        $iProfileId = !empty($iContentId) ? (int)$this->getContentOwnerProfileId($iContentId) : bx_get_logged_profile_id();
+        $iGroupId = !empty($aValues[$sField]) ? $aValues[$sField] : 0;
+
+        if(!isset($this->aInputs[$sField]['content']))
+            $this->aInputs[$sField]['content'] = '';
+
+        $this->aInputs[$sField]['content'] .= $oPrivacy->initGroupChooser($sPrivacyObject, $iProfileId, array(
+            'content_id' => $iContentId,
+            'group_id' => $iGroupId,
+            'html_ids' => array(
+                'form' => $this->getId()
+            )
+        ));
+    }
+    
+    protected function _validatePrivacyField($sField, $sPrivacyObject, $aValues)
+    {
+        $mixedValue = $this->aInputs[$sField]['value'];
+
+        $bValue = false;
+        foreach($this->aInputs[$sField]['values'] as $aValue)
+            if(isset($aValue['key']) && $aValue['key'] == $mixedValue) {
+                $bValue = true;
+                break;
+            }
+
+        if(!$bValue) {
+            $sTitle = '';
+            if(is_numeric($mixedValue) && (int)$mixedValue < 0 && ($oContext = BxDolProfile::getInstance(abs((int)$mixedValue))) !== false)
+                $sTitle = $oContext->getDisplayName();
+            else
+                $sTitle = _t('_sys_ps_group_title_unknown');
+
+            $this->aInputs[$sField]['values'] = array_merge(array(
+                array('key' => $mixedValue, 'value' => $sTitle),
+                array('key' => '', 'value' => '----', 'attrs' => array('disabled' => 'disabled'))
+            ), $this->aInputs[$sField]['values']);
+        }
+    }
+
+    /**
+     * 
+     * MultiCategories related methods. 
+     * 
+     */
+    
+    protected function processMulticatBefore($sFieldName, &$aValsToAdd){
+        if (isset($this->aInputs[$sFieldName])){
+            $this->aInputs[$sFieldName]['value'] = array_unique(array_filter($this->aInputs[$sFieldName]['value'], function($sTmp){
+               return trim($sTmp);
+            }));  
+            $aValsToAdd[$sFieldName] = implode(',', $this->aInputs[$sFieldName]['value']);
+        }
+    }
+    
+    protected function processMulticatAfter($sFieldName, $iContentId){
+        $CNF = &$this->_oModule->_oConfig->CNF;
+        $bAutoActivation = (isset($CNF['PARAM_MULTICAT_AUTO_ACTIVATION_FOR_CATEGORIES']) && getParam($CNF['PARAM_MULTICAT_AUTO_ACTIVATION_FOR_CATEGORIES']) == 'on') ? true : false;
+		$oCategories = BxDolCategories::getInstance();
+        if (isset($this->aInputs[$sFieldName])){
+            $oCategories->delete($this->_oModule->getName(), $iContentId);
+            foreach($this->aInputs[$sFieldName]['value'] as  $sValue) {
+                $oCategories->add($this->_oModule->getName(), bx_get_logged_profile_id(), $sValue, $iContentId, $bAutoActivation);
+            }
+        }
+    }
+    
+    protected function genCustomViewRowValueMulticat(&$aInput)
+    {
+		$oCategories = BxDolCategories::getInstance();
+        $aValues = $oCategories->getData(array('type' => 'by_module_and_object', 'module' => $this->_oModule->getName(), 'object_id' => (!empty($aInput['content_id']) ? (int)$aInput['content_id'] : 0 )));
+        if (count($aValues) > 0){
+            $aVars = array('bx_repeat:cats' => array());
+            foreach ($aValues as  $sValue) {
+                $aVars['bx_repeat:cats'][] = array(
+                    'url' => $oCategories->getUrl($this->_oModule->getName(), $sValue),
+                    'name' => _t($sValue),
+                    'bx_if:more' => array(
+                        'condition' => $sValue === end($aValues) ? false : true,
+                        'content' => array('1')
+                    ),
+                );
+            }
+            
+            if (!$aVars['bx_repeat:cats'])
+                return '';
+
+            return $this->_oModule->_oTemplate->parseHtmlByName('category_list_inline.html', $aVars);
+        }
+        return '';
+    }
+    
+    protected function genCustomInputMulticat(&$aInput)
+    {
+        $sJsObject = $this->_oModule->_oConfig->getJsObject('categories');
+		$aValuesForSelect = BxDolCategories::getInstance()->getData(array('type' => 'by_module_and_author', 'module' => $this->_oModule->getName(), 'author' => bx_get_logged_profile_id()));
+        
+        $aSelectedItems = array();
+        if (isset($aInput['value']) && is_array($aInput['value']))
+		    $aInput['value'] = array_filter($aInput['value']);
+        
+        if(!empty($aInput['value'])) {
+            if (!is_array($aInput['value']))
+                $aValues = BxDolCategories::getInstance()->getData(array('type' => 'by_module_and_object', 'module' => $this->_oModule->getName(), 'object_id' => (!empty($aInput['content_id']) ? (int)$aInput['content_id'] : 0 )));
+            else
+                $aValues = $aInput['value'];
+            
+            $aValues = array_filter($aValues);
+            foreach($aValues as $sValue) {
+                if (!array_key_exists($sValue, $aValuesForSelect)){
+                    $aValuesForSelect[$sValue] = array('key' => $sValue, 'value' => $sValue);
+                }
+            }
+            foreach($aValues as $sValue) {
+                $sInput = $this->genCustomInputMulticatSelect($aInput, $aValuesForSelect, $sValue);
+                $aSelectedItems[] = array('js_object' => $sJsObject, 'select_cat' => $sInput);
+            }
+        }
+        else{
+            $aSelectedItems = array(
+                array('js_object' => $sJsObject, 'select_cat' => $this->genCustomInputMulticatSelect($aInput, $aValuesForSelect))
+            );
+        }
+        return $this->_oModule->_oTemplate->parseHtmlByName('form_categories.html', array(
+            'bx_repeat:items' => $aSelectedItems,
+            'js_object' => $sJsObject, 
+            'select_cat' => $this->genCustomInputMulticatSelect($aInput, $aValuesForSelect, -1),
+            'input_cat' => $this->genCustomInputMulticatInput($aInput),
+            'js_object' => $sJsObject,
+            'btn_add' => $this->genCustomInputMulticatButton($aInput),
+            'btn_add_new' => $this->genCustomInputMulticatButtonNew($aInput)
+        ));
+    }
+    
+    protected function genCustomInputMulticatSelect($aInput, $aValues, $mixedValue = '')
+    {
+        foreach($aValues as $sKey => $aValue) {
+            $aValues[$sKey]['value'] = _t($aValue['key']);
+        }
+        $aValues = array_merge(array('1' => array('key' => false, 'value' =>  _t('_sys_please_select'))), $aValues);
+        
+        $aInput['type'] = 'select';
+        $aInput['name'] .= '[]';
+        $aInput['value'] = $mixedValue;
+        $aInput['values'] = $aValues;
+        return $this->genInput($aInput);
+    }
+    
+    protected function genCustomInputMulticatInput($aInput)
+    {
+        $aInput['type'] = 'text';
+        $aInput['name'] .= '[]';
+        $aInput['value'] = '';
+        return $this->genInput($aInput);
+    }
+    
+    protected function genCustomInputMulticatButton($aInput)
+    {
+        $sName = $aInput['name'];
+        $aInput['type'] = 'button';
+        $aInput['name'] .= '_add';
+        $aInput['value'] = _t('_sys_txt_categories_button_caption_add');
+        $aInput['attrs']['class'] = 'bx-def-margin-right bx-def-margin-sec-top';
+        $aInput['attrs']['onclick'] = $this->_oModule->_oConfig->getJsObject('categories') . ".categoryAdd(this, '" . $sName . "');";
+        return $this->genInputButton($aInput);
+    }
+    
+    protected function genCustomInputMulticatButtonNew($aInput)
+    {
+        $sName = $aInput['name'];
+        $aInput['type'] = 'button';
+        $aInput['name'] .= '_add';
+        $aInput['value'] = _t('_sys_txt_categories_button_caption_addnew');
+        $aInput['attrs']['class'] = 'bx-def-margin-sec-top';
+        $aInput['attrs']['onclick'] = $this->_oModule->_oConfig->getJsObject('categories') . ".categoryAddNew(this, '" . $sName . "');";
+        return $this->genInputButton($aInput);
+    }
+    
+    protected function genCustomInputAuthor ($aInput)
+    {
+        if(empty($aInput['custom']) || !is_array($aInput['custom']))
+            $aInput['custom'] = array();
+        $aInput['custom']['only_once'] = 1;
+        
+        $aInput['ajax_get_suggestions'] = BX_DOL_URL_ROOT . "modules/?r=" . $this->_oModule->_oConfig->getUri() . "/ajax_get_profiles";
+        return $this->genCustomInputUsernamesSuggestions($aInput);
+    }
+    
+    protected function _isMulticatEnabled(){
+        $CNF = $this->_oModule->_oConfig->CNF;
+        return isset($CNF['PARAM_MULTICAT_ENABLED']) && $CNF['PARAM_MULTICAT_ENABLED'] === true && isset($CNF['FIELD_MULTICAT']);
+    }
+}
+
+/** @} */

+ 1 - 1
upgrade/files/13.0.0.B1-13.0.0.B2/files/plugins/autoload.php

@@ -4,4 +4,4 @@
 
 require_once __DIR__ . '/composer/autoload_real.php';
 
-return ComposerAutoloaderInite5f3ca458ceaf6c532affea33489186b::getLoader();
+return ComposerAutoloaderInit8c0999e551e6e1761860c4d06e368d0c::getLoader();

+ 0 - 15
upgrade/files/13.0.0.B1-13.0.0.B2/files/plugins/composer/autoload_files.php

@@ -1,15 +0,0 @@
-<?php
-
-// autoload_files.php @generated by Composer
-
-$vendorDir = dirname(dirname(__FILE__));
-$baseDir = dirname($vendorDir);
-
-return array(
-    '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
-    '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
-    'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
-    '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
-    '2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
-    'c65d09b6820da036953a371c8c73a9b1' => $vendorDir . '/facebook/graph-sdk/src/Facebook/polyfills.php',
-);

+ 7 - 7
upgrade/files/13.0.0.B1-13.0.0.B2/files/plugins/composer/autoload_real.php

@@ -2,7 +2,7 @@
 
 // autoload_real.php @generated by Composer
 
-class ComposerAutoloaderInite5f3ca458ceaf6c532affea33489186b
+class ComposerAutoloaderInit8c0999e551e6e1761860c4d06e368d0c
 {
     private static $loader;
 
@@ -24,15 +24,15 @@ class ComposerAutoloaderInite5f3ca458ceaf6c532affea33489186b
 
         require __DIR__ . '/platform_check.php';
 
-        spl_autoload_register(array('ComposerAutoloaderInite5f3ca458ceaf6c532affea33489186b', 'loadClassLoader'), true, true);
+        spl_autoload_register(array('ComposerAutoloaderInit8c0999e551e6e1761860c4d06e368d0c', 'loadClassLoader'), true, true);
         self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
-        spl_autoload_unregister(array('ComposerAutoloaderInite5f3ca458ceaf6c532affea33489186b', 'loadClassLoader'));
+        spl_autoload_unregister(array('ComposerAutoloaderInit8c0999e551e6e1761860c4d06e368d0c', 'loadClassLoader'));
 
         $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
         if ($useStaticLoader) {
             require __DIR__ . '/autoload_static.php';
 
-            call_user_func(\Composer\Autoload\ComposerStaticInite5f3ca458ceaf6c532affea33489186b::getInitializer($loader));
+            call_user_func(\Composer\Autoload\ComposerStaticInit8c0999e551e6e1761860c4d06e368d0c::getInitializer($loader));
         } else {
             $map = require __DIR__ . '/autoload_namespaces.php';
             foreach ($map as $namespace => $path) {
@@ -53,19 +53,19 @@ class ComposerAutoloaderInite5f3ca458ceaf6c532affea33489186b
         $loader->register(true);
 
         if ($useStaticLoader) {
-            $includeFiles = Composer\Autoload\ComposerStaticInite5f3ca458ceaf6c532affea33489186b::$files;
+            $includeFiles = Composer\Autoload\ComposerStaticInit8c0999e551e6e1761860c4d06e368d0c::$files;
         } else {
             $includeFiles = require __DIR__ . '/autoload_files.php';
         }
         foreach ($includeFiles as $fileIdentifier => $file) {
-            composerRequiree5f3ca458ceaf6c532affea33489186b($fileIdentifier, $file);
+            composerRequire8c0999e551e6e1761860c4d06e368d0c($fileIdentifier, $file);
         }
 
         return $loader;
     }
 }
 
-function composerRequiree5f3ca458ceaf6c532affea33489186b($fileIdentifier, $file)
+function composerRequire8c0999e551e6e1761860c4d06e368d0c($fileIdentifier, $file)
 {
     if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
         require $file;

+ 6 - 6
upgrade/files/13.0.0.B1-13.0.0.B2/files/plugins/composer/autoload_static.php

@@ -4,15 +4,15 @@
 
 namespace Composer\Autoload;
 
-class ComposerStaticInite5f3ca458ceaf6c532affea33489186b
+class ComposerStaticInit8c0999e551e6e1761860c4d06e368d0c
 {
     public static $files = array (
         '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
         '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
         'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
         '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
-        '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
         'c65d09b6820da036953a371c8c73a9b1' => __DIR__ . '/..' . '/facebook/graph-sdk/src/Facebook/polyfills.php',
+        '2cffec82183ee1cea088009cef9a6fc3' => __DIR__ . '/..' . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
     );
 
     public static $prefixLengthsPsr4 = array (
@@ -214,10 +214,10 @@ class ComposerStaticInite5f3ca458ceaf6c532affea33489186b
     public static function getInitializer(ClassLoader $loader)
     {
         return \Closure::bind(function () use ($loader) {
-            $loader->prefixLengthsPsr4 = ComposerStaticInite5f3ca458ceaf6c532affea33489186b::$prefixLengthsPsr4;
-            $loader->prefixDirsPsr4 = ComposerStaticInite5f3ca458ceaf6c532affea33489186b::$prefixDirsPsr4;
-            $loader->prefixesPsr0 = ComposerStaticInite5f3ca458ceaf6c532affea33489186b::$prefixesPsr0;
-            $loader->classMap = ComposerStaticInite5f3ca458ceaf6c532affea33489186b::$classMap;
+            $loader->prefixLengthsPsr4 = ComposerStaticInit8c0999e551e6e1761860c4d06e368d0c::$prefixLengthsPsr4;
+            $loader->prefixDirsPsr4 = ComposerStaticInit8c0999e551e6e1761860c4d06e368d0c::$prefixDirsPsr4;
+            $loader->prefixesPsr0 = ComposerStaticInit8c0999e551e6e1761860c4d06e368d0c::$prefixesPsr0;
+            $loader->classMap = ComposerStaticInit8c0999e551e6e1761860c4d06e368d0c::$classMap;
 
         }, null, ClassLoader::class);
     }

+ 2 - 2
upgrade/files/13.0.0.B1-13.0.0.B2/files/plugins/composer/installed.php

@@ -5,7 +5,7 @@
         'type' => 'library',
         'install_path' => __DIR__ . '/../../',
         'aliases' => array(),
-        'reference' => 'cd93af16a1e273b377937e63f3987d34a9722148',
+        'reference' => 'fdccd47f1174e7f00dd99005a809a664730fd00e',
         'name' => 'unaio/una',
         'dev' => true,
     ),
@@ -332,7 +332,7 @@
             'type' => 'library',
             'install_path' => __DIR__ . '/../../',
             'aliases' => array(),
-            'reference' => 'cd93af16a1e273b377937e63f3987d34a9722148',
+            'reference' => 'fdccd47f1174e7f00dd99005a809a664730fd00e',
             'dev_requirement' => false,
         ),
         'wikimedia/less.php' => array(

+ 3 - 0
upgrade/files/13.0.0.B1-13.0.0.B2/files/template/css/forms.css

@@ -674,3 +674,6 @@
     display:table-cell;
     vertical-align:middle;
 }
+.combodate {
+    display: flex;
+}

+ 188 - 0
upgrade/files/13.0.0.B1-13.0.0.B2/files/template/scripts/BxBaseCmtsForm.php

@@ -0,0 +1,188 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaBaseView UNA Base Representation Classes
+ * @{
+ */
+
+class BxBaseCmtsForm extends BxTemplFormView
+{
+    protected static $_sAttributeMaskId;
+    protected static $_sAttributeMaskName;
+
+    protected $_oObject;
+
+    protected $_sGhostTemplateImage;
+
+    public function __construct($aInfo, $oTemplate)
+    {
+        parent::__construct($aInfo, $oTemplate);
+
+        if(empty(self::$_sAttributeMaskId))
+            self::$_sAttributeMaskId = $this->aFormAttrs['id'];
+
+        if(empty(self::$_sAttributeMaskName))
+            self::$_sAttributeMaskName = $this->aFormAttrs['name'];
+
+        $this->_oObject = null;
+
+        $this->_sGhostTemplateImage = 'comments_uploader_nfw.html';
+
+    	if(isset($this->aInputs['cmt_image'])) {
+            $this->aInputs['cmt_image']['storage_object'] = 'sys_cmts_images';
+            $this->aInputs['cmt_image']['images_transcoder'] = 'sys_cmts_images_preview';
+            $this->aInputs['cmt_image']['uploaders'] = !empty($this->aInputs['cmt_image']['value']) ? unserialize($this->aInputs['cmt_image']['value']) : array('sys_cmts_html5');
+            $this->aInputs['cmt_image']['upload_buttons_titles'] = array('Simple' => 'camera', 'HTML5' => 'camera');
+            $this->aInputs['cmt_image']['storage_private'] = 0;
+            $this->aInputs['cmt_image']['multiple'] = true;
+            $this->aInputs['cmt_image']['content_id'] = 0;
+            $this->aInputs['cmt_image']['ghost_template'] = '';
+        }
+    }
+    
+    function getHtmlEditorQueryParams($aInput)
+    {
+        $aQueryParams = parent::getHtmlEditorQueryParams($aInput);
+        if (isset($this->aInputs['id'])){
+            $aQueryParams['cid'] = $this->aInputs['id']['value'];
+        }
+        $aQueryParams['m'] = 'sys_cmts';
+        $aQueryParams['fi'] = '';
+        
+        bx_alert('system', 'editor_query_params', 0, 0, array(
+            'form' => $this,
+            'override_result' => &$aQueryParams
+        ));
+        
+        return $aQueryParams;
+    }
+    
+    public function getAttributeMask($sAttribute)
+    {
+        $sName = '_sAttributeMask' . bx_gen_method_name($sAttribute);
+        return isset(self::$$sName) ? self::$$sName : '';
+    }
+
+    public function getStorageObjectName()
+    {
+        return isset($this->aInputs['cmt_image']['storage_object']) ? $this->aInputs['cmt_image']['storage_object'] : '';
+    }
+
+    public function getTranscoderPreviewName()
+    {
+    	return isset($this->aInputs['cmt_image']['images_transcoder']) ? $this->aInputs['cmt_image']['images_transcoder'] : '';
+    }
+    
+    function initChecker ($aValues = array (), $aSpecificValues = array())
+    {
+        if(isset($this->aInputs['cmt_image'])) {
+            if(!empty($this->aInputs['sys']['value']) && !empty($this->aInputs['id']['value']) && !empty($aValues['cmt_id'])) {
+                $oObject = $this->_getObject(bx_process_input($this->aInputs['sys']['value']), (int)$this->aInputs['id']['value']);
+                if($oObject)
+                    $this->aInputs['cmt_image']['content_id'] = $oObject->getCommentUniqId($aValues['cmt_id']);
+            }
+
+            $this->aInputs['cmt_image']['ghost_template'] = $this->oTemplate->parseHtmlByName($this->_sGhostTemplateImage, $this->_getGhostTmplVarsImage());
+        }
+
+        if (isset($this->aInputs['cmt_anonymous']) && isset($aValues['cmt_author_id']))
+            $this->aInputs['cmt_anonymous']['checked'] = $aValues['cmt_author_id'] < 0;
+        
+        parent::initChecker ($aValues, $aSpecificValues);
+    }
+    
+    public function insert ($aValsToAdd = array(), $isIgnore = false)
+    {
+        $aValsToAdd['cmt_author_id'] *= isset($this->aInputs['cmt_anonymous']) && $this->getCleanValue('cmt_anonymous') ? -1 : 1;
+
+        return parent::insert ($aValsToAdd, $isIgnore);
+    }
+
+    public function update ($iCmtId, $aValsToAdd = array(), &$aTrackTextFieldsChanges = null)
+    {
+        if (isset($this->aInputs['cmt_anonymous'])) {
+            $aCmt = $this->_getObject($this->getCleanValue('sys'), $this->getCleanValue('id'))->getCommentRow($iCmtId);
+
+            $aValsToAdd['cmt_author_id'] = ($this->getCleanValue('cmt_anonymous') ? -1 : 1) * abs($aCmt['cmt_author_id']);
+        }
+
+        return parent::update ($iCmtId, $aValsToAdd, $aTrackTextFieldsChanges);
+    }
+    
+    public function processImages ($oCmts, $sFieldName, $iCmtUniqId, $iCmtId, $iCmtAuthorId, $isAssociateWithContent = false)
+    {
+        if(!isset($this->aInputs[$sFieldName]))
+            return true;
+
+        $mixedFileIds = $this->getCleanValue($sFieldName);
+        if(!$mixedFileIds)
+            return true;
+
+        $oStorage = BxDolStorage::getObjectInstance($this->aInputs[$sFieldName]['storage_object']);
+        if(!$oStorage)
+            return false;
+
+        $aGhostImages = $oStorage->getGhosts($iCmtAuthorId, $isAssociateWithContent ? 0 : $iCmtUniqId, true, $oCmts->isAdmin($iCmtAuthorId));
+        if(!$aGhostImages)
+            return true;
+
+        foreach($aGhostImages as $aImage) {
+            if(is_array($mixedFileIds) && !in_array($aImage['id'], $mixedFileIds))
+                continue;
+
+            if($aImage['private'])
+                $oStorage->setFilePrivate($aImage['id'], 1);
+
+            if($iCmtId)
+                $this->_associalImageWithContent($oCmts, $sFieldName, $iCmtUniqId, $iCmtId, $iCmtAuthorId, $aImage['id']);
+        }
+
+        return true;
+    }
+
+    protected function genCustomRowCmtCf(&$aInput)
+    {
+        $aInput = BxDolContentFilter::getInstance()->getInputForComments($aInput);
+        if($aInput['type'] == 'hidden') {
+            $this->_sCodeAdd .= $this->genInput($aInput);
+            return '';
+        }
+
+        return $this->genRowStandard($aInput);
+    }
+
+    protected function _getObject($sSystem, $iId)
+    {
+        if(empty($this->_oObject))
+            $this->_oObject = BxDolCmts::getObjectInstance($sSystem, $iId);
+
+        return $this->_oObject;
+    }
+
+    protected function _getGhostTmplVarsImage($aCmt = array())
+    {
+    	return array (
+            'name' => $this->aInputs['cmt_image']['name'],
+            'content_id' => (int)$this->aInputs['cmt_image']['content_id'],
+            'editor_id' => '',
+        );
+    }
+
+    protected function _associalImageWithContent($oCmts, $sFieldName, $iCmtUniqId, $iCmtId, $iCmtAuthorId, $iImageId)
+    {
+        $oStorage = BxDolStorage::getObjectInstance($this->aInputs[$sFieldName]['storage_object']);
+        if(!$oStorage)
+            return false;
+
+        $oStorage->updateGhostsContentId($iImageId, $iCmtAuthorId, $iCmtUniqId, $oCmts->isAdmin($iCmtAuthorId));
+
+        $aSystem = $oCmts->getSystemInfo();
+        $oCmts->getQueryObject()->saveImages($aSystem['system_id'], $iCmtId, $iImageId);
+
+        return true;
+    }
+}
+
+/** @} */

+ 8 - 1
upgrade/files/13.0.0.B1-13.0.0.B2/files/template/scripts/BxBaseFormView.php

@@ -1370,7 +1370,14 @@ BLAH;
     
     function getHtmlEditorQueryParams($aInput)
     {
-        return ['i' => $aInput['name'], 'f' => $this->aFormAttrs['id']];
+        $aQueryParams = ['i' => $aInput['name'], 'f' => $this->aFormAttrs['id'], 'fi' => ''];
+        
+        bx_alert('system', 'editor_query_params', 0, 0, array(
+            'form' => $this,
+            'override_result' => &$aQueryParams
+        ));
+        
+        return $aQueryParams;
     }
 
     /**