Commits

Ivica Nedeljkovic  committed 69542a1 Merge

Merge with default

  • Participants
  • Parent commits 96d3618, 3cb7c3d
  • Branches zurmoUpgrade

Comments (0)

Files changed (15)

File app/protected/extensions/zurmoinc/framework/dataproviders/RedBeanModelDataProvider.php

         public function calculateTotalItemCount()
         {
             $joinTablesAdapter = new RedBeanModelJoinTablesQueryAdapter($this->modelClassName);
-            $where = $this->makeWhere($this->modelClassName, $this->searchAttributeData, $joinTablesAdapter);
-            $modelClassName = $this->modelClassName;
+            $where             = $this->makeWhere($this->modelClassName, $this->searchAttributeData, $joinTablesAdapter);
+            $modelClassName    = $this->modelClassName;
             return $modelClassName::getCount($joinTablesAdapter, $where, $this->modelClassName, $joinTablesAdapter->getSelectDistinct());
         }
 

File app/protected/extensions/zurmoinc/framework/forms/DynamicSearchForm.php

         {
             return array_merge(parent::rules(), array(
                                array('dynamicStructure', 'safe'),
-                               array('dynamicStructure',   'validateDynamicStructure', 'on' => 'validateDynamic'),
+                               array('dynamicStructure',   'validateDynamicStructure', 'on' => 'validateDynamic, validateSaveSearch'),
                                array('dynamicClauses',   'safe'),
-                               array('dynamicClauses',   'validateDynamicClauses', 'on' => 'validateDynamic'),
+                               array('dynamicClauses',   'validateDynamicClauses', 'on' => 'validateDynamic, validateSaveSearch'),
             ));
         }
 

File app/protected/extensions/zurmoinc/framework/tests/unit/SearchFormTest.php

                 array('dateTime__DateTime', 'safe'),
                 array('dateTime2__DateTime', 'safe'),
                 array('dynamicStructure', 'safe'),
-                array('dynamicStructure',   'validateDynamicStructure', 'on' => 'validateDynamic'),
+                array('dynamicStructure',   'validateDynamicStructure', 'on' => 'validateDynamic, validateSaveSearch'),
                 array('dynamicClauses',   'safe'),
-                array('dynamicClauses',   'validateDynamicClauses', 'on' => 'validateDynamic'),
+                array('dynamicClauses',   'validateDynamicClauses', 'on' => 'validateDynamic, validateSaveSearch'),
                 array('anyA', 'safe'),
                 array('ABName', 'safe'),
                 array('differentOperatorA', 'safe'),

File app/protected/extensions/zurmoinc/framework/tests/unit/SearchUtilTest.php

         public function testGetSearchAttributesFromSearchArrayChangeEmptyArrayValuesToNull()
         {
             $searchArray = array('testMultiSelectDropDown' => array('values' => array(0 => '')));
-            $resultArray = array('testMultiSelectDropDown' => array('values' => array()));
             $newArray = SearchUtil::getSearchAttributesFromSearchArray($searchArray);
-            $this->assertEquals($resultArray, $newArray);
+            $this->assertEquals(array(), $newArray);
 
             $searchArray = array('testMultiSelectDropDown' => array('values' => array(0 => null)));
             $newArray = SearchUtil::getSearchAttributesFromSearchArray($searchArray);
-            $this->assertEquals($resultArray, $newArray);
+            $this->assertEquals(array(), $newArray);
 
             $searchArray = array('testMultiSelectDropDown' => array('values' => array(0 => null, 1 => 'xyz')));
             $resultArray = array('testMultiSelectDropDown' => array('values' => array(0 => 'xyz')));
             $this->assertEquals($resultArray, $newArray);
 
             $searchArray = array('testDropDownAsMultiSelectDropDown' => array('value' => array(0 => '')));
-            $resultArray = array('testDropDownAsMultiSelectDropDown' => array('value' => array()));
             $newArray = SearchUtil::getSearchAttributesFromSearchArray($searchArray);
-            $this->assertEquals($resultArray, $newArray);
+            $this->assertEquals(array(), $newArray);
 
             $searchArray = array('testDropDownAsMultiSelectDropDown' => array('value' => array(0 => null)));
             $newArray = SearchUtil::getSearchAttributesFromSearchArray($searchArray);
-            $this->assertEquals($resultArray, $newArray);
+            $this->assertEquals(array(), $newArray);
 
             $searchArray = array('testDropDownAsMultiSelectDropDown' => array('value' => array(0 => null, 1 => 'xyz')));
             $resultArray = array('testDropDownAsMultiSelectDropDown' => array('value' => array(0 => 'xyz')));
                 'a' => array('values' => array(0 => '')),
             );
             $newArray = SearchUtil::getSearchAttributesFromSearchArrayForSavingExistingSearchCriteria($searchArray);
-            $this->assertEquals(array('a' => array('values' => array())), $newArray);
+            $this->assertEquals(array(), $newArray);
 
             $searchArray = array(
                 'a' => array('value' => array(0 => '')),
             );
             $newArray = SearchUtil::getSearchAttributesFromSearchArrayForSavingExistingSearchCriteria($searchArray);
-            $this->assertEquals(array('a' => array('value' => array())), $newArray);
+            $this->assertEquals(array(), $newArray);
 
             $searchArray = array(
                 'a' => array('values' => array(0 => null)),
             );
             $newArray = SearchUtil::getSearchAttributesFromSearchArrayForSavingExistingSearchCriteria($searchArray);
-            $this->assertEquals(array('a' => array('values' => array())), $newArray);
+            $this->assertEquals(array(), $newArray);
 
             $searchArray = array(
                 'a' => array('value' => array(0 => null)),
             );
             $newArray = SearchUtil::getSearchAttributesFromSearchArrayForSavingExistingSearchCriteria($searchArray);
-            $this->assertEquals(array('a' => array('value' => array())), $newArray);
+            $this->assertEquals(array(), $newArray);
 
             $searchArray = array(
                 'a' => array('value' => array(0 => null, 1 => 'xyz')),

File app/protected/extensions/zurmoinc/framework/utils/ModelDataProviderUtil.php

                 //Supporting the use of relatedAttributeName. Alternatively you can use relatedModelData to produce the same results.
                 else
                 {
-                    //Because a relatedAttributeName is in use, one of the joins gets skipped unless we manually process
-                    //it here.
-                    static::processJoinForRelatedModelDataWhenRelatedAttributeNameIsUsed($modelClassName,
-                                                                                         $clauseInformation,
-                                                                                         $joinTablesAdapter);
+                    $modelAttributeToDataProviderAdapter = new RedBeanModelAttributeToDataProviderAdapter(
+                                                               $modelClassName,
+                                                               $clauseInformation['attributeName'],
+                                                               $clauseInformation['relatedModelData']['attributeName']);
+                    if ($modelAttributeToDataProviderAdapter->getRelationType() == RedBeanModel::MANY_MANY)
+                    {
+                        static::buildJoinForManyToManyRelatedAttributeAndGetWhereClauseData($modelAttributeToDataProviderAdapter,
+                                                                                            $joinTablesAdapter);
+                    }
+                    else
+                    {
+                        //Because a relatedAttributeName is in use, one of the joins gets skipped unless we manually process
+                        //it here.
+                        static::processJoinForRelatedModelDataWhenRelatedAttributeNameIsUsed($modelClassName,
+                                                                                             $clauseInformation,
+                                                                                             $joinTablesAdapter);
+                    }
+
                     //Two adapters are created, because the first adapter gives us the proper modelClassName
                     //to use when using relatedAttributeName
                     $modelAttributeToDataProviderAdapter = new RedBeanModelAttributeToDataProviderAdapter(

File app/protected/extensions/zurmoinc/framework/utils/SQLOperatorUtil.php

     class SQLOperatorUtil
     {
         /**
-         * Confirms usage of operator type is valid.  For example
-         * strings, cannot use greater than or less than.
+         * Confirms usage of operator type is valid.
          * @return boolean;
          */
         public static function isValidOperatorTypeByValue($operatorType, $value)

File app/protected/extensions/zurmoinc/framework/utils/SearchUtil.php

          */
         private static function changeEmptyArrayValuesToNull(& $searchArray)
         {
+            $keysToUnset = array();
             foreach ($searchArray as $key => $value)
             {
                 if (is_array($value) && isset($value['values']) && is_array($value['values']))
                             $searchArray[$key]['values'] = array_values($searchArray[$key]['values']);
                         }
                     }
+                    if(count($searchArray[$key]) == 1 && count($searchArray[$key]['values']) == 0)
+                    {
+                        $keysToUnset[] = $key;
+                    }
                 }
                 if (is_array($value) && isset($value['value']) && is_array($value['value']))
                 {
                             $searchArray[$key]['value'] = array_values($searchArray[$key]['value']);
                         }
                     }
+                    if(count($searchArray[$key]) == 1 && count($searchArray[$key]['value']) == 0)
+                    {
+                        $keysToUnset[] = $key;
+                    }
                 }
                 elseif (is_array($value))
                 {
                     self::changeEmptyArrayValuesToNull($searchArray[$key]);
                 }
             }
+            foreach($keysToUnset as $key)
+            {
+                unset($searchArray[$key]);
+            }
         }
 
         /**

File app/protected/extensions/zurmoinc/framework/views/PortletRefreshView.php

             'content'   => $this->portlet->renderContent(),
             'editable'  => $this->portlet->isEditable(),
             'collapsed' => $this->portlet->collapsed,
+            'removable' => $this->arePortletsRemovable(),
             );
             $cClipWidget = new CClipWidget();
             $cClipWidget->beginClip("JuiPortlet");
             $cClipWidget->endClip();
             return $cClipWidget->getController()->clips['JuiPortlet'];
         }
+        
+        protected function arePortletsRemovable()
+        {
+            return true;
+        }
     }
 ?>

File app/protected/extensions/zurmoinc/framework/views/assets/interactions.js

         }
     });
 
-        $('.headerNav > .parent').live({
+    $('.headerNav').live({
         click: function() {
-            if ($(this).find('ul:visible').length == 0)
+            var attrId = $(this).attr('id');
+            if ($(this).find('ul').css('display') == 'none')
             {
-                if ( $(this).find('ul').length > 0 ){
-                    $(this).find('ul').stop(true, true).delay(0).fadeIn(100);
+                if ($(this).find('ul').length > 0){
+                    $(this).find('ul').show();
+                    $(document).one('mouseup',function (e)
+                    {
+                        var container = $(this).find('ul').find('ul');
+                        if (container.has(e.target).length === 0 && $(e.target).closest('ul.headerNav').attr('id') != attrId)
+                        {
+                            container.hide();
+                        }
+                     });
                 }
             }
             else
             {
-                 $(this).find('ul').stop(true, true).fadeOut(250);
-            }
-        },
-        focusout: function() {
-            if ( $(this).find('ul').length > 0 ){
-                $(this).find('ul').stop(true, true).fadeOut(250);
+                 $(this).find('ul').hide();
             }
         }
     });

File app/protected/modules/comments/models/Comment.php

 
         public static function getModuleClassName()
         {
-            return 'ZurmoModule';
+            return 'CommentsModule';
         }
 
         public static function canSaveMetadata()

File app/protected/modules/designer/tests/unit/MultiSelectDropDownFormTest.php

 
             $this->assertEquals(array('name'        => 'my test account',
                                       'officePhone' => null,
-                                      'testHobbies' => array('values' => array()),
                                       'officeFax'   => null), $modifiedSearchPostData);
 
             $account             = new Account(false);

File app/protected/modules/notes/NotesModule.php

             return true;
         }
 
-        public static function getGamificationRulesType()
-        {
-            return 'NoteGamification';
-        }
-
         /**
          * Even though notes are never globally searched, the search form can still be used by a specific
          * search view for a module.  Either this module or a related module.  This is why a class is returned.

File app/protected/modules/notes/models/Note.php

         {
             return true;
         }
+
+        public static function getGamificationRulesType()
+        {
+            return 'NoteGamification';
+        }
     }
 ?>

File app/protected/modules/zurmo/tests/unit/SearchOwnedCustomFieldRecursiveDataTest.php

+<?php
+    /*********************************************************************************
+     * Zurmo is a customer relationship management program developed by
+     * Zurmo, Inc. Copyright (C) 2012 Zurmo Inc.
+     *
+     * Zurmo is free software; you can redistribute it and/or modify it under
+     * the terms of the GNU General Public License version 3 as published by the
+     * Free Software Foundation with the addition of the following permission added
+     * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
+     * IN WHICH THE COPYRIGHT IS OWNED BY ZURMO, ZURMO DISCLAIMS THE WARRANTY
+     * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
+     *
+     * Zurmo is distributed in the hope that it will be useful, but WITHOUT
+     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+     * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+     * details.
+     *
+     * You should have received a copy of the GNU General Public License along with
+     * this program; if not, see http://www.gnu.org/licenses or write to the Free
+     * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+     * 02110-1301 USA.
+     *
+     * You can contact Zurmo, Inc. with a mailing address at 113 McHenry Road Suite 207,
+     * Buffalo Grove, IL 60089, USA. or at email address contact@zurmo.com.
+     ********************************************************************************/
+
+    /**
+     * Test class to illustrate a bug related to nested search from a many-many relationship when searching a
+     * dropdown field.  This test which now works, shows that this bug is fixed.
+     */
+    class SearchOwnedCustomFieldRecursiveDataTest extends BaseTest
+    {
+        public static function setUpBeforeClass()
+        {
+            parent::setUpBeforeClass();
+            SecurityTestHelper::createSuperAdmin();
+        }
+
+        /**
+         * Searching Many To Many on a custom field (dropdown)
+         */
+        public function testManyManyCustomFieldSearch()
+        {
+            $quote               = DatabaseCompatibilityUtil::getQuote();
+            $searchAttributeData = array();
+            $searchAttributeData['clauses'] = array(
+                1 => array(
+                    'attributeName' => 'opportunities',
+                        'relatedModelData' => array(
+                            'attributeName'     => 'stage',
+                            'relatedAttributeName' => 'value',
+                            'operatorType'      => 'oneOf',
+                            'value'             => array(0 => 'something'),
+                    ),
+                ),
+            );
+            $searchAttributeData['structure'] = '1';
+            //Build the query 'where' and 'joins'. Confirm they are as expected
+            $joinTablesAdapter = new RedBeanModelJoinTablesQueryAdapter('Contact');
+            $where             = ModelDataProviderUtil::makeWhere('Contact', $searchAttributeData, $joinTablesAdapter);
+            $compareWhere      = "({$quote}customfield{$quote}.{$quote}value{$quote} IN('something'))";
+            $this->assertEquals($compareWhere, $where);
+            $this->assertEquals(0, $joinTablesAdapter->getFromTableJoinCount());
+            $this->assertEquals(4, $joinTablesAdapter->getLeftTableJoinCount());
+            $leftTables = $joinTablesAdapter->getLeftTablesAndAliases();
+            $this->assertEquals('contact_opportunity',     $leftTables[0]['tableName']);
+            $this->assertEquals('opportunity',             $leftTables[1]['tableName']);
+            $this->assertEquals('ownedcustomfield',        $leftTables[2]['tableName']);
+            $this->assertEquals('customfield',             $leftTables[3]['tableName']);
+            $this->assertTrue($joinTablesAdapter->getSelectDistinct());
+
+            //Now test that the subsetSQL query produced is correct.
+            $subsetSql = Contact::makeSubsetOrCountSqlQuery('contact', $joinTablesAdapter, 1, 5, $where,
+                                                        null, false, $joinTablesAdapter->getSelectDistinct());
+            $compareSubsetSql  = "select distinct {$quote}contact{$quote}.{$quote}id{$quote} id ";
+            $compareSubsetSql .= "from {$quote}contact{$quote} ";
+            $compareSubsetSql .= "left join {$quote}contact_opportunity{$quote} on ";
+            $compareSubsetSql .= "{$quote}contact_opportunity{$quote}.{$quote}contact_id{$quote} = {$quote}contact{$quote}.{$quote}id{$quote} ";
+            $compareSubsetSql .= "left join {$quote}opportunity{$quote} on ";
+            $compareSubsetSql .= "{$quote}opportunity{$quote}.{$quote}id{$quote} = {$quote}contact_opportunity{$quote}.{$quote}opportunity_id{$quote} ";
+            $compareSubsetSql .= "left join {$quote}ownedcustomfield{$quote} on ";
+            $compareSubsetSql .= "{$quote}ownedcustomfield{$quote}.{$quote}id{$quote} = {$quote}opportunity{$quote}.{$quote}stage_ownedcustomfield_id{$quote} ";
+            $compareSubsetSql .= "left join {$quote}customfield{$quote} on ";
+            $compareSubsetSql .= "{$quote}customfield{$quote}.{$quote}id{$quote} = {$quote}ownedcustomfield{$quote}.{$quote}customfield_id{$quote} ";
+            $compareSubsetSql .= "where " . $compareWhere . ' ';
+            $compareSubsetSql .= 'limit 5 offset 1';
+            $this->assertEquals($compareSubsetSql, $subsetSql);
+            //Make sure the sql runs properly.
+            $data = Contact::getSubset($joinTablesAdapter, 0, 5, $where, null, null, $joinTablesAdapter->getSelectDistinct());
+        }
+    }
+?>

File app/protected/modules/zurmo/views/HeaderLinksView.php

             $content  .= "<span id='notifications-link' class='tooltip'>" . $count ."</span></a>";
             $content  .= CHtml::tag('div',
                                     array('id' => 'notifications-flyout', 'style' => 'display:none;'),
-                                    CHtml::image($imageSourceUrl, Yii::t('Default', 'Loading')), 'div');            
-            Yii::app()->clientScript->registerScript('notificationPopupLinkScript', "                               
-                $('#notifications-link').unbind('focusout');
-                $('#notifications-link').bind('focusout', function(e)
-                {  
-                    $('#notifications-flyout').stop(true, true).fadeOut(250);  
-                });
-                $('#notifications-link').unbind('click');
-                $('#notifications-link').bind('click', function(e)
-                {                                             
+                                    CHtml::image($imageSourceUrl, Yii::t('Default', 'Loading')), 'div');
+            Yii::app()->clientScript->registerScript('notificationPopupLinkScript', "
+                $('#notifications-link').live('click', function()
+                {
                     if ($('#notifications-flyout').css('display') == 'none')
-                    {                       
-                        $('#notifications-flyout').show();                       
+                    {
+                        $('#notifications-flyout').show();
                         $.ajax({
                             url 	 : '" . $this->notificationsUrl . "',
                             type     : 'GET',
                             dataType : 'html',
                             success  : function(html)
-                            {                                
+                            {
                                 jQuery('#notifications-flyout').html(html);
-                                $('#notifications-link').attr('tabindex',-1).focus();
-
+                                $(document).one('mouseup',function (e)
+                                {
+                                    var container = $('#notifications-flyout');
+                                    if (container.has(e.target).length === 0 && e.target.id != 'notifications-link')
+                                    {
+                                        container.hide();
+                                    }
+                                });
                             }
                         });
-                    }      
+                    }
                     else
                     {
-                        $('#notifications-flyout').hide(); 
-                    }                                            
-                });                
-            ", CClientScript::POS_END);
+                        $('#notifications-flyout').hide();
+                    }
+                });
+            ", CClientScript::POS_HEAD);
             Yii::app()->clientScript->registerScript('deleteNotificationFromAjaxListViewScript', "
                 function deleteNotificationFromAjaxListView(element, modelId)
                 {