Jelajahi Sumber

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

RomanL 6 tahun lalu
induk
melakukan
b11c74cc97
29 mengubah file dengan 574 tambahan dan 311 penghapusan
  1. 8 0
      inc/classes/BxDolBadgesQuery.php
  2. 14 6
      inc/classes/BxDolForm.php
  3. 25 17
      modules/base/general/classes/BxBaseModGeneralModule.php
  4. 29 0
      modules/base/groups/classes/BxBaseModGroupsConfig.php
  5. 13 4
      modules/base/groups/classes/BxBaseModGroupsModule.php
  6. 2 2
      modules/base/profile/classes/BxBaseModProfileConfig.php
  7. 37 33
      modules/base/profile/classes/BxBaseModProfileModule.php
  8. 5 9
      modules/boonex/channels/classes/BxCnlConfig.php
  9. 1 1
      modules/boonex/channels/classes/BxCnlModule.php
  10. 7 13
      modules/boonex/events/classes/BxEventsConfig.php
  11. 6 3
      modules/boonex/feedback/classes/BxFdbConfig.php
  12. 24 4
      modules/boonex/feedback/classes/BxFdbDb.php
  13. 174 80
      modules/boonex/feedback/classes/BxFdbFormQuestion.php
  14. 19 3
      modules/boonex/feedback/classes/BxFdbGridQuestions.php
  15. 55 34
      modules/boonex/feedback/classes/BxFdbModule.php
  16. 2 2
      modules/boonex/feedback/classes/BxFdbTemplate.php
  17. 1 1
      modules/boonex/feedback/install/langs/en.xml
  18. 2 2
      modules/boonex/feedback/install/sql/enable.sql
  19. 3 3
      modules/boonex/feedback/install/sql/install.sql
  20. 21 11
      modules/boonex/feedback/js/question.js
  21. 14 2
      modules/boonex/feedback/template/css/question.css
  22. 9 0
      modules/boonex/feedback/template/form_answer.html
  23. 3 11
      modules/boonex/feedback/template/form_answers.html
  24. 7 13
      modules/boonex/groups/classes/BxGroupsConfig.php
  25. 2 2
      modules/boonex/organizations/classes/BxOrgsModule.php
  26. 7 13
      modules/boonex/spaces/classes/BxSpacesConfig.php
  27. 1 1
      modules/boonex/timeline/classes/BxTimelineTemplate.php
  28. 83 38
      studio/classes/BxDolStudioForm.php
  29. 0 3
      studio/classes/BxDolStudioToolsAudit.php

+ 8 - 0
inc/classes/BxDolBadgesQuery.php

@@ -98,6 +98,14 @@ class BxDolBadgesQuery extends BxDolDb
 
                 $sWhereClause = " AND `sc`.`id` = :id";
                 break;
+            
+            case 'by_module':
+                $aMethod['name'] = 'getAll';
+                $sWhereClause = " AND `sc`.`module` = :module";
+                $aMethod['params'][1] = array(
+                    'module' => $aParams['module']
+                );
+                break;
         }
 
         if(!empty($sGroupClause))

+ 14 - 6
inc/classes/BxDolForm.php

@@ -721,6 +721,7 @@ class BxDolForm extends BxDol implements iBxDolReplaceable
     protected $oTemplate;
 
     protected $_isValid = true;
+    protected $_sChecker;
     protected $_sCheckerHelper;
     protected $_aSpecificValues;
 
@@ -755,6 +756,7 @@ class BxDolForm extends BxDol implements iBxDolReplaceable
         if (!isset($this->aFormAttrs['action']))
             $this->aFormAttrs['action'] = '';
 
+        $this->_sChecker = isset($this->aParams['checker']) ? $this->aParams['checker'] : 'BxDolFormChecker';
         $this->_sCheckerHelper = isset($this->aParams['checker_helper']) ? $this->aParams['checker_helper'] : '';
     }
 
@@ -802,7 +804,7 @@ class BxDolForm extends BxDol implements iBxDolReplaceable
     {
         $this->_aSpecificValues = $aSpecificValues;
 
-        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker = new $this->_sChecker($this->_sCheckerHelper);
         $oChecker->setFormMethod($this->aFormAttrs['method'], $aSpecificValues);
         $oChecker->setFormParams($this->aParams);
         $oChecker->setFieldsCheckForSpam($this->_aFieldsCheckForSpam);
@@ -851,7 +853,7 @@ class BxDolForm extends BxDol implements iBxDolReplaceable
 
     function insert ($aValsToAdd = array(), $isIgnore = false)
     {
-        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker = new $this->_sChecker($this->_sCheckerHelper);
         $oChecker->setFormMethod($this->aFormAttrs['method'], $this->_aSpecificValues);
         $sSql = $oChecker->dbInsert($this->aParams['db'], $this->aInputs, $aValsToAdd, $isIgnore);
         if (!$sSql)
@@ -864,7 +866,7 @@ class BxDolForm extends BxDol implements iBxDolReplaceable
 
     function update ($val, $aValsToAdd = array(), &$aTrackTextFieldsChanges = null)
     {
-        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker = new $this->_sChecker($this->_sCheckerHelper);
         $oChecker->setFormMethod($this->aFormAttrs['method'], $this->_aSpecificValues);
         $sSql = $oChecker->dbUpdate($val, $this->aParams['db'], $this->aInputs, $aValsToAdd, $aTrackTextFieldsChanges);
         if (!$sSql)
@@ -874,7 +876,7 @@ class BxDolForm extends BxDol implements iBxDolReplaceable
 
     function delete ($val)
     {
-        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker = new $this->_sChecker($this->_sCheckerHelper);
         $oChecker->setFormMethod($this->aFormAttrs['method'], $this->_aSpecificValues);
         $sSql = $oChecker->dbDelete($val, $this->aParams['db'], $this->aInputs);
         if (!$sSql)
@@ -891,7 +893,7 @@ class BxDolForm extends BxDol implements iBxDolReplaceable
 
     function getCleanValue ($sName)
     {
-        $oChecker = new BxDolFormChecker($this->_sCheckerHelper);
+        $oChecker = new $this->_sChecker($this->_sCheckerHelper);
         $oChecker->setFormMethod($this->aFormAttrs['method'], $this->_aSpecificValues);
         $a = isset($this->aInputs[$sName]) ? $this->aInputs[$sName] : false;
         if ($a && isset($a['db']['pass']))
@@ -1190,7 +1192,13 @@ class BxDolFormChecker
                         $sSubmitName = $k;
 
             $a['name'] = str_replace('[]', '', $a['name']);
-            $val = BxDolForm::getSubmittedValue($a['name'], $this->_sFormMethod, $this->_aSpecificValues);
+
+            $sMethodGetSubmittedValue = 'getSubmittedValue' . bx_gen_method_name($a['name']);
+            if(method_exists($this, $sMethodGetSubmittedValue))
+                $val = $this->$sMethodGetSubmittedValue($a['name'], $this->_sFormMethod, $this->_aSpecificValues);
+            else
+                $val = BxDolForm::getSubmittedValue($a['name'], $this->_sFormMethod, $this->_aSpecificValues);
+
             if (isset(BxDolForm::$TYPES_FILE[$a['type']]))
                 $val = isset($_FILES[$a['name']]['name']) ? $_FILES[$a['name']]['name'] : '';
 

+ 25 - 17
modules/base/general/classes/BxBaseModGeneralModule.php

@@ -1777,31 +1777,39 @@ class BxBaseModGeneralModule extends BxDolModule
         return $iProfileId;
     }
 	
-	public function serviceGetBadges($iContentId,  $bIsSingle = false, $bIsCompact  = false)
+    public function serviceGetBadges($iContentId,  $bIsSingle = false, $bIsCompact  = false)
     {
+        $sModule = $this->getName();
+
         $oBadges = BxDolBadges::getInstance();
-        $aBadges = $oBadges->getData(
-            array(
+        $aBadges = $oBadges->getData(array(
             'type' => ($bIsSingle ? 'by_module&object2_single' : 'by_module&object2'),
-            'module' => $this->_aModule['name'],
+            'module' => $sModule,
             'object_id' => $iContentId
-            )
-        );
-        
-        if ($bIsSingle && count($aBadges) > 0)
+        ));
+
+        $sResult = false;
+        bx_alert('system', 'get_badges', 0, 0, array(
+            'module' => $sModule, 
+            'content_id' => $iContentId,
+            'is_single' => $bIsSingle,
+            'is_compact' => $bIsCompact,
+            'badges' => $aBadges, 
+            'override_result' => &$sResult
+        ));
+        if($sResult !== false)
+            return $sResult;
+
+        if($bIsSingle && count($aBadges) > 0)
             return BxDolService::call('system', 'get_badge', array($aBadges[0], $bIsCompact), 'TemplServices');
-        
+
         $aBadgesOutput = array();
-        foreach($aBadges as $aBadge) {
+        foreach($aBadges as $aBadge)
             $aBadgesOutput[] =  array('badge' => BxDolService::call('system', 'get_badge', array($aBadge, $bIsCompact), 'TemplServices'));
-        }
 
-        return $this->_oTemplate->parseHtmlByName('badges.html', 
-			array(
-				'bx_repeat:items' => $aBadgesOutput,
-			)
-		);
-        
+        return $this->_oTemplate->parseHtmlByName('badges.html', array(
+            'bx_repeat:items' => $aBadgesOutput,
+        ));
     }
 
     /**

+ 29 - 0
modules/base/groups/classes/BxBaseModGroupsConfig.php

@@ -0,0 +1,29 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    Groups Groups
+ * @ingroup     UnaModules
+ * 
+ * @{
+ */
+
+class BxBaseModGroupsConfig extends BxBaseModProfileConfig
+{
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this->_aMenuItems2MethodsActions = array (
+            'profile-fan-add' => 'checkAllowedFanAdd',
+            'profile-fan-remove' => 'checkAllowedFanRemove',
+            'profile-subscribe-add' => 'checkAllowedSubscribeAdd',
+            'profile-subscribe-remove' => 'checkAllowedSubscribeRemove',
+            'profile-actions-more' => 'checkAllowedViewMoreMenu',
+            'convos-compose' => 'checkAllowedContact',
+        );
+    }
+}
+
+/** @} */

+ 13 - 4
modules/base/groups/classes/BxBaseModGroupsModule.php

@@ -613,14 +613,23 @@ class BxBaseModGroupsModule extends BxBaseModProfileModule
     }
 
     /**
+     * @deprecated since version 11.0.1 use BxBaseModGroupsModule::checkAllowedCompose instead
      * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
      */
-    public function checkAllowedCompose (&$aDataEntry, $isPerformAction = false)
+    public function checkAllowedCompose(&$aDataEntry, $isPerformAction = false)
     {
-        if (!$this->isFan($aDataEntry[$this->_oConfig->CNF['FIELD_ID']]))
+        return $this->checkAllowedContact($aDataEntry, $isPerformAction);
+    }
+
+    /**
+     * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
+     */
+    public function checkAllowedContact($aDataEntry, $isPerformAction = false)
+    {
+        if(!$this->isFan($aDataEntry[$this->_oConfig->CNF['FIELD_ID']]))
             return _t('_sys_txt_access_denied');
-        
-        return parent::checkAllowedCompose ($aDataEntry, $isPerformAction);
+
+        return parent::checkAllowedContact($aDataEntry, $isPerformAction);
     }
 
     /**

+ 2 - 2
modules/base/profile/classes/BxBaseModProfileConfig.php

@@ -27,8 +27,8 @@ class BxBaseModProfileConfig extends BxBaseModGeneralConfig
             'profile-subscribe-remove' => 'checkAllowedSubscribeRemove',
             'profile-actions-more' => 'checkAllowedViewMoreMenu',
             'profile-set-acl-level' => 'checkAllowedSetMembership',
-            'convos-compose' => 'checkAllowedCompose',
-            'messenger' => 'checkAllowedCompose',
+            'convos-compose' => 'checkAllowedContact',
+            'messenger' => 'checkAllowedContact',
         );        
     }
 }

+ 37 - 33
modules/base/profile/classes/BxBaseModProfileModule.php

@@ -1286,34 +1286,17 @@ class BxBaseModProfileModule extends BxBaseModGeneralModule implements iBxDolCon
      */
     public function serviceCheckAllowedContactForProfile ($aDataEntry, $isPerformAction = false, $iProfileId = false)
     {
-        $CNF = &$this->_oConfig->CNF;
-
-        if(!$iProfileId)
-            $iProfileId = $this->_iProfileId;
-
-        // check is view allowed
-        if(($mixedResult = $this->serviceCheckAllowedViewForProfile($aDataEntry, $isPerformAction, $iProfileId)) !== CHECK_ACTION_RESULT_ALLOWED)
-            return $mixedResult;
-
-        // moderator and owner always have access
-        $oProfile = BxDolProfile::getInstanceByContentAndType($aDataEntry[$CNF['FIELD_ID']], $this->getName());
-        if(($oProfile && $oProfile->id() == $iProfileId) || $this->_isModerator($isPerformAction))
-            return CHECK_ACTION_RESULT_ALLOWED;
-
-        // check privacy
-        if(!empty($CNF['OBJECT_PRIVACY_CONTACT'])) {
-            $oPrivacy = BxDolPrivacy::getObjectInstance($CNF['OBJECT_PRIVACY_CONTACT']);
-            if($oPrivacy && !$oPrivacy->check($aDataEntry[$CNF['FIELD_ID']], $iProfileId))
-                return _t('_sys_access_denied_to_private_content');
-        }
+        $mixedResult = $this->_serviceCheckAllowedContactForProfile($aDataEntry, $isPerformAction, $iProfileId);
 
         // call alert to allow custom checks
-        $mixedResult = null;
-        bx_alert('system', 'check_allowed_contact', 0, 0, array('module' => $this->getName(), 'content_info' => $aDataEntry, 'profile_id' => $iProfileId, 'override_result' => &$mixedResult));
-        if($mixedResult !== null)
-            return $mixedResult;
+        bx_alert('system', 'check_allowed_contact', 0, 0, array(
+            'module' => $this->getName(), 
+            'content_info' => $aDataEntry, 
+            'profile_id' => $iProfileId, 
+            'override_result' => &$mixedResult
+        ));
 
-        return CHECK_ACTION_RESULT_ALLOWED;
+        return $mixedResult;
     }
 
     public function serviceSetViewProfileCover($oPage, $aProfileInfo)
@@ -1455,18 +1438,13 @@ class BxBaseModProfileModule extends BxBaseModGeneralModule implements iBxDolCon
         return CHECK_ACTION_RESULT_ALLOWED;
     }
 
-	/**
+    /**
+     * @deprecated since version 11.0.1 use BxBaseModProfileModule::checkAllowedCompose instead
      * @return CHECK_ACTION_RESULT_ALLOWED if access is granted or error message if access is forbidden.
      */
     public function checkAllowedCompose (&$aDataEntry, $isPerformAction = false)
     {
-        $CNF = &$this->_oConfig->CNF;
-
-        $oProfile = BxDolProfile::getInstanceByContentAndType($aDataEntry[$CNF['FIELD_ID']], $this->getName());
-        if($oProfile && $oProfile->id() == $this->_iProfileId)
-            return _t('_sys_txt_access_denied');
-
-        return $this->checkAllowedView ($aDataEntry, $isPerformAction);
+        return $this->checkAllowedContact($aDataEntry, $isPerformAction);
     }
 
     /**
@@ -1567,6 +1545,32 @@ class BxBaseModProfileModule extends BxBaseModGeneralModule implements iBxDolCon
 		return $oProfile->id() == $iLogged;
     }
 
+    protected function _serviceCheckAllowedContactForProfile($aDataEntry, $isPerformAction = false, $iProfileId = false)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(!$iProfileId)
+            $iProfileId = $this->_iProfileId;
+
+        // check is view allowed
+        if(($mixedResult = $this->serviceCheckAllowedViewForProfile($aDataEntry, $isPerformAction, $iProfileId)) !== CHECK_ACTION_RESULT_ALLOWED)
+            return $mixedResult;
+
+        // moderator and owner always have access
+        $oProfile = BxDolProfile::getInstanceByContentAndType($aDataEntry[$CNF['FIELD_ID']], $this->getName());
+        if(($oProfile && $oProfile->id() == $iProfileId) || $this->_isModerator($isPerformAction))
+            return CHECK_ACTION_RESULT_ALLOWED;
+
+        // check privacy
+        if(!empty($CNF['OBJECT_PRIVACY_CONTACT'])) {
+            $oPrivacy = BxDolPrivacy::getObjectInstance($CNF['OBJECT_PRIVACY_CONTACT']);
+            if($oPrivacy && !$oPrivacy->check($aDataEntry[$CNF['FIELD_ID']], $iProfileId))
+                return _t('_sys_access_denied_to_private_content');
+        }
+
+        return CHECK_ACTION_RESULT_ALLOWED;
+    }
+
     // ====== COMMON METHODS
     public function alertAfterAdd($aContentInfo)
     {

+ 5 - 9
modules/boonex/channels/classes/BxCnlConfig.php

@@ -9,23 +9,19 @@
  * @{
  */
 
-class BxCnlConfig extends BxBaseModProfileConfig
+class BxCnlConfig extends BxBaseModGroupsConfig
 {
     function __construct($aModule)
     {
         parent::__construct($aModule);
 
-        $aMenuItems2Methods = array (
+        $this->_aMenuItems2MethodsActions = array_merge($this->_aMenuItems2MethodsActions, array(
             'view-channel-profile' => 'checkAllowedView',
             'edit-channel-profile' => 'checkAllowedEdit',
             'edit-channel-cover' => 'checkAllowedChangeCover',
             'invite-to-channel' => 'checkAllowedInvite',
             'delete-channel-profile' => 'checkAllowedDelete',
-            'profile-subscribe-add' => 'checkAllowedSubscribeAdd',
-            'profile-subscribe-remove' => 'checkAllowedSubscribeRemove',
-            'profile-actions-more' => 'checkAllowedViewMoreMenu',
-            'convos-compose' => 'checkAllowedCompose',
-        );
+        ));
 
         $this->CNF = array (
 
@@ -122,8 +118,8 @@ class BxCnlConfig extends BxBaseModProfileConfig
 
             // menu items which visibility depends on custom visibility checking
             'MENU_ITEM_TO_METHOD' => array (
-                'bx_channels_view_actions' => $aMenuItems2Methods,
-                'bx_channels_view_actions_more' => $aMenuItems2Methods,
+                'bx_channels_view_actions' => $this->_aMenuItems2MethodsActions,
+                'bx_channels_view_actions_more' => $this->_aMenuItems2MethodsActions,
             ),
 
             // informer messages

+ 1 - 1
modules/boonex/channels/classes/BxCnlModule.php

@@ -465,7 +465,7 @@ class BxCnlModule extends BxBaseModGroupsModule
         return $a;
     }
     
-    public function checkAllowedCompose (&$aDataEntry, $isPerformAction = false)
+    public function checkAllowedContact($aDataEntry, $isPerformAction = false)
     {
         return _t('_sys_txt_access_denied');
     }

+ 7 - 13
modules/boonex/events/classes/BxEventsConfig.php

@@ -9,25 +9,19 @@
  * @{
  */
 
-class BxEventsConfig extends BxBaseModProfileConfig
+class BxEventsConfig extends BxBaseModGroupsConfig
 {
     function __construct($aModule)
     {
         parent::__construct($aModule);
 
-        $aMenuItems2Methods = array (
+        $this->_aMenuItems2MethodsActions = array_merge($this->_aMenuItems2MethodsActions, array(
             'view-event-profile' => 'checkAllowedView',
             'edit-event-profile' => 'checkAllowedEdit',
             'edit-event-cover' => 'checkAllowedChangeCover',
             'invite-to-event' => 'checkAllowedInvite',
-            'delete-event-profile' => 'checkAllowedDelete',
-            'profile-fan-add' => 'checkAllowedFanAdd',
-            'profile-fan-remove' => 'checkAllowedFanRemove',
-            'profile-subscribe-add' => 'checkAllowedSubscribeAdd',
-            'profile-subscribe-remove' => 'checkAllowedSubscribeRemove',
-            'profile-actions-more' => 'checkAllowedViewMoreMenu',
-            'convos-compose' => 'checkAllowedCompose',
-        );
+            'delete-event-profile' => 'checkAllowedDelete'
+        ));
 
         $this->CNF = array (
 
@@ -153,9 +147,9 @@ class BxEventsConfig extends BxBaseModProfileConfig
 
             // menu items which visibility depends on custom visibility checking
             'MENU_ITEM_TO_METHOD' => array (
-                'bx_events_view_actions' => $aMenuItems2Methods,
-                'bx_events_view_actions_more' => $aMenuItems2Methods,
-                'bx_events_view_actions_all' => $aMenuItems2Methods,
+                'bx_events_view_actions' => $this->_aMenuItems2MethodsActions,
+                'bx_events_view_actions_more' => $this->_aMenuItems2MethodsActions,
+                'bx_events_view_actions_all' => $this->_aMenuItems2MethodsActions,
             ),
 
             // informer messages

+ 6 - 3
modules/boonex/feedback/classes/BxFdbConfig.php

@@ -34,15 +34,18 @@ class BxFdbConfig extends BxBaseModGeneralConfig
             'FIELD_ANSWERS' => 'answers',
             'FIELD_STATUS_ADMIN' => 'status_admin',
 
+            'FIELD_ANS_ID' => 'id',
             'FIELD_ANS_TITLE' => 'title',
             'FIELD_ANS_IMPORTANT' => 'important',
 
+            'FIELD_ANS2USR_TEXT' => 'text',
+
             // page URIs
 
             // some params
             'PARAM_QUESTIONS' => 'bx_feedback_enable_questions',
             'PARAM_QUESTION_LIFETIME' => 'bx_feedback_question_lifetime',
-            'PARAM_ANSWER_NOTIFY_IMPORTANT' => 'bx_feedback_enable_answer_notify_important',
+            'PARAM_ANSWER_NTF_IMPORTANT_ONLY' => 'bx_feedback_enable_answer_ntf_important_only',
 
             // objects
             'OBJECT_FORM_QUESTION' => 'bx_feedback_question',
@@ -93,9 +96,9 @@ class BxFdbConfig extends BxBaseModGeneralConfig
     /**
      * Notify about important Answers only.
      */
-    public function isModeAni()
+    public function isModeNio()
     {
-        return getParam($this->CNF['PARAM_ANSWER_NOTIFY_IMPORTANT']) == 'on';
+        return getParam($this->CNF['PARAM_ANSWER_NTF_IMPORTANT_ONLY']) == 'on';
     }
 }
 

+ 24 - 4
modules/boonex/feedback/classes/BxFdbDb.php

@@ -235,6 +235,25 @@ class BxFdbDb extends BxBaseModGeneralDb
         return $this->query($sSql);
     }
 
+    public function insertAnswer2User($aParamsSet)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        if(empty($aParamsSet))
+            return false;
+
+        $sSql = "INSERT INTO `" . $CNF['TABLE_ANSWERS2USERS'] . "` SET " . $this->arrayToSQL($aParamsSet);
+        return $this->query($sSql);
+    }
+
+    public function deleteAnswer2User($aParams)
+    {
+        $CNF = &$this->_oConfig->CNF;
+
+        $sSql = "DELETE FROM `" . $CNF['TABLE_ANSWERS2USERS'] . "` WHERE " . $this->arrayToSQL($aParams, " AND ");
+        return $this->query($sSql);
+    }
+
     public function isAnswer($iQuestionId, $iProfileId)
     {
         $iAnswer = (int)$this->getAnswers(array(
@@ -250,10 +269,11 @@ class BxFdbDb extends BxBaseModGeneralDb
     {
         $CNF = &$this->_oConfig->CNF;
 
-        $bResult = (int)$this->query("INSERT INTO `" . $CNF['TABLE_ANSWERS2USERS'] . "` SET `answer_id`=:answer_id, `profile_id`=:profile_id, `text`=:text, `added`=UNIX_TIMESTAMP()", array(
+        $bResult = (int)$this->insertAnswer2User(array(
             'answer_id' => $iAnswerId,
             'profile_id' => $iProfileId,
-            'text' => $sText
+            'text' => $sText,
+            'added' => time()
         )) > 0;
 
         if($bResult)
@@ -266,11 +286,11 @@ class BxFdbDb extends BxBaseModGeneralDb
     {
         $CNF = &$this->_oConfig->CNF;
 
-        $bResult = (int)$this->query("DELETE FROM `" . $CNF['TABLE_ANSWERS2USERS'] . "` WHERE `answer_id`=:answer_id AND `profile_id`=:profile_id", array(
+        $bResult = (int)$this->deleteAnswer2User(array(
             'answer_id' => $iAnswerId,
             'profile_id' => $iProfileId
         )) > 0;
-        
+
         if($bResult)
             $this->query("UPDATE `" . $CNF['TABLE_ANSWERS'] . "` SET `votes`=`votes`-1 WHERE `id`=:id", array('id' => $iAnswerId));
 

+ 174 - 80
modules/boonex/feedback/classes/BxFdbFormQuestion.php

@@ -9,9 +9,48 @@
  * @{
  */
 
-class BxFdbFormQuestionCheckerHelper extends BxDolFormCheckerHelper
+require_once (BX_DOL_DIR_STUDIO_CLASSES . 'BxDolStudioForm.php');
+
+class BxFdbFormQuestionChecker extends BxDolFormChecker
+{
+    function __construct($sHelper = '')
+    {
+        parent::__construct($sHelper);
+    }
+
+    public function getSubmittedValueAnswers($sKey, $sMethod, &$aSpecificValues = false)
+    {
+        $aValues = array(
+            'ids' => array(), 
+            'vals' => array(), 
+            'imps' => array()
+        );
+
+        $iIndex = (int)BxDolForm::getSubmittedValue($sKey . '_ind', $sMethod, $aSpecificValues);
+
+        for($i = 0; $i < $iIndex; $i++) {
+            $sValue = BxDolForm::getSubmittedValue($sKey . '_' . $i, $sMethod, $aSpecificValues);
+            if($sValue === false)
+                continue;
+
+            $aValues['vals'][$i] = $sValue;
+            
+            $sId = BxDolForm::getSubmittedValue($sKey . '_id_' . $i, $sMethod, $aSpecificValues);
+            if($sId !== false)
+                $aValues['ids'][$i] = $sId;
+
+            $sImp = BxDolForm::getSubmittedValue($sKey . '_imp_' . $i, $sMethod, $aSpecificValues);
+            if($sImp !== false && strcmp($sValue, $sImp) == 0)
+                $aValues['imps'][] = $sImp;
+        }
+
+        return $aValues;
+    }
+}
+
+class BxFdbFormQuestionCheckerHelper extends BxDolStudioFormCheckerHelper
 {
-    static public function checkAvailAnswers ($s)
+    static public function checkAvailAnswers ($s, $sName)
     {
         $iCountMin = 2;
 
@@ -27,16 +66,20 @@ class BxFdbFormQuestionCheckerHelper extends BxDolFormCheckerHelper
     }
 }
 
-class BxFdbFormQuestion extends BxBaseModTextFormEntry
+class BxFdbFormQuestion extends BxTemplStudioFormView
 {
+    protected $_sModule;
+    protected $_oModule;
+    
     protected $_sJsObject;
 
     public function __construct($aInfo, $oTemplate = false)
     {
-        $this->MODULE = 'bx_feedback';
-
         parent::__construct($aInfo, $oTemplate);
 
+        $this->_sModule = 'bx_feedback';
+        $this->_oModule = BxDolModule::getInstance($this->_sModule);
+
         $this->_sJsObject = $this->_oModule->_oConfig->getJsObject('question');
     }
 
@@ -50,18 +93,20 @@ class BxFdbFormQuestion extends BxBaseModTextFormEntry
                 'question_id' => $aValues['id']
             ));
 
-            $this->aInputs[$CNF['FIELD_ANSWERS']] = array_merge($this->aInputs[$CNF['FIELD_ANSWERS']], array(
-                'value' => array(),
-                'value_ids' => array(),
-                'value_imps' => array()
-            ));
+            $aValue = array(
+                'ids' => array(),
+                'vals' => array(),
+                'imps' => array()
+            );
 
-            foreach($aAnswers as $aAnswer) {
-                $this->aInputs[$CNF['FIELD_ANSWERS']]['value'][] = $aAnswer['title'];
-                $this->aInputs[$CNF['FIELD_ANSWERS']]['value_ids'][] = $aAnswer['id'];
+            foreach($aAnswers as $i => $aAnswer) {
+                $aValue['ids'][$i] = $aAnswer['id'];
+                $aValue['vals'][$i] = $aAnswer['title'];
                 if((int)$aAnswer['important'] == 1)
-                    $this->aInputs[$CNF['FIELD_ANSWERS']]['value_imps'][] = $aAnswer['title'];
+                    $aValue['imps'][$i] = true;
             }
+
+            $this->aInputs[$CNF['FIELD_ANSWERS']]['value'] = $aValue;
         }
 
         return parent::initChecker($aValues, $aSpecificValues);
@@ -96,21 +141,21 @@ class BxFdbFormQuestion extends BxBaseModTextFormEntry
         if(!isset($this->aInputs[$sField]))
             return true;
 
-        $aAnswers = $this->getCleanValue($sField);
-        if(empty($aAnswers) || !is_array($aAnswers))
-            return true;
+        $iIndex = (int)$this->getCleanValue($sField . '_ind');
+        for($i = 0; $i < $iIndex; $i++) {
+            $sValue = $this->getCleanValue($sField . '_' . $i);
+            if(empty($sValue) || get_mb_len($sValue) == 0)
+                continue;
 
-        $aAnswersImps = $this->getCleanValue($sField . '_imps');
-        $bAnswersImps = !empty($aAnswersImps) && is_array($aAnswersImps);
+            $sImp = $this->getCleanValue($sField . '_imp_' . $i);
 
-        foreach($aAnswers as $iIndex => $sAnswer)
-            if($sAnswer != '' && get_mb_len($sAnswer) > 0)
-                $this->_oModule->_oDb->insertAnswer(array(
-                    'question_id' => $iContentId,
-                    'title' => bx_process_input($sAnswer),
-                    'important' => $bAnswersImps && in_array($sAnswer, $aAnswersImps) ? 1 : 0,
-                    'order' => $iIndex
-                ));
+            $this->_oModule->_oDb->insertAnswer(array(
+                'question_id' => $iContentId,
+                'title' => bx_process_input($sValue),
+                'important' => !empty($sImp) && $sImp == 'on' ? 1 : 0,
+                'order' => $i
+            ));
+        }
 
         return true;
     }
@@ -122,28 +167,39 @@ class BxFdbFormQuestion extends BxBaseModTextFormEntry
         if(!isset($this->aInputs[$sField]))
             return true;
 
-        $aAnswersValues = $this->getCleanValue($sField);
+        $iIndex = (int)$this->getCleanValue($sField . '_ind');
+
+        $aAnswersValues = $aAnswersIds = $aAnswersImps = array();
+        for($i = 0; $i < $iIndex; $i++) {
+            $sValue = $this->getCleanValue($sField . '_' . $i);
+            if(empty($sValue))
+                continue;
 
-        $aAnswersIds = $this->getCleanValue($sField . '_ids');
-        if(empty($aAnswersIds) || !is_array($aAnswersIds))
-            $aAnswersIds = array();
+            $aAnswersValues[$i] = $sValue;
 
-        $aAnswersImps = $this->getCleanValue($sField . '_imps');
-        if(empty($aAnswersImps) || !is_array($aAnswersImps))
-            $aAnswersImps = array();
+            $iId = $this->getCleanValue($sField . '_id_' . $i);
+            if(!empty($iId))
+                $aAnswersIds[$i] = $iId;
+
+            $sImpName = $sField . '_imp_' . $i;
+            $sImpValue = $this->getCleanValue($sImpName);
+
+            if(!empty($sImpValue) && $sImpValue == 'on')
+                $aAnswersImps[$i] = true;
+        }
 
         //--- Remove deleted
         $aAnswersDb = $this->_oModule->_oDb->getAnswers(array('type' => 'question_id_pairs', 'question_id' => $iContentId));
         $this->_oModule->_oDb->deleteAnswerById(array_diff(array_keys($aAnswersDb), $aAnswersIds));
 
         //--- Update existed and remove empty
-        foreach($aAnswersIds as $iIndex => $iAnswerId) {
-            $sAnswersValue = $aAnswersValues[$iIndex];
+        foreach($aAnswersIds as $i => $iAnswerId) {
+            $sAnswersValue = $aAnswersValues[$i];
 
             if($sAnswersValue != '' && get_mb_len($sAnswersValue) > 0)
                 $this->_oModule->_oDb->updateAnswer(array(
                     'title' => bx_process_input($sAnswersValue), 
-                    'important' => in_array($sAnswersValue, $aAnswersImps) ? 1 : 0
+                    'important' => isset($aAnswersImps[$i]) && $aAnswersImps[$i] === true ? 1 : 0
                 ), array('id' => (int)$iAnswerId));
             else 
                 $this->_oModule->_oDb->deleteAnswer(array('id' => (int)$iAnswerId));
@@ -155,13 +211,12 @@ class BxFdbFormQuestion extends BxBaseModTextFormEntry
         if($iAnswersValues > $iAnswersIds) {
             $iMaxOrder = (int)$this->_oModule->_oDb->getAnswers(array('type' => 'question_id_max_order', 'question_id' => $iContentId));
 
-            $aAnswersValues = array_slice($aAnswersValues, $iAnswersIds);
-            foreach($aAnswersValues as $sAnswersValue)
-                if($sAnswersValue != '' && get_mb_len($sAnswersValue) > 0)
+            for($i = $iAnswersIds; $i < $iAnswersValues; $i++)
+                if($aAnswersValues[$i] != '' && get_mb_len($aAnswersValues[$i]) > 0)
                     $this->_oModule->_oDb->insertAnswer(array(
                         'question_id' => $iContentId,
-                        'title' => bx_process_input($sAnswersValue),
-                        'important' => in_array($sAnswersValue, $aAnswersImps) ? 1 : 0,
+                        'title' => bx_process_input($aAnswersValues[$i]),
+                        'important' => isset($aAnswersImps[$i]) && $aAnswersImps[$i] === true ? 1 : 0,
                         'order' => ++$iMaxOrder
                     ));
         }
@@ -171,79 +226,118 @@ class BxFdbFormQuestion extends BxBaseModTextFormEntry
 
     protected function genCustomInputAnswers(&$aInput)
     {
-        if(!empty($aInput['value']) && is_array($aInput['value'])) {
-            $aAnswersImp = array();
-            if(!empty($aInput['value_imps']) && is_array($aInput['value_imps']))
-                $aAnswersImp = $aInput['value_imps'];
-
-            $aTmplVarsAnswers = array();
-            foreach($aInput['value'] as $iIndex => $sValue) {
-                $sInputText = $this->genCustomInputAnswersText($aInput, $sValue);
-                if(!empty($aInput['value_ids'][$iIndex]))
-                    $sInputText .= $this->genCustomInputAnswersHidden($aInput, (int)$aInput['value_ids'][$iIndex]);
-
-                $sInputCheckbox = $this->genCustomInputAnswersCheckbox($aInput, $sValue, in_array($sValue, $aAnswersImp));
-
-                $aTmplVarsAnswers[] = array(
-                    'js_object' => $this->_sJsObject, 
-                    'input_text' => $sInputText,
-                    'input_checkbox' => $sInputCheckbox
-                );
-            }
-        }
-        else {
-            $aAnswer = array(
-                'js_object' => $this->_sJsObject, 
-                'input_text' => $this->genCustomInputAnswersText($aInput),
-                'input_checkbox' => $this->genCustomInputAnswersCheckbox($aInput)
-            );
-
-            $aTmplVarsAnswers = array($aAnswer, $aAnswer);
+        $iIndex = 0;
+        $sAnswers = '';
+
+        if(!empty($aInput['value']) && !empty($aInput['value']['vals']) && is_array($aInput['value']['vals'])) {
+            $aImps = array();
+            if(!empty($aInput['value']['imps']) && is_array($aInput['value']['imps']))
+                $aImps = $aInput['value']['imps'];
+
+            foreach($aInput['value']['vals'] as $i => $sValue)
+                $sAnswers .= $this->_genAnswerSample($aInput, $iIndex++, array(
+                    'value' => $sValue,
+                    'id' => !empty($aInput['value']['ids'][$i]) ? (int)$aInput['value']['ids'][$i] : '',
+                    'checked' => isset($aImps[$i]) && $aImps[$i] === true,
+                ));
         }
+        else
+            for($i = 0; $i < 2; $i++)
+                $sAnswers .= $this->_genAnswerSample($aInput, $iIndex++);
 
         return $this->_oModule->_oTemplate->parseHtmlByName('form_answers.html', array(
-            'bx_repeat:answers' => $aTmplVarsAnswers,
+            'index' => $this->genCustomInputAnswersIndex($aInput, $iIndex),
+            'sample' => $this->_genAnswerSample($aInput, '|x|'),
+            'answers' => $sAnswers,
             'btn_add' => $this->genCustomInputAnswersButton($aInput)
         ));
     }
 
-    protected function genCustomInputAnswersText($aInput, $mixedValue = '')
+    protected function _genAnswerSample($aInput, $iIndex, $aParams = array())
+    {
+        $sClass = 'bx-form-input-answer';
+        if(!empty($aParams['class']))
+            $sClass .= ' ' . $aParams['class'];
+
+        $sValue = !empty($aParams['value']) ? $aParams['value'] : '';
+
+        $sInputText = $this->genCustomInputAnswersText($aInput, $iIndex, $sValue);
+        if(!empty($aParams['id']))
+            $sInputText .= $this->genCustomInputAnswersHidden($aInput, $iIndex, $aParams['id']);
+
+        return $this->_oModule->_oTemplate->parseHtmlByName('form_answer.html', array(
+            'js_object' => $this->_sJsObject, 
+            'class' => $sClass,
+            'input_text' => $sInputText,
+            'input_checkbox' => $this->genCustomInputAnswersCheckbox($aInput, $iIndex, 'on', (isset($aParams['checked']) && $aParams['checked'] === true))
+        ));
+    }
+
+    protected function genCustomInputAnswersText($aInput, $iIndex, $mixedValue = '')
     {
-        $aInput['type'] = 'text';
-        $aInput['name'] .= '[]';
+        $aInput['type'] = 'text_translatable';
+        $aInput['name'] .= '_' . $iIndex;
+        $aInput['caption'] = '';
         $aInput['value'] = $mixedValue;
+        $aInput['db'] = array(
+            'pass' => 'Xss'
+        );
         
         if(!is_array($aInput['attrs']))
             $aInput['attrs'] = array();
 
-        $sJsMethod = 'javascript:' . $this->_sJsObject . '.answerOnType(event)';
+        $sJsMethod = 'javascript:' . $this->_sJsObject . '.answerOnType(event, \'' . $aInput['name'] . '\')';
         $aInput['attrs'] = array_merge($aInput['attrs'], array(
+            'bx-index' => $iIndex,
             'class' => 'bx-def-margin-sec-top-auto',
             'onkeyup' => $sJsMethod,
             'onpaste' => $sJsMethod,
             'onblur' => $sJsMethod
         ));
 
-        return $this->genInput($aInput);
+        return $this->genRow($aInput);
     }
 
-    protected function genCustomInputAnswersCheckbox($aInput, $mixedValue = '', $bChecked = false)
+    protected function genCustomInputAnswersCheckbox($aInput, $iIndex, $mixedValue = '', $bChecked = false)
     {
         $aInput['type'] = 'checkbox';
-        $aInput['name'] .= '_imps[]';
+        $aInput['name'] .= '_imp_' . $iIndex;
         $aInput['caption'] = _t('_bx_feedback_form_question_input_answers_imp');
         $aInput['value'] = $mixedValue;
         $aInput['required'] = 0;
         if($bChecked)
             $aInput['checked'] = 'checked';
 
+        if(!is_array($aInput['attrs']))
+            $aInput['attrs'] = array();
+
+        $aInput['attrs'] = array_merge($aInput['attrs'], array(
+            'bx-index' => $iIndex,
+        ));
+
         return $this->genRow($aInput);
     }
 
-    protected function genCustomInputAnswersHidden($aInput, $mixedValue = '')
+    protected function genCustomInputAnswersHidden($aInput, $iIndex, $mixedValue = '')
     {
         $aInput['type'] = 'hidden';
-        $aInput['name'] .= '_ids[]';
+        $aInput['name'] .= '_id_' . $iIndex;
+        $aInput['value'] = $mixedValue;
+
+        if(!is_array($aInput['attrs']))
+            $aInput['attrs'] = array();
+
+        $aInput['attrs'] = array_merge($aInput['attrs'], array(
+            'bx-index' => $iIndex,
+        ));
+
+        return $this->genInput($aInput);
+    }
+
+    protected function genCustomInputAnswersIndex($aInput, $mixedValue = '')
+    {        
+        $aInput['type'] = 'hidden';
+        $aInput['name'] .= '_ind';
         $aInput['value'] = $mixedValue;
 
         return $this->genInput($aInput);

+ 19 - 3
modules/boonex/feedback/classes/BxFdbGridQuestions.php

@@ -56,7 +56,15 @@ class BxFdbGridQuestions extends BxTemplGrid
         $oForm->initChecker();
 
         if($oForm->isSubmittedAndValid()) {
-            if(($iId = $oForm->insert()) !== false) {
+            $iNow = time();
+
+            $aValsToAdd = array(
+                'author' => bx_get_logged_profile_id(),
+                'added' => $iNow,
+                'changed' => $iNow
+            );
+
+            if(($iId = $oForm->insert($aValsToAdd)) !== false) {
                 $this->_oModule->onAddQuestion($iId);
 
                 $aRes = array('grid' => $this->getCode(false), 'blink' => $iId);
@@ -102,7 +110,11 @@ class BxFdbGridQuestions extends BxTemplGrid
         $oForm->initChecker($aQuestion);
 
         if($oForm->isSubmittedAndValid()) {
-            if($oForm->update($iId) !== false) {
+            $aValsToAdd = array(
+                'changed' => time()
+            );
+
+            if($oForm->update($iId, $aValsToAdd) !== false) {
                 $this->_oModule->onEditQuestion($iId);
 
                 $aRes = array('grid' => $this->getCode(false), 'blink' => $iId);
@@ -125,9 +137,13 @@ class BxFdbGridQuestions extends BxTemplGrid
 
     protected function _delete($mixedId)
     {
+        $aQuestion = $this->_oModule->_oDb->getQuestions(array('type' => 'id', 'id' => $mixedId));
+        if(empty($aQuestion) || !is_array($aQuestion))
+            return false;
+
         $mixedResult = parent::_delete($mixedId);
         if($mixedResult !== false)
-            $this->_oModule->onDeleteQuestion($mixedId);
+            $this->_oModule->onDeleteQuestion($aQuestion);
 
         return $mixedResult;
     }

+ 55 - 34
modules/boonex/feedback/classes/BxFdbModule.php

@@ -149,11 +149,10 @@ class BxFdbModule extends BxBaseModGeneralModule
     {
         $CNF = &$this->_oConfig->CNF;
 
-        $bModeAni = $this->_oConfig->isModeAni();
         $iOwnerId = (int)$aEvent['owner_id'];
 
         $aAnswer = $this->_oDb->getAnswers(array('type' => 'id_for_profile', 'id' => $aEvent['object_id'], 'profile_id' => $iOwnerId));
-        if(empty($aAnswer) || !is_array($aAnswer) || ($bModeAni && (int)$aAnswer[$CNF['FIELD_ANS_IMPORTANT']] == 0))
+        if(empty($aAnswer) || !is_array($aAnswer) || ($this->_oConfig->isModeNio() && (int)$aAnswer[$CNF['FIELD_ANS_IMPORTANT']] == 0))
             return false;
 
         $aQuestion = $this->_oDb->getQuestions(array('type' => 'id', 'id' => $aAnswer['question_id']));
@@ -164,7 +163,7 @@ class BxFdbModule extends BxBaseModGeneralModule
             return false;
 
         //--- Title & Description
-        $sTitle = !empty($aAnswer[$CNF['FIELD_TEXT']]) ? $aAnswer[$CNF['FIELD_TEXT']] : '';
+        $sTitle = !empty($aAnswer[$CNF['FIELD_ANS2USR_TEXT']]) ? $aAnswer[$CNF['FIELD_ANS2USR_TEXT']] : '';
 
         $bCache = true;
         $aContent = $this->_getContentForTimelineAnswer($aEvent, $aQuestion, $aAnswer, $aBrowseParams);
@@ -265,6 +264,9 @@ class BxFdbModule extends BxBaseModGeneralModule
         if(empty($aAnswer) || !is_array($aAnswer))
             return array();
 
+        if($this->_oConfig->isModeNio() && (int)$aAnswer[$CNF['FIELD_ANS_IMPORTANT']] == 0)
+            return array();
+
         $sEntryUrl = BX_DOL_URL_ROOT;
         $sEntryCaption = strmaxtextlen($aQuestion[$CNF['FIELD_TEXT']], 20, '...');
 
@@ -301,11 +303,26 @@ class BxFdbModule extends BxBaseModGeneralModule
         bx_alert($this->getName(), 'edited_question', $iId);
     }
 
-    public function onDeleteQuestion($iId)
+    public function onDeleteQuestion($aQuestion)
     {
-        $this->_oDb->deleteAnswer(array('question_id' => $iId));
+        $CNF = &$this->_oConfig->CNF;
+
+        $oLanguage = BxDolStudioLanguagesUtils::getInstance();
+        $oLanguage->deleteLanguageString($aQuestion[$CNF['FIELD_TEXT']]);
+
+        $aAnswers = $this->_oDb->getAnswers(array('type' => 'question_id', 'question_id' => $aQuestion[$CNF['FIELD_ID']]));
+        if(!empty($aAnswers) && is_array($aAnswers))
+            foreach($aAnswers as $aAnswer) {
+                $oLanguage->deleteLanguageString($aAnswer[$CNF['FIELD_ANS_TITLE']]);
+
+                $this->_oDb->deleteAnswer(array('id' => $aAnswer[$CNF['FIELD_ANS_ID']]));
+                $this->_oDb->deleteAnswer2User(array('answer_id' => $aAnswer[$CNF['FIELD_ANS_ID']]));
+            }
 
-        bx_alert($this->getName(), 'deleted_question', $iId);
+        bx_alert($this->getName(), 'deleted_question', $aQuestion[$CNF['FIELD_ID']], false, array(
+            'question' => $aQuestion,
+            'answers' => $aAnswers,
+        ));
     }
 
     public function onAddAnswer($iQuestionId, $iAnswerId, $iProfileId = false, $aParams = array())
@@ -313,25 +330,23 @@ class BxFdbModule extends BxBaseModGeneralModule
         $CNF = &$this->_oConfig->CNF;
 
         $sModule = $this->getName();
-        $bModeAni = $this->_oConfig->isModeAni();
-
         $aAnswer = $this->_oDb->getAnswers(array('type' => 'id', 'id' => $iAnswerId));
 
-        if(!$bModeAni || (int)$aAnswer[$CNF['FIELD_ANS_IMPORTANT']] > 0) {
-            bx_alert($sModule, 'added_answer', $iAnswerId, $iProfileId, array_merge(array(
-                'object_author_id' => $iProfileId,
-                'question_id' => $iQuestionId, 
-                'data' => $aAnswer['data'], 
-                'votes' => $aAnswer['votes']
-            ), $aParams));
-
-            bx_alert($sModule, 'added_answer_notif', $iQuestionId, $iProfileId, array_merge(array(
-                'object_author_id' => $iProfileId,
-                'subobject_id' => $iAnswerId, 
-                'data' => $aAnswer['data'], 
-                'votes' => $aAnswer['votes']
-            ), $aParams));
-        }
+        bx_alert($sModule, 'added_answer', $iAnswerId, $iProfileId, array_merge(array(
+            'object_author_id' => $iProfileId,
+            'question_id' => $iQuestionId, 
+            'important' => $aAnswer['important'],
+            'data' => $aAnswer['data'], 
+            'votes' => $aAnswer['votes']
+        ), $aParams));
+
+        bx_alert($sModule, 'added_answer_notif', $iQuestionId, $iProfileId, array_merge(array(
+            'object_author_id' => $iProfileId,
+            'subobject_id' => $iAnswerId, 
+            'important' => $aAnswer['important'],
+            'data' => $aAnswer['data'], 
+            'votes' => $aAnswer['votes']
+        ), $aParams));
     }
 
     public function onDeleteAnswer($iQuestionId, $iAnswerId, $iProfileId = false, $aParams = array())
@@ -339,15 +354,21 @@ class BxFdbModule extends BxBaseModGeneralModule
         $CNF = &$this->_oConfig->CNF;
 
         $sModule = $this->getName();
-        $bModeAni = $this->_oConfig->isModeAni();
-
         $aAnswer = $this->_oDb->getAnswers(array('type' => 'id', 'id' => $iAnswerId));
 
-        if(!$bModeAni || (int)$aAnswer[$CNF['FIELD_ANS_IMPORTANT']] > 0) {
-            bx_alert($sModule, 'deleted_answer', $iAnswerId, $iProfileId, array_merge(array('question_id' => $iQuestionId), $aParams));
-
-            bx_alert($sModule, 'deleted_answer_notif', $iQuestionId, $iProfileId, array_merge(array('subobject_id' => $iAnswerId), $aParams));
-        }
+        bx_alert($sModule, 'deleted_answer', $iAnswerId, $iProfileId, array_merge(array(
+            'question_id' => $iQuestionId,
+            'important' => $aAnswer['important'],
+            'data' => $aAnswer['data'], 
+            'votes' => $aAnswer['votes']
+        ), $aParams));
+
+        bx_alert($sModule, 'deleted_answer_notif', $iQuestionId, $iProfileId, array_merge(array(
+            'subobject_id' => $iAnswerId,
+            'important' => $aAnswer['important'],
+            'data' => $aAnswer['data'], 
+            'votes' => $aAnswer['votes']
+        ), $aParams));
     }
 
 
@@ -361,7 +382,7 @@ class BxFdbModule extends BxBaseModGeneralModule
     	$sUrl = BX_DOL_URL_ROOT;
 
         //--- Title
-        $sTitle = strmaxtextlen($aQuestion[$CNF['FIELD_TEXT']], 20, '...');
+        $sTitle = strmaxtextlen(_t($aQuestion[$CNF['FIELD_TEXT']]), 20, '...');
 
     	return array(
             'sample' => isset($CNF['T']['txt_sample_question_single_with_article']) ? $CNF['T']['txt_sample_question_single_with_article'] : $CNF['T']['txt_sample_question_single'],
@@ -390,10 +411,10 @@ class BxFdbModule extends BxBaseModGeneralModule
             $sUrl = $oOwnerProfile->getUrl();
 
         //--- Title
-        $sTitle = strmaxtextlen($aQuestion[$CNF['FIELD_TEXT']], 20, '...');
+        $sTitle = strmaxtextlen(_t($aQuestion[$CNF['FIELD_TEXT']]), 20, '...');
 
-        $sTextKey = '_bx_feedback_txt_answer_format' . (empty($aAnswer[$CNF['FIELD_TEXT']]) ? '_wo_comment' : '');
-        $sText = _t($sTextKey, $aAnswer[$CNF['FIELD_ANS_TITLE']], $aAnswer[$CNF['FIELD_TEXT']]); 
+        $sTextKey = '_bx_feedback_txt_answer_format' . (empty($aAnswer[$CNF['FIELD_ANS2USR_TEXT']]) ? '_wo_comment' : '');
+        $sText = _t($sTextKey, _t($aAnswer[$CNF['FIELD_ANS_TITLE']]), $aAnswer[$CNF['FIELD_ANS2USR_TEXT']]); 
 
     	return array(
             'sample' => isset($CNF['T']['txt_sample_answer_single_with_article']) ? $CNF['T']['txt_sample_answer_single_with_article'] : $CNF['T']['txt_sample_answer_single'],

+ 2 - 2
modules/boonex/feedback/classes/BxFdbTemplate.php

@@ -39,7 +39,7 @@ class BxFdbTemplate extends BxBaseModGeneralTemplate
                 'js_object' => $sJsObject,
                 'id' => $aAnswer['id'],
                 'question_id' => $iQuestion,
-                'title' => bx_process_output($aAnswer['title']),
+                'title' => bx_process_output(_t($aAnswer['title'])),
                 'bx_if:show_checked' => array(
                     'condition' => (int)$aAnswer['checked'] > 0,
                     'content' => array()
@@ -51,7 +51,7 @@ class BxFdbTemplate extends BxBaseModGeneralTemplate
         $this->addJsTranslation(array('_bx_feedback_txt_enter_text'));
         return $this->getJsCode('question') . $this->parseHtmlByName('question.html', array(
             'html_id' => $this->_oConfig->getHtmlIds('question') . $iQuestion,
-            'text' => bx_process_output($aQuestion['text']),
+            'text' => bx_process_output(_t($aQuestion['text'])),
             'bx_repeat:answers' => $aTmplVarsAnswers,
         ));
     }

+ 1 - 1
modules/boonex/feedback/install/langs/en.xml

@@ -5,7 +5,7 @@
     <!-- Options -->
     <string name="_bx_feedback_option_enable_questions"><![CDATA[Enable questions]]></string>
     <string name="_bx_feedback_option_question_lifetime"><![CDATA[Default question lifetime (in days).]]></string>
-    <string name="_bx_feedback_option_answer_notify_important"><![CDATA[Notify about 'important' answers only.]]></string>
+    <string name="_bx_feedback_option_answer_ntf_important_only"><![CDATA[Notify about 'important' answers only.]]></string>
 
     <!-- Pages -->
     <string name="_bx_feedback_page_block_title_question"><![CDATA[Question]]></string>

+ 2 - 2
modules/boonex/feedback/install/sql/enable.sql

@@ -13,7 +13,7 @@ INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `c
 ('bx_feedback_enable_questions', 'on', @iCategId, '_bx_feedback_option_enable_questions', 'checkbox', '', '', '', 1),
 ('bx_feedback_question_lifetime', '', @iCategId, '_bx_feedback_option_question_lifetime', 'digit', '', '', '', 2),
 
-('bx_feedback_enable_answer_notify_important', 'on', @iCategId, '_bx_feedback_option_answer_notify_important', 'checkbox', '', '', '', 10);
+('bx_feedback_enable_answer_ntf_important_only', 'on', @iCategId, '_bx_feedback_option_answer_ntf_important_only', 'checkbox', '', '', '', 10);
 
 
 -- PAGE: add block on homepage
@@ -36,7 +36,7 @@ INSERT INTO `sys_objects_grid` (`object`, `source_type`, `source`, `table`, `fie
 INSERT INTO `sys_grid_fields` (`object`, `name`, `title`, `width`, `translatable`, `chars_limit`, `params`, `order`) VALUES
 ('bx_feedback_questions', 'checkbox', '_sys_select', '2%', 0, '', '', 1),
 ('bx_feedback_questions', 'switcher', '_bx_feedback_grid_question_column_switcher', '5%', 0, '', '', 2),
-('bx_feedback_questions', 'text', '_bx_feedback_grid_question_column_text', '63%', 0, 32, '', 3),
+('bx_feedback_questions', 'text', '_bx_feedback_grid_question_column_text', '63%', 1, 32, '', 3),
 ('bx_feedback_questions', 'added', '_bx_feedback_grid_question_column_title_added', '10%', 0, 16, '', 4),
 ('bx_feedback_questions', 'actions', '', '20%', 0, '', '', 5);
 

+ 3 - 3
modules/boonex/feedback/install/sql/install.sql

@@ -37,7 +37,7 @@ CREATE TABLE IF NOT EXISTS `bx_fdb_answers2users` (
 
 -- FORMS
 INSERT INTO `sys_objects_form`(`object`, `module`, `title`, `action`, `form_attrs`, `table`, `key`, `uri`, `uri_title`, `submit_name`, `params`, `deletable`, `active`, `override_class_name`, `override_class_file`) VALUES 
-('bx_feedback_question', 'bx_feedback', '_bx_feedback_form_question', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'bx_fdb_questions', 'id', '', '', 'a:1:{i:0;s:9:"do_submit";}', 'a:1:{s:14:"checker_helper";s:30:"BxFdbFormQuestionCheckerHelper";}', 0, 1, 'BxFdbFormQuestion', 'modules/boonex/feedback/classes/BxFdbFormQuestion.php');
+('bx_feedback_question', 'bx_feedback', '_bx_feedback_form_question', '', 'a:1:{s:7:"enctype";s:19:"multipart/form-data";}', 'bx_fdb_questions', 'id', '', '', 'a:1:{i:0;s:9:"do_submit";}', 'a:2:{s:7:"checker";s:24:"BxFdbFormQuestionChecker";s:14:"checker_helper";s:30:"BxFdbFormQuestionCheckerHelper";}', 0, 1, 'BxFdbFormQuestion', 'modules/boonex/feedback/classes/BxFdbFormQuestion.php');
 
 INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`, `title`) VALUES 
 ('bx_feedback_question', 'bx_feedback_question_add', 'bx_feedback', 0, '_bx_feedback_form_question_display_add'),
@@ -46,8 +46,8 @@ INSERT INTO `sys_form_displays`(`object`, `display_name`, `module`, `view_mode`,
 ('bx_feedback_question', 'bx_feedback_question_view', 'bx_feedback', 1, '_bx_feedback_form_question_display_view');
 
 INSERT INTO `sys_form_inputs`(`object`, `module`, `name`, `value`, `values`, `checked`, `type`, `caption_system`, `caption`, `info`, `required`, `collapsed`, `html`, `attrs`, `attrs_tr`, `attrs_wrapper`, `checker_func`, `checker_params`, `checker_error`, `db_pass`, `db_params`, `editable`, `deletable`) VALUES 
-('bx_feedback_question', 'bx_feedback', 'text', '', '', 0, 'textarea', '_bx_feedback_form_question_input_sys_text', '_bx_feedback_form_question_input_text', '', 1, 0, 0, '', '', '', 'Avail', '', '_bx_feedback_form_question_input_text_err', 'Xss', '', 1, 0),
-('bx_feedback_question', 'bx_feedback', 'answers', '', '', 0, 'custom', '_bx_feedback_form_question_input_sys_answers', '_bx_feedback_form_question_input_answers', '', 1, 0, 0, '', '', '', 'AvailAnswers', '', '_bx_feedback_form_question_input_answers_err', '', '', 1, 0),
+('bx_feedback_question', 'bx_feedback', 'text', '', '', 0, 'textarea_translatable', '_bx_feedback_form_question_input_sys_text', '_bx_feedback_form_question_input_text', '', 1, 0, 0, '', '', '', 'AvailTranslatable', 'a:1:{i:0;s:4:"text";}', '_bx_feedback_form_question_input_text_err', 'Xss', '', 1, 0),
+('bx_feedback_question', 'bx_feedback', 'answers', '', '', 0, 'list_translatable', '_bx_feedback_form_question_input_sys_answers', '_bx_feedback_form_question_input_answers', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('bx_feedback_question', 'bx_feedback', 'lifetime', '', '', 0, 'text', '_bx_feedback_form_question_input_sys_lifetime', '_bx_feedback_form_question_input_lifetime', '_bx_feedback_form_question_input_lifetime_inf', 0, 0, 0, '', '', '', '', '', '', 'Int', '', 1, 0),
 ('bx_feedback_question', 'bx_feedback', 'allow_view_to', '', '', 0, 'custom', '_bx_feedback_form_question_input_sys_allow_view_to', '_bx_feedback_form_question_input_allow_view_to', '', 1, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('bx_feedback_question', 'bx_feedback', 'delete_confirm', 1, '', 0, 'checkbox', '_bx_feedback_form_question_input_sys_delete_confirm', '_bx_feedback_form_question_input_delete_confirm', '_bx_feedback_form_question_input_delete_confirm_info', 1, 0, 0, '', '', '', 'Avail', '', '_bx_feedback_form_question_input_delete_confirm_error', '', '', 1, 0),

+ 21 - 11
modules/boonex/feedback/js/question.js

@@ -19,11 +19,6 @@ function BxFdbQuestion(oOptions) {
     this._oRequestParams = oOptions.oRequestParams == undefined ? {} : oOptions.oRequestParams;
 }
 
-BxFdbQuestion.prototype.answerOnType = function(oEvent) {
-    var oText = $(oEvent.target);
-    oText.parents('.bx-form-input-answer:first').find("input[type = 'checkbox']").val(oText.val().trim());
-};
-
 BxFdbQuestion.prototype.answerOnSelect = function(oElement, iQuestion) {
     var $this = this;
 
@@ -47,21 +42,36 @@ BxFdbQuestion.prototype.answerOnSelect = function(oElement, iQuestion) {
     bx_prompt(_t('_bx_feedback_txt_enter_text'), '', fOnOk);
 };
 
+
+/**
+ * Methods for 'Answers' custom field.
+ */
+/*
+BxFdbQuestion.prototype.answerOnType = function(oEvent, sName) {
+    var oParent = $(oEvent.target).parents('.bx-form-input-answer:first');
+    oParent.find("input[type = 'checkbox']").val(oParent.find("input[name = '" + sName + "']").val().trim());
+};
+*/
+
 BxFdbQuestion.prototype.answerAdd = function(oButton, sName) {
-    var oButton = $(oButton);
+    var oField = $(oButton).parents('#bx-form-element-' + sName);
+
+    var oIndex = oField.find("input[type = 'hidden'][name = '" + sName + "_ind']");
+    var iIndex = oIndex.val();
 
-    var oSubentry = oButton.parents('#bx-form-element-' + sName).find('.bx-form-input-answer:first').clone();
-    oSubentry.find("input[type = 'text']").val('');
-    oSubentry.find("input[type = 'checkbox']").val('1').removeAttr('checked');
-    oSubentry.find("input[type = 'hidden']").remove();
+    var sSample = oField.find('.bx-form-input-answer-sample:first').html().replace(/\|x\|/g, iIndex);
 
-    oButton.parents('.bx-form-input-answer-add:first').before(oSubentry);
+    oField.find('.bx-form-input-answer:last').after($(sSample));
+    oIndex.val(parseInt(iIndex) + 1);
 };
 
 BxFdbQuestion.prototype.answerDelete = function(oButton) {
     $(oButton).parents('.bx-form-input-answer:first').remove();
 };
 
+/**
+ * Internal methods.
+ */
 BxFdbQuestion.prototype._getDefaultParams = function() {
     var oDate = new Date();
     return {_t:oDate.getTime()};

+ 14 - 2
modules/boonex/feedback/template/css/question.css

@@ -2,22 +2,34 @@
 .bx-form-input-answer {
     position: relative;
 }
+.bx-form-input-answer-sample {
+    display: none;
+}
 .bx-form-input-answer-row {
     display: -webkit-flex;
     display: flex;
     -webkit-align-items: center;
     align-items: center;
 }
-.bx-form-input-answer-cell.bx-fi-sc-txt {
+.bx-form-input-answer-row.bx-fi-ar-chb {
+    margin-top: 0.25rem;
+}
+.bx-form-input-answer-row.bx-fi-ar-chb input {
+    margin-left: 0;
+}
+.bx-form-input-answer-cell.bx-fi-ac-txt {
     -webkit-flex: 1 1 auto;
     -ms-flex: 1 1 auto;
     flex: 1 1 auto;
 }
-.bx-form-input-answer-cell.bx-fi-sc-btn {
+.bx-form-input-answer-cell.bx-fi-ac-btn {
     -webkit-flex: 0 0 auto;
     -ms-flex: 0 0 auto;
     flex: 0 0 auto;
 }
+.bx-form-input-answer-cell.bx-fi-ac-txt .bx-form-input-text {
+    margin-top: 0;
+}
 
 .bx-form-input-answer-add {
     text-align: right;

+ 9 - 0
modules/boonex/feedback/template/form_answer.html

@@ -0,0 +1,9 @@
+<div class="__class__ bx-def-padding-thd-topbottom">
+    <div class="bx-form-input-answer-row bx-fi-ar-txt">
+        <div class="bx-form-input-answer-cell bx-fi-ac-txt">__input_text__</div>
+        <div class="bx-form-input-answer-cell bx-fi-ac-btn bx-def-padding-sec-left">
+            <a class="bx-btn" href="javascript:void(0)" onclick="javascript:__js_object__.answerDelete(this)"><i class="sys-icon times"></i></a>
+        </div>
+    </div>
+    <div class="bx-form-input-answer-row bx-fi-ar-chb">__input_checkbox__</div>
+</div>

+ 3 - 11
modules/boonex/feedback/template/form_answers.html

@@ -1,14 +1,6 @@
 <div class="bx-form-input-answers">
-    <bx_repeat:answers>
-        <div class="bx-form-input-answer bx-def-padding-thd-topbottom">
-            <div class="bx-form-input-answer-row bx-def-margin-thd-top-auto">
-                <div class="bx-form-input-answer-cell bx-fi-sc-txt">__input_text__</div>
-                <div class="bx-form-input-answer-cell bx-fi-sc-btn bx-def-padding-sec-left">
-                    <a class="bx-btn" href="javascript:void(0)" onclick="javascript:__js_object__.answerDelete(this)"><i class="sys-icon times"></i></a>
-                </div>
-            </div>
-            <div class="bx-form-input-answer-row bx-def-margin-thd-top-auto">__input_checkbox__</div>
-        </div>
-    </bx_repeat:answers>
+    __index__
+    <div class="bx-form-input-answer-sample">__sample__</div>
+    __answers__
     <div class="bx-form-input-answer-add">__btn_add__</div>
 </div>

+ 7 - 13
modules/boonex/groups/classes/BxGroupsConfig.php

@@ -9,25 +9,19 @@
  * @{
  */
 
-class BxGroupsConfig extends BxBaseModProfileConfig
+class BxGroupsConfig extends BxBaseModGroupsConfig
 {
     function __construct($aModule)
     {
         parent::__construct($aModule);
 
-        $aMenuItems2Methods = array (
+        $this->_aMenuItems2MethodsActions = array_merge($this->_aMenuItems2MethodsActions, array(
             'view-group-profile' => 'checkAllowedView',
             'edit-group-profile' => 'checkAllowedEdit',
             'edit-group-cover' => 'checkAllowedChangeCover',
             'invite-to-group' => 'checkAllowedInvite',
-            'delete-group-profile' => 'checkAllowedDelete',
-            'profile-fan-add' => 'checkAllowedFanAdd',
-            'profile-fan-remove' => 'checkAllowedFanRemove',
-            'profile-subscribe-add' => 'checkAllowedSubscribeAdd',
-            'profile-subscribe-remove' => 'checkAllowedSubscribeRemove',
-            'profile-actions-more' => 'checkAllowedViewMoreMenu',
-            'convos-compose' => 'checkAllowedCompose',
-        );
+            'delete-group-profile' => 'checkAllowedDelete'
+        ));
 
         $this->CNF = array (
 
@@ -149,9 +143,9 @@ class BxGroupsConfig extends BxBaseModProfileConfig
 
             // menu items which visibility depends on custom visibility checking
             'MENU_ITEM_TO_METHOD' => array (
-                'bx_groups_view_actions' => $aMenuItems2Methods,
-                'bx_groups_view_actions_more' => $aMenuItems2Methods,
-                'bx_groups_view_actions_all' => $aMenuItems2Methods,
+                'bx_groups_view_actions' => $this->_aMenuItems2MethodsActions,
+                'bx_groups_view_actions_more' => $this->_aMenuItems2MethodsActions,
+                'bx_groups_view_actions_all' => $this->_aMenuItems2MethodsActions,
             ),
 
             // informer messages

+ 2 - 2
modules/boonex/organizations/classes/BxOrgsModule.php

@@ -79,9 +79,9 @@ class BxOrgsModule extends BxBaseModGroupsModule
         $oAccount->updateProfileContextAuto();
     }
 
-    public function checkAllowedCompose (&$aDataEntry, $isPerformAction = false)
+    public function checkAllowedContact($aDataEntry, $isPerformAction = false)
     {
-        return BxBaseModProfileModule::checkAllowedCompose ($aDataEntry, $isPerformAction);
+        return BxBaseModProfileModule::checkAllowedContact($aDataEntry, $isPerformAction);
     }
 }
 

+ 7 - 13
modules/boonex/spaces/classes/BxSpacesConfig.php

@@ -9,25 +9,19 @@
  * @{
  */
 
-class BxSpacesConfig extends BxBaseModProfileConfig
+class BxSpacesConfig extends BxBaseModGroupsConfig
 {
     function __construct($aModule)
     {
         parent::__construct($aModule);
 
-        $aMenuItems2Methods = array (
+        $this->_aMenuItems2MethodsActions = array_merge($this->_aMenuItems2MethodsActions, array(
             'view-space-profile' => 'checkAllowedView',
             'edit-space-profile' => 'checkAllowedEdit',
             'edit-space-cover' => 'checkAllowedChangeCover',
             'invite-to-space' => 'checkAllowedInvite',
-            'delete-space-profile' => 'checkAllowedDelete',
-            'profile-fan-add' => 'checkAllowedFanAdd',
-            'profile-fan-remove' => 'checkAllowedFanRemove',
-            'profile-subscribe-add' => 'checkAllowedSubscribeAdd',
-            'profile-subscribe-remove' => 'checkAllowedSubscribeRemove',
-            'profile-actions-more' => 'checkAllowedViewMoreMenu',
-            'convos-compose' => 'checkAllowedCompose',
-        );
+            'delete-space-profile' => 'checkAllowedDelete'
+        ));
 
         $this->CNF = array (
 
@@ -150,9 +144,9 @@ class BxSpacesConfig extends BxBaseModProfileConfig
 
             // menu items which visibility depends on custom visibility checking
             'MENU_ITEM_TO_METHOD' => array (
-                'bx_spaces_view_actions' => $aMenuItems2Methods,
-                'bx_spaces_view_actions_more' => $aMenuItems2Methods,
-                'bx_spaces_view_actions_all' => $aMenuItems2Methods,
+                'bx_spaces_view_actions' => $this->_aMenuItems2MethodsActions,
+                'bx_spaces_view_actions_more' => $this->_aMenuItems2MethodsActions,
+                'bx_spaces_view_actions_all' => $this->_aMenuItems2MethodsActions,
             ),
 
             // informer messages

+ 1 - 1
modules/boonex/timeline/classes/BxTimelineTemplate.php

@@ -1456,7 +1456,7 @@ class BxTimelineTemplate extends BxBaseModNotificationsTemplate
         $bSticked = (int)$aEvent['sticked'] > 0;
         $bPromoted = (int)$aEvent['promoted'] > 0;
 
-        $sClass = $sStylePrefix . '-view-sizer';
+        $sClass = $aEvent['type'] . ' ' . (!empty($aEvent['action']) ? $aEvent['action'] . ' ' : '') . $sStylePrefix . '-view-sizer';
         if($bViewOutline) {
             $sClass = $sStylePrefix . '-grid-item-sizer';
             if($bPinned || $bSticked || $bPromoted) {

+ 83 - 38
studio/classes/BxDolStudioForm.php

@@ -85,81 +85,126 @@ class BxDolStudioForm extends BxBaseFormView
         return (int)$iId;
     }
 
-	function getCleanValue ($sName)
+    function getCleanValue ($sName)
     {
-		$aResult = parent::getCleanValue($sName);
+        $aResult = parent::getCleanValue($sName);
 
-		$a = isset($this->aInputs[$sName]) ? $this->aInputs[$sName] : false;
-		if($a && !empty($a['reverse']) && !empty($a['values']))
-			$aResult = array_diff(array_keys($a['values']), (is_array($aResult) ? $aResult : array()));
+        $a = isset($this->aInputs[$sName]) ? $this->aInputs[$sName] : false;
+        if($a && !empty($a['reverse']) && !empty($a['values']))
+            $aResult = array_diff(array_keys($a['values']), (is_array($aResult) ? $aResult : array()));
 
-		return $aResult;
+        return $aResult;
     }
 
     protected function processTranslations($sType = 'insert')
     {
-        $aType2Method = array(
-            'insert' => 'addLanguageString',
-            'update' => 'updateLanguageString'
-        );
-
-        $oLanguage = BxDolStudioLanguagesUtils::getInstance();
-
         $bResult = false;
         foreach($this->aInputs as $sName => $aInput) {
-            if(!in_array($aInput['type'], array('text_translatable', 'textarea_translatable')))
+            if(!in_array($aInput['type'], array('text_translatable', 'textarea_translatable', 'list_translatable')))
                 continue; 
 
-            $sKey = $this->getCleanValue($sName);
-            if(empty($sKey))
+            if($aInput['type'] == 'list_translatable') {
+                $iIndex = (int)$this->getCleanValue($sName . '_ind');
+                for($i = 0; $i < $iIndex; $i++) 
+                    if($this->_processTranslationsByName($sType, $sName . '_' . $i) !== false)
+                        $bResult = true;
+
                 continue;
+            }
 
-            $aValues = $this->getTranslationsValues($sType, $sName);
-            foreach($aValues as $iLanguageId => $sString)
-                if($oLanguage->{$aType2Method[$sType]}($sKey, $sString, $iLanguageId))
-                    $bResult = true;
+            if($this->_processTranslationsByName($sType, $sName) !== false)
+                $bResult = true;
         }
 
         return $bResult;
     }
 
-    protected function processTranslationsKey($sType = 'insert')
+    protected function _processTranslationsByName($sType, $sName)
     {
         $oLanguage = BxDolStudioLanguagesUtils::getInstance();
 
+        $aType2Method = array(
+            'insert' => 'addLanguageString',
+            'update' => 'updateLanguageString'
+        );
+
+        $bResult = false;
+
+        $sKey = $this->getCleanValue($sName);
+        if(empty($sKey))
+            return $bResult;
+
+        $aValues = $this->getTranslationsValues($sType, $sName);
+        foreach($aValues as $iLanguageId => $sString)
+            if($oLanguage->{$aType2Method[$sType]}($sKey, $sString, $iLanguageId))
+                $bResult = true;
+
+        return $bResult;
+    }
+
+    protected function processTranslationsKey($sType = 'insert')
+    {
         foreach($this->aInputs as $sName => $aInput) {
-            if(!in_array($aInput['type'], array('text_translatable', 'textarea_translatable'))) 
+            if(!in_array($aInput['type'], array('text_translatable', 'textarea_translatable', 'list_translatable'))) 
                 continue;
 
-            //--- Fill in the Key if some values are available only. 
-            $sKey = '';
-            $sKeyDefault = $this->getCleanValue($sName);
+            if($aInput['type'] == 'list_translatable') {
+                $iIndex = (int)$this->getCleanValue($sName . '_ind');
+                for($i = 0; $i < $iIndex; $i++)
+                    $this->_processTranslationsKeyByName($sType, $sName . '_' . $i);
 
-            $aValues = $this->getTranslationsValues($sType, $sName, true);
-            if(!empty($aValues) && is_array($aValues))
-                $sKey = $this->getTranslationsKey($sType, $sName, $sKeyDefault);
-
-            //--- Remove Key if it exists but no values for him
-            if(empty($sKey) && !empty($sKeyDefault)) 
-                $oLanguage->deleteLanguageString($sKeyDefault);
+                continue;
+            }
 
-            BxDolForm::setSubmittedValue($sName, $sKey, $this->aFormAttrs['method']);
+            $this->_processTranslationsKeyByName($sType, $sName);
         }
     }
 
-    protected function processTranslationsValues ()
+    protected function _processTranslationsKeyByName($sType, $sName)
     {
-        $aLanguages = BxDolStudioLanguagesUtils::getInstance()->getLanguages();
+        $oLanguage = BxDolStudioLanguagesUtils::getInstance();
+
+        //--- Fill in the Key if some values are available only. 
+        $sKey = '';
+        $sKeyDefault = $this->getCleanValue($sName);
+
+        $aValues = $this->getTranslationsValues($sType, $sName, true);
+        if(!empty($aValues) && is_array($aValues))
+            $sKey = $this->getTranslationsKey($sType, $sName, $sKeyDefault);
+
+        //--- Remove Key if it exists but no values for him
+        if(empty($sKey) && !empty($sKeyDefault)) 
+            $oLanguage->deleteLanguageString($sKeyDefault);
 
+        BxDolForm::setSubmittedValue($sName, $sKey, $this->aFormAttrs['method']);
+    }
+            
+    protected function processTranslationsValues()
+    {
         foreach($this->aInputs as $sName => $aInput) {
-            if(!in_array($aInput['type'], array('text_translatable', 'textarea_translatable')))
+            if(!in_array($aInput['type'], array('text_translatable', 'textarea_translatable', 'list_translatable')))
+                continue;
+
+            if($aInput['type'] == 'list_translatable') {
+                $iIndex = (int)$this->getCleanValue($sName . '_ind');
+                for($i = 0; $i < $iIndex; $i++)
+                    $this->_processTranslationsValuesByName($sName . '_' . $i);
+
                 continue;
+            }
 
-            foreach($aLanguages as $sLangName => $sLangTitle)
-                $this->aInputs[$sName]['values'][$sLangName] = BxDolForm::getSubmittedValue($sName . '-' . $sLangName, $this->aFormAttrs['method']);
+            $this->_processTranslationsValuesByName($sName);
         }
     }
 
+    protected function _processTranslationsValuesByName($sName)
+    {
+        $aLanguages = BxDolStudioLanguagesUtils::getInstance()->getLanguages();
+
+        foreach($aLanguages as $sLangName => $sLangTitle)
+            $this->aInputs[$sName]['values'][$sLangName] = BxDolForm::getSubmittedValue($sName . '-' . $sLangName, $this->aFormAttrs['method']);
+    }
+
     protected function getTranslationsKey($sType, $sName, $sValue)
     {
         $iRand = time();

+ 0 - 3
studio/classes/BxDolStudioToolsAudit.php

@@ -71,9 +71,6 @@ class BxDolStudioToolsAudit extends BxDol
         $this->sMinMysqlVer = '5.5.3';
         $this->aMysqlOptimizationSettings = array (
             'key_buffer_size' => array('op' => '>=', 'val' => 128*1024, 'type' => 'bytes'),
-            'query_cache_limit' => array('op' => '>=', 'val' => 1000000),
-            'query_cache_size' => array('op' => '>=', 'val' => 16*1024*1024, 'type' => 'bytes'),
-            'query_cache_type' => array('op' => 'strcasecmp', 'val' => 'on'),
             'max_heap_table_size' => array('op' => '>=', 'val' => 16*1024*1024, 'type' => 'bytes'),
             'tmp_table_size' => array('op' => '>=', 'val' => 16*1024*1024, 'type' => 'bytes'),
             'thread_cache_size ' => array('op' => '>', 'val' => 0),