0012928: Fulltext search for all description fields and add it to query filter
authorMichael Spahn <m.spahn@metaways.de>
Wed, 5 Apr 2017 09:47:14 +0000 (11:47 +0200)
committerPaul Mehrer <p.mehrer@metaways.de>
Wed, 12 Apr 2017 11:03:15 +0000 (13:03 +0200)
https://forge.tine20.org/view.php?id=12928

Change-Id: I0816ef77eebc5da2d5aade4225804e819f10b0ad
Reviewed-on: http://gerrit.tine20.com/customers/4500
Reviewed-by: Paul Mehrer <p.mehrer@metaways.de>
Tested-by: Paul Mehrer <p.mehrer@metaways.de>
49 files changed:
tests/tine20/Addressbook/CliTest.php
tests/tine20/Sales/JsonTest.php
tests/tine20/Timetracker/AbstractTest.php
tests/tine20/Timetracker/ControllerTest.php
tests/tine20/Timetracker/ExportTest.php
tests/tine20/Timetracker/FilterTest.php
tests/tine20/Timetracker/JsonTest.php
tine20/Addressbook/Model/ContactFilter.php
tine20/Addressbook/Setup/Update/Release10.php
tine20/Addressbook/Setup/setup.xml
tine20/Calendar/Model/EventFilter.php
tine20/Calendar/Setup/Update/Release10.php
tine20/Calendar/Setup/setup.xml
tine20/Crm/Model/LeadFilter.php
tine20/Crm/Setup/Update/Release10.php [new file with mode: 0644]
tine20/Crm/Setup/setup.xml
tine20/Events/Model/Event.php
tine20/Events/Setup/Update/Release1.php
tine20/Events/Setup/Update/Release10.php [new file with mode: 0644]
tine20/Events/Setup/setup.xml
tine20/Projects/Model/ProjectFilter.php
tine20/Projects/Setup/Update/Release10.php [new file with mode: 0644]
tine20/Projects/Setup/setup.xml
tine20/Sales/Model/Contract.php
tine20/Sales/Model/Customer.php
tine20/Sales/Model/Invoice.php
tine20/Sales/Model/Offer.php
tine20/Sales/Model/OrderConfirmation.php
tine20/Sales/Model/Product.php
tine20/Sales/Model/PurchaseInvoice.php
tine20/Sales/Model/Supplier.php
tine20/Sales/Setup/Update/Release10.php
tine20/Sales/Setup/setup.xml
tine20/Tasks/Model/TaskFilter.php
tine20/Tasks/Setup/Update/Release10.php [new file with mode: 0644]
tine20/Tasks/Setup/setup.xml
tine20/Timetracker/Controller/Timesheet.php
tine20/Timetracker/Model/Timeaccount.php
tine20/Timetracker/Model/Timesheet.php
tine20/Timetracker/Setup/Update/Release10.php [new file with mode: 0644]
tine20/Timetracker/Setup/setup.xml
tine20/Tinebase/Cache/PerRequest.php
tine20/Tinebase/CustomField.php
tine20/Tinebase/Model/Filter/FilterGroup.php
tine20/Tinebase/Model/Filter/FullText.php
tine20/Tinebase/Model/Filter/Int.php
tine20/Tinebase/Model/Tree/Node/Filter.php
tine20/Tinebase/Setup/Update/Release10.php
tine20/Tinebase/Setup/setup.xml

index c42104a..16a34ea 100644 (file)
@@ -120,6 +120,9 @@ class Addressbook_CliTest extends TestCase
             'email' => $attenderEmail
         );
         Calendar_Model_Attender::resolveEmailToContact($attenderData);
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         
         $opts = new Zend_Console_Getopt('abp:');
         $opts->setArguments(array());
index 5da5479..d0e5fb7 100644 (file)
@@ -17,7 +17,8 @@ class Sales_JsonTest extends TestCase
      * @var Sales_Frontend_Json
      */
     protected $_instance = array();
-    
+
+    protected $_deleteContracts = array();
     /**
      * Runs the test methods of this class.
      *
@@ -44,6 +45,8 @@ class Sales_JsonTest extends TestCase
         
         Sales_Controller_Contract::getInstance()->setNumberPrefix();
         Sales_Controller_Contract::getInstance()->setNumberZerofill();
+
+        $this->_deleteContracts = array();
     }
 
     protected function tearDown()
@@ -51,6 +54,10 @@ class Sales_JsonTest extends TestCase
         Tinebase_Core::getPreference()->setValue(Tinebase_Preference::ADVANCED_SEARCH, false);
 
         parent::tearDown();
+
+        if (count($this->_deleteContracts) > 0) {
+            $this->_instance->deleteContracts($this->_deleteContracts);
+        }
     }
 
     /**
@@ -257,6 +264,10 @@ class Sales_JsonTest extends TestCase
         $contract = $this->_getContract();
         $contractData = $this->_instance->saveContract($contract->toArray());
 
+        $this->_deleteContracts = array($contractData['id']);
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+
         // search & check
         $search = $this->_instance->searchContracts($this->_getFilter(), $this->_getPaging());
         $this->assertEquals($contract->title, $search['results'][0]['title']);
@@ -308,6 +319,10 @@ class Sales_JsonTest extends TestCase
         $contract = $this->_getContract();
         $contractData = $this->_instance->saveContract($contract->toArray());
 
+        $this->_deleteContracts = array($contractData['id']);
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+
         $filter = $this->_getFilter();
         $filter[] = array('field' => 'end_date', 'operator' => 'equals', 'value' => '');
 
@@ -787,6 +802,11 @@ class Sales_JsonTest extends TestCase
 
         // test notcontains
         $contract2 = Sales_Controller_Contract::getInstance()->create($this->_getContract('test2'));
+
+        $this->_deleteContracts = array($contract->getId(), $contract2->getId());
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+
         $result = $this->_instance->searchContracts($this->_getFilter(), array());
         $this->assertEquals(2, $result['totalcount'], 'should find 2 contracts');
 
@@ -795,8 +815,8 @@ class Sales_JsonTest extends TestCase
             array('field' => 'query', 'operator' => 'notcontains', 'value' => 'wolf'),
         ), $this->_getPaging());
 
-        $this->assertEquals($contract2->title, $search['results'][0]['title']);
         $this->assertEquals(1, $search['totalcount']);
+        $this->assertEquals($contract2->title, $search['results'][0]['title']);
 
         // search with notcontains
         $search = $this->_instance->searchContracts(array(
index db8f181..d8f2fa8 100644 (file)
@@ -24,6 +24,10 @@ abstract class Timetracker_AbstractTest extends TestCase
      */
     protected $_lastCreatedRecord = null;
 
+    protected $_deleteTimeAccounts = array();
+    protected $_deleteTimeSheets = array();
+    protected $_deletePersistentFilters = array();
+
     /**
      * Runs the test methods of this class.
      *
@@ -45,7 +49,10 @@ abstract class Timetracker_AbstractTest extends TestCase
     protected function setUp()
     {
         Tinebase_Acl_Roles::getInstance()->resetClassCache();
-        Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+        $this->_deleteTimeAccounts = array();
+        $this->_deleteTimeSheets = array();
+        $this->_deletePersistentFilters = array();
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         $this->_json = new Timetracker_Frontend_Json();
         
         Sales_Controller_Contract::getInstance()->setNumberPrefix();
@@ -62,6 +69,16 @@ abstract class Timetracker_AbstractTest extends TestCase
     {
         Tinebase_TransactionManager::getInstance()->rollBack();
         Tinebase_Acl_Roles::getInstance()->resetClassCache();
+
+        if (count($this->_deleteTimeAccounts) > 0) {
+            Timetracker_Controller_Timeaccount::getInstance()->delete($this->_deleteTimeAccounts);
+        }
+        if (count($this->_deleteTimeSheets) > 0) {
+            Timetracker_Controller_Timesheet::getInstance()->delete($this->_deleteTimeSheets);
+        }
+        if (count($this->_deletePersistentFilters) > 0) {
+            Tinebase_PersistentFilter::getInstance()->delete($this->_deleteTimeSheets);
+        }
     }
 
     /************ protected helper funcs *************/
@@ -369,6 +386,11 @@ abstract class Timetracker_AbstractTest extends TestCase
         $timesheetArray[$customField2->name] = Tinebase_Record_Abstract::generateUID();
 
         $timesheetData = $this->_json->saveTimesheet($timesheetArray);
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         
         // checks
         $this->assertTrue((isset($timesheetData['customfields'][$_customField1->name]) || array_key_exists($_customField1->name, $timesheetData['customfields'])), 'cf 1 not found');
index 5618313..bae6817 100644 (file)
@@ -40,6 +40,9 @@ class Timetracker_ControllerTest extends TestCase
      * @var array
      */
     protected $_objects = array();
+
+    protected $_deleteTimeAccounts = array();
+    protected $_deleteTimeSheets = array();
     
     /**
      * Sets up the fixture.
@@ -50,6 +53,8 @@ class Timetracker_ControllerTest extends TestCase
     protected function setUp()
     {
         parent::setUp();
+
+        Timetracker_Controller_Timesheet::unsetInstance();
         
         $this->_timeaccountController = Timetracker_Controller_Timeaccount::getInstance();
         $this->_timesheetController = Timetracker_Controller_Timesheet::getInstance();
@@ -61,6 +66,11 @@ class Timetracker_ControllerTest extends TestCase
         Tinebase_Acl_Roles::getInstance()->resetClassCache();
         
         $this->_roleRights = self::removeManageAllRight();
+
+        Tinebase_Acl_Roles::getInstance()->resetClassCache();
+
+        $this->_deleteTimeAccounts = array();
+        $this->_deleteTimeSheets = array();
     }
     
     /**
@@ -97,6 +107,38 @@ class Timetracker_ControllerTest extends TestCase
         parent::tearDown();
         
         Tinebase_Acl_Roles::getInstance()->resetClassCache();
+
+        if (count($this->_deleteTimeSheets) > 0 || count($this->_deleteTimeAccounts) > 0) {
+            $role = Tinebase_Acl_Roles::getInstance()->getRoleByName('admin role');
+            Tinebase_Acl_Roles::getInstance()->setRoleRights($role->getId(), $this->_roleRights);
+            Tinebase_Acl_Roles::getInstance()->resetClassCache();
+
+       /*     try {
+                $grants = new Tinebase_Record_RecordSet('Timetracker_Model_TimeaccountGrants', array(array(
+                    'account_id' => Tinebase_Core::getUser()->getId(),
+                    'account_type' => 'user',
+                    Tinebase_Model_Grants::GRANT_ADMIN => TRUE,
+                )));
+                Timetracker_Model_TimeaccountGrants::setTimeaccountGrants(
+                    $this->_objects['timeaccount'],
+                    $grants,
+                    TRUE
+                );
+            } catch (Exception $e) {
+            }*/
+        }
+
+        if (count($this->_deleteTimeSheets) > 0) {
+            try {
+                Timetracker_Controller_Timesheet::getInstance()->delete($this->_deleteTimeSheets);
+            } catch (Exception $e) {}
+        }
+
+        if (count($this->_deleteTimeAccounts) > 0) {
+            try {
+                Timetracker_Controller_Timeaccount::getInstance()->delete($this->_deleteTimeAccounts);
+            } catch (Exception $e) {}
+        }
     }
     
     /************ test functions follow **************/
@@ -369,22 +411,51 @@ class Timetracker_ControllerTest extends TestCase
                 $this->_timesheetController->create($ts);
                 break;
             case 'search_bookable':
+                $this->_deleteTimeSheets[] = $this->_objects['timesheet']->getId();
+                $this->_deleteTimeAccounts[] = $this->_objects['timeaccount']->getId();
+
+                Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+                $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+
                 $filter = $this->_getTimeaccountFilter(TRUE);
                 $result = $this->_timeaccountController->search($filter);
                 $this->assertEquals($_expect, count($result));
                 break;
             case 'searchTA':
+
+                $this->_deleteTimeSheets[] = $this->_objects['timesheet']->getId();
+                $this->_deleteTimeAccounts[] = $this->_objects['timeaccount']->getId();
+
+                Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+                $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+
                 $filter = $this->_getTimeaccountFilter();
                 $result = $this->_timeaccountController->search($filter);
                 $this->assertEquals($_expect, count($result));
                 break;
             case 'searchTS':
-                $filter = $this->_getTimesheetFilter();
                 $ts = $this->_timesheetController->create($ts);
+
+                $this->_deleteTimeSheets[] = $this->_objects['timesheet']->getId();
+                $this->_deleteTimeSheets[] = $ts->getId();
+                $this->_deleteTimeAccounts[] = $this->_objects['timeaccount']->getId();
+
+                Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+                $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+
+                $filter = $this->_getTimesheetFilter();
+
                 $result = $this->_timesheetController->search($filter);
                 $this->assertEquals($_expect, count($result));
                 break;
             case 'searchTSExport':
+
+                $this->_deleteTimeSheets[] = $this->_objects['timesheet']->getId();
+                $this->_deleteTimeAccounts[] = $this->_objects['timeaccount']->getId();
+
+                Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+                $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+
                 $filter = $this->_getTimesheetFilter();
                 $result = $this->_timesheetController->search($filter, NULL, FALSE, FALSE, 'export');
                 $this->assertEquals($_expect, count($result));
@@ -447,6 +518,7 @@ class Timetracker_ControllerTest extends TestCase
     /**
      * get Timeaccount filter
      *
+     * @param bool $bookable
      * @return Timetracker_Model_TimeaccountFilter
      */
     protected function _getTimeaccountFilter($bookable = FALSE)
@@ -474,7 +546,7 @@ class Timetracker_ControllerTest extends TestCase
     /**
      * get Timesheet filter
      *
-     * @return array
+     * @return Timetracker_Model_TimesheetFilter
      */
     protected function _getTimesheetFilter()
     {
index 3b688a9..323e198 100644 (file)
@@ -26,6 +26,12 @@ class Timetracker_ExportTest extends Timetracker_AbstractTest
         // create
         $timesheet = $this->_getTimesheet();
         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         
         // export & check
         $csvExportClass = new Timetracker_Export_Csv(new Timetracker_Model_TimesheetFilter($this->_getTimesheetFilter()));
@@ -78,6 +84,11 @@ class Timetracker_ExportTest extends Timetracker_AbstractTest
         // create
         $timeaccount = $this->_getTimeaccount();
         $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
+
+        $this->_deleteTimeAccounts[] = $timeaccountData['id'];
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         
         // export & check
         $odsExportClass = Tinebase_Export::factory(new Timetracker_Model_TimeaccountFilter($this->_getTimeaccountFilter()), 'ods');
@@ -105,6 +116,12 @@ class Timetracker_ExportTest extends Timetracker_AbstractTest
         // create
         $timesheet = $this->_getTimesheet();
         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         
         // export & check
         $options = ($_definitionId === NULL) ? array('format' => 'ods') : array('definitionId' => $_definitionId);
index 46cb442..3aa28ac 100644 (file)
@@ -76,6 +76,11 @@ class Timetracker_FilterTest extends Timetracker_AbstractTest
         ));
         $ta1->relations = array($this->_getRelation($contract, $ta1));
         $this->_timeaccountController->update($ta1);
+
+        $this->_deleteTimeAccounts[] = $ta1->getId();
+        $this->_deleteTimeAccounts[] = $ta2->getId();
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         
         // search by contract
         $f = new Timetracker_Model_TimeaccountFilter(array(array('field' => 'contract', 'operator' => 'AND', 'value' =>
index e9c3371..65471fb 100644 (file)
@@ -19,7 +19,7 @@ require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php'
 class Timetracker_JsonTest extends Timetracker_AbstractTest
 {
     protected $_testUser = NULL;
-    
+
     /**
      * Sets up the fixture.
      * This method is called before a test is executed.
@@ -122,6 +122,10 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         $timeaccount = $this->_getTimeaccount();
         $timeaccount->is_open = 0;
         $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
+        $this->_deleteTimeAccounts = array($timeaccountData['id']);
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
 
         // search & check
         $timeaccountFilter = $this->_getTimeaccountFilter();
@@ -277,6 +281,11 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
 
         $timesheet = $this->_getTimesheet();
         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
 
         $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(array(
             'field'     => 'customfield',
@@ -302,20 +311,20 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         $ts = $this->_json->saveTimesheet($timesheetArray);
 
         // test with default grants
-        $this->assertTrue((isset($ts['customfields'][$cf->name]) || array_key_exists($cf->name, $ts['customfields'])), 'customfield should be readable');
+        $this->assertTrue((isset($ts['customfields'][$cf->name])), 'customfield should be readable');
         $this->assertEquals($value, $ts['customfields'][$cf->name]);
 
         // remove all grants
         Tinebase_CustomField::getInstance()->setGrants($cf, array());
         $ts = $this->_json->getTimesheet($ts['id']);
 
-        $this->assertTrue(! (isset($ts['customfields']) || array_key_exists('customfields', $ts)), 'customfields should not be readable');
+        $this->assertTrue(!isset($ts['customfields']), 'customfields should not be readable');
         $ts = $this->_updateCfOfTs($ts, $cf->name, 'try to update');
 
         // only read allowed
         Tinebase_CustomField::getInstance()->setGrants($cf, array(Tinebase_Model_CustomField_Grant::GRANT_READ));
         $ts = $this->_json->getTimesheet($ts['id']);
-        $this->assertTrue((isset($ts['customfields'][$cf->name]) || array_key_exists($cf->name, $ts['customfields'])), 'customfield should be readable again');
+        $this->assertTrue(isset($ts['customfields'][$cf->name]), 'customfield should be readable again');
         $this->assertEquals($value, $ts['customfields'][$cf->name], 'value should not have changed');
         $ts = $this->_updateCfOfTs($ts, $cf->name, 'try to update');
         $this->assertEquals($value, $ts['customfields'][$cf->name], 'value should still not have changed');
@@ -474,6 +483,11 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         $timesheet = $this->_getTimesheet();
 
         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
 
         // search & check
         $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(), $this->_getPaging());
@@ -552,6 +566,12 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         //$timesheet = $this->_getTimesheet(NULL, $_startDate);
         $timesheet = $this->_getTimesheet(array('timeaccount_id' => null, 'start_date' => $_startDate));
         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+
 
         $result = $this->_json->searchTimesheets($this->_getTimesheetDateFilter($_filterType), $this->_getPaging());
 
@@ -569,11 +589,16 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         // create
         $timesheet = $this->_getTimesheet();
         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
 
         // update timeaccount -> is_billable = false
         $ta = Timetracker_Controller_Timeaccount::getInstance()->get($timesheetData['timeaccount_id']['id']);
         $ta->is_billable = 0;
-        Timetracker_Controller_Timeaccount::getInstance()->update($ta);
+        Timetracker_Controller_Timeaccount::getInstance()->update($ta);;
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
 
         // search & check
         $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(array(
@@ -613,9 +638,16 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
     {
         $timesheet = $this->_getTimesheet();
         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
         $timesheet = $this->_getTimesheet();
         $timesheet->is_billable = false;
         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         
         // search & check
         $search = $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(), $this->_getPaging());
@@ -708,7 +740,7 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
 
         // create
         $filterName = Tinebase_Record_Abstract::generateUID();
-        $persistentFiltersJson->savePersistentFilter(array(
+        $persistentFilterData = $persistentFiltersJson->savePersistentFilter(array(
             'application_id'    => Tinebase_Application::getInstance()->getApplicationById('Timetracker')->getId(),
             'filters'           => $tsFilter,
             'name'              => $filterName,
@@ -716,6 +748,12 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         ));
         $timesheet = $this->_getTimesheet();
         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+        $this->_deleteTimeSheets[] = $timesheetData['id'];
+        $this->_deleteTimeAccounts[] = $timesheetData['timeaccount_id']['id'];
+        $this->_deletePersistentFilters[] = $persistentFilterData['id'];
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
 
         // search persistent filter
         $persistentFilters = $persistentFiltersJson->searchPersistentFilter($this->_getPersistentFilterFilter($filterName), NULL);
@@ -857,11 +895,18 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
     {
         // create 100+ timesheets
         $first = $this->_getTimesheet(array(), TRUE);
+        $this->_deleteTimeSheets[] = $first->getId();
+        $this->_deleteTimeAccounts[] = $first->timeaccount_id;
         for ($i = 0; $i < 122; $i++) {
-            $this->_getTimesheet(array(
+            $next = $this->_getTimesheet(array(
                 'timeaccount_id' => $first->timeaccount_id
             ), TRUE);
+
+            $this->_deleteTimeSheets[] = $next->getId();
         }
+
+        Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
+        $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         
         // update multi with filter
         $filterArray = $this->_getTimesheetDateFilter();
index 79513d6..d69fabc 100644 (file)
@@ -42,7 +42,7 @@ class Addressbook_Model_ContactFilter extends Tinebase_Model_Filter_FilterGroup
         'id'                   => array('filter' => 'Addressbook_Model_ContactIdFilter', 'options' => array('modelName' => 'Addressbook_Model_Contact')),
         'query'                => array(
             'filter' => 'Tinebase_Model_Filter_Query', 
-            'options' => array('fields' => array('n_family', 'n_given', 'org_name', 'org_unit', 'email', 'email_home', 'adr_one_locality'))
+            'options' => array('fields' => array('n_family', 'n_given', 'org_name', 'org_unit', 'email', 'email_home', 'adr_one_locality', 'note'))
         ),
         'path'                => array(
             'filter' => 'Tinebase_Model_Filter_Path',
@@ -136,7 +136,7 @@ class Addressbook_Model_ContactFilter extends Tinebase_Model_Filter_FilterGroup
         'tel_pager_normalized'          => array('filter' => 'Tinebase_Model_Filter_Text'),
         'tel_prefer_normalized'         => array('filter' => 'Tinebase_Model_Filter_Text'),
         'tel_work_normalized'           => array('filter' => 'Tinebase_Model_Filter_Text'),
-        'note'                 => array('filter' => 'Tinebase_Model_Filter_Text'),
+        'note'                 => array('filter' => 'Tinebase_Model_Filter_FullText'),
         'role'                 => array('filter' => 'Tinebase_Model_Filter_Text'),
         'pubkey'               => array('filter' => 'Tinebase_Model_Filter_Text'),
         'assistent'            => array('filter' => 'Tinebase_Model_Filter_Text'),
index a93a4e0..1928bd0 100644 (file)
@@ -57,4 +57,28 @@ class Addressbook_Setup_Update_Release10 extends Setup_Update_Abstract
         $this->setTableVersion('addressbook', 23);
         $this->setApplicationVersion('Addressbook', '10.3');
     }
+
+
+    /**
+     * update to 10.4
+     *
+     * Add fulltext index for note field
+     */
+    public function update_3()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>note</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>note</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('addressbook', $declaration);
+
+        $this->setTableVersion('addressbook', 24);
+        $this->setApplicationVersion('Addressbook', '10.4');
+    }
 }
index 2efe194..cb68a61 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <application>
     <name>Addressbook</name>
-    <version>10.3</version>
+    <version>10.4</version>
     <order>10</order>
     <depends>
         <application>Admin</application>
@@ -11,7 +11,7 @@
             <name>addressbook</name>
             <engine>InnoDB</engine>
             <charset>utf8</charset>
-            <version>23</version>
+            <version>24</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>type</name>
                     </field>
                 </index>
+                <index>
+                    <name>note</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>note</name>
+                    </field>
+                </index>
             </declaration>
         </table>
         <table>
index fce4fce..e4cbaee 100644 (file)
@@ -66,7 +66,7 @@ class Calendar_Model_EventFilter extends Tinebase_Model_Filter_FilterGroup
         'rrule_constraints'     => array('filter' => 'Tinebase_Model_Filter_Text'),
         'summary'               => array('filter' => 'Tinebase_Model_Filter_Text'),
         'location'              => array('filter' => 'Tinebase_Model_Filter_Text'),
-        'description'           => array('filter' => 'Tinebase_Model_Filter_Text'),
+        'description'           => array('filter' => 'Tinebase_Model_Filter_FullText'),
         'is_deleted'            => array('filter' => 'Tinebase_Model_Filter_Bool'),
         'deleted_by'            => array('filter' => 'Tinebase_Model_Filter_User'),
         'deleted_time'          => array('filter' => 'Tinebase_Model_Filter_DateTime'),
index 824635e..17c509d 100644 (file)
@@ -117,4 +117,27 @@ class Calendar_Setup_Update_Release10 extends Setup_Update_Abstract
 
         $this->setApplicationVersion('Calendar', '10.4');
     }
+
+    /**
+     * update to 10.5
+     *
+     * Add fulltext index for description field
+     */
+    public function update_4()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('cal_events', $declaration);
+
+        $this->setTableVersion('cal_events', 13);
+        $this->setApplicationVersion('Calendar', '10.5');
+    }
 }
index 826be40..f3225f0 100644 (file)
@@ -2,14 +2,14 @@
 <application>
     <name>Calendar</name>
     <!-- gettext('Calendar') -->   
-    <version>10.4</version>
+    <version>10.5</version>
     <order>15</order>
     <status>enabled</status>
     <tables>
         <!-- events -->
         <table>
             <name>cal_events</name>
-            <version>12</version>
+            <version>13</version>
             <declaration>
                 <field>
                     <name>id</name>
                     <length>255</length>
                 </field>
                 <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
+                <index>
                     <name>id</name>
                     <primary>true</primary>
                     <field>
index 3a08074..31caa30 100644 (file)
@@ -38,7 +38,7 @@ class Crm_Model_LeadFilter extends Tinebase_Model_Filter_FilterGroup
     protected $_filterModel = array(
         'id'                    => array('filter' => 'Tinebase_Model_Filter_Id'),
         'query'                 => array('filter' => 'Crm_Model_LeadQueryFilter'),
-        'description'           => array('filter' => 'Tinebase_Model_Filter_Text'),
+        'description'           => array('filter' => 'Tinebase_Model_Filter_FullText'),
         'lead_name'             => array('filter' => 'Tinebase_Model_Filter_Text'),
         'tag'                   => array('filter' => 'Tinebase_Model_Filter_Tag', 'options' => array(
             'idProperty' => 'metacrm_lead.id',
diff --git a/tine20/Crm/Setup/Update/Release10.php b/tine20/Crm/Setup/Update/Release10.php
new file mode 100644 (file)
index 0000000..1ebae58
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Crm
+ * @subpackage  Setup
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL3
+ * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Michael Spahn <m.spahn@metaways.de>
+ */
+class Crm_Setup_Update_Release10 extends Setup_Update_Abstract
+{
+    /**
+     * update to 10.1
+     *
+     * Add fulltext index for description field
+     */
+    public function update_0()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('metacrm_lead', $declaration);
+
+        $this->setTableVersion('metacrm_lead', 9);
+        $this->setApplicationVersion('Crm', '10.1');
+    }
+}
index d92f373..138d779 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <application>
     <name>Crm</name>
-    <version>10.0</version>
+    <version>10.1</version>
     <order>20</order>
     <depends>
         <application>Admin</application>
@@ -12,7 +12,7 @@
     <tables>
         <table>
             <name>metacrm_lead</name>
-            <version>8</version>
+            <version>9</version>
             <declaration>
                 <field>
                     <name>id</name>
                     </field>
                 </index>
                 <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
+                <index>
                     <name>metacrm_lead::container_id--container::id</name>
                     <field>
                         <name>container_id</name>
index 473b877..db573ec 100644 (file)
@@ -1,18 +1,18 @@
 <?php
 /**
  * class to hold Event data
- * 
+ *
  * @package     Events
  * @subpackage  Model
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Stefanie Stamer <s.stamer@metaways.de>
  * @copyright   Copyright (c) 2007-2015 Metaways Infosystems GmbH (http://www.metaways.de)
- * 
+ *
  */
 
 /**
  * class to hold Event data
- * 
+ *
  * @package     Events
  * @subpackage  Model
  */
@@ -63,7 +63,7 @@ class Events_Model_Event extends Tinebase_Record_Abstract
 
         'appName'         => 'Events',
         'modelName'       => 'Event',
-        
+
         'fields'          => array(
             'title' => array(
                 'validators'  => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence' => 'required'),
@@ -152,7 +152,9 @@ class Events_Model_Event extends Tinebase_Record_Abstract
                     'type'       => 'date'
             ),
             'description' => array(
-                    'validators' => array(Zend_Filter_Input::ALLOW_EMPTY => TRUE)
+                'validators' => array(Zend_Filter_Input::ALLOW_EMPTY => TRUE),
+                'type' => 'fulltext',
+                'queryFilter' => true
             )
         )
      );
index 12cbd86..70a0c9a 100644 (file)
@@ -12,7 +12,7 @@
 class Events_Setup_Update_Release1 extends Setup_Update_Abstract
 {
     /**
-     * update to 3.0
+     * update to 10.0
      * @return void
      */
     public function update_0()
diff --git a/tine20/Events/Setup/Update/Release10.php b/tine20/Events/Setup/Update/Release10.php
new file mode 100644 (file)
index 0000000..6052863
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Events
+ * @subpackage  Setup
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL3
+ * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Paul Mehrer <p.mehrer@metaways.de>
+ */
+
+class Events_Setup_Update_Release10 extends Setup_Update_Abstract
+{
+    /**
+     * Update to 10.1
+     *
+     * Add fulltext index for description field of events_event
+     */
+    public function update_0()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('events_event', $declaration);
+
+        $this->setTableVersion('events_event', '2');
+        $this->setApplicationVersion('Events', '10.1');
+    }
+}
index 95881c2..8d3407e 100644 (file)
@@ -2,13 +2,13 @@
 <application>
     <name>Events</name>
     <!-- gettext('Events') -->   
-    <version>10.0</version>
+    <version>10.1</version>
     <order>60</order>
     <status>enabled</status>
     <tables>
         <table>
             <name>events_event</name>
-            <version>1</version>
+            <version>2</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>container_id</name>
                     </field>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
     </tables>
index 9e1331e..3f9990a 100644 (file)
@@ -38,7 +38,7 @@ class Projects_Model_ProjectFilter extends Tinebase_Model_Filter_FilterGroup
      * @var array filter model fieldName => definition
      */
     protected $_filterModel = array(
-        'query'                => array('filter' => 'Tinebase_Model_Filter_Query', 'options' => array('fields' => array('title', 'number'))),
+        'query'                => array('filter' => 'Tinebase_Model_Filter_Query', 'options' => array('fields' => array('title', 'number', 'description'))),
         'container_id'         => array('filter' => 'Tinebase_Model_Filter_Container', 'options' => array('applicationName' => 'Projects')),
         'id'                   => array('filter' => 'Tinebase_Model_Filter_Id', 'options' => array('modelName' => 'Projects_Model_Project')),
         'tag'                  => array('filter' => 'Tinebase_Model_Filter_Tag', 'options' => array(
@@ -48,7 +48,7 @@ class Projects_Model_ProjectFilter extends Tinebase_Model_Filter_FilterGroup
 
         'title'          => array('filter' => 'Tinebase_Model_Filter_Text'),
         'number'         => array('filter' => 'Tinebase_Model_Filter_Text'),
-        'description'    => array('filter' => 'Tinebase_Model_Filter_Text'),
+        'description'    => array('filter' => 'Tinebase_Model_Filter_FullText'),
         'status'         => array('filter' => 'Tinebase_Model_Filter_Text'),
     
         'contact'        => array('filter' => 'Tinebase_Model_Filter_Relation', 'options' => array(
diff --git a/tine20/Projects/Setup/Update/Release10.php b/tine20/Projects/Setup/Update/Release10.php
new file mode 100644 (file)
index 0000000..0fe06c9
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Projects
+ * @subpackage  Setup
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL3
+ * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Michael Spahn <m.spahn@metaways.de>
+ */
+class Projects_Setup_Update_Release10 extends Setup_Update_Abstract
+{
+    /**
+     * update to 10.1
+     *
+     * Add fulltext index to description field of projects_project
+     */
+    public function update_0()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('projects_project', $declaration);
+
+        $this->setTableVersion('projects_project', '3');
+        $this->setApplicationVersion('Projects', '10.1');
+    }
+}
index 51aa483..f9b5fa0 100644 (file)
@@ -2,14 +2,14 @@
 <application>
     <name>Projects</name>
     <!-- gettext('Projects') -->   
-    <version>10.0</version>
+    <version>10.1</version>
     <order>60</order>
     <status>enabled</status>
     <tables>
         <!-- project -->
         <table>
             <name>projects_project</name>
-            <version>2</version>
+            <version>3</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>container_id</name>
                     </field>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
     </tables>
index 16d357d..72143f7 100644 (file)
@@ -130,7 +130,7 @@ class Sales_Model_Contract extends Tinebase_Record_Abstract
             ),
             'description' => array(
                 'label'   => 'Description', // _('Description')
-                'type'    => 'text',
+                'type'    => 'fulltext',
                 'queryFilter' => TRUE,
             ),
             'parent_id'       => array(
@@ -222,7 +222,7 @@ class Sales_Model_Contract extends Tinebase_Record_Abstract
             ),
             
             'fulltext' => array(
-                'type' => 'string',
+                'type' => 'string'
             ),
             
             'merge_invoices' => array(
index 7ab4bb5..55f8c9c 100644 (file)
@@ -78,7 +78,7 @@ class Sales_Model_Customer extends Tinebase_Record_Abstract
             'description' => array(
                 'label'       => 'Description', // _('Description')
                 'group'       => 'core',
-                'type'        => 'text',
+                'type'        => 'fulltext',
                 'queryFilter' => TRUE,
                 'shy'         => TRUE
             ),
index a1ad97a..95af8e6 100644 (file)
@@ -82,6 +82,7 @@ class Sales_Model_Invoice extends Tinebase_Record_Abstract
             ),
             'description' => array(
                 'label'   => 'Description', // _('Description')
+                'type'    => 'fulltext',
                 'queryFilter' => TRUE,
             ),
             'address_id'       => array(
index 33c37c7..56bdd8d 100644 (file)
@@ -85,7 +85,7 @@ class Sales_Model_Offer extends Tinebase_Record_Abstract
                 'label'      => 'Description',    // _('Description')
                 'validators' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
                 'queryFilter' => TRUE,
-                'type' => 'text',
+                'type' => 'fulltext',
             ),
             'customer' => array(
                 'type' => 'virtual',
index 1a5cfa4..3e09e24 100644 (file)
@@ -72,7 +72,7 @@ class Sales_Model_OrderConfirmation extends Tinebase_Record_Abstract
                 'label'      => 'Description',    // _('Description')
                 'validators' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
                 'queryFilter' => TRUE,
-                'type' => 'text',
+                'type' => 'fulltext',
             ),
             'contract' => array(
                 'type' => 'virtual',
index 037e7fa..8bfe99e 100644 (file)
@@ -58,7 +58,7 @@ class Sales_Model_Product extends Tinebase_Record_Abstract
                 'label'       => 'Description',  // _('Description')
                 'validators'  => array(Zend_Filter_Input::ALLOW_EMPTY => true),
                 'queryFilter' => TRUE,
-                'type'        => 'text',
+                'type'        => 'fulltext',
             ),
             'purchaseprice' => array(
                 'label'        => 'Purchaseprice', // _('Purchaseprice')
index 8e18bfb..5b0f648 100644 (file)
@@ -93,6 +93,7 @@ class Sales_Model_PurchaseInvoice extends Tinebase_Record_Abstract
             'description' => array(
                 'label'   => 'Description',     // _('Description')
                 'queryFilter' => TRUE,
+                'type' => 'fulltext'
             ),
             'date' => array(
                 'type'  => 'date',
index b59fb18..5ceb5ff 100644 (file)
@@ -76,7 +76,7 @@ class Sales_Model_Supplier extends Tinebase_Record_Abstract
             'description' => array(
                 'label'       => 'Description', // _('Description')
                 'group'       => 'core',
-                'type'        => 'text',
+                'type'        => 'fulltext',
                 'queryFilter' => TRUE,
                 'shy'         => TRUE
             ),
index 42fd7b0..abecf3b 100644 (file)
@@ -28,4 +28,165 @@ class Sales_Setup_Update_Release10 extends Setup_Update_Abstract
         }
         $this->setApplicationVersion('Sales', '10.1');
     }
+
+    /**
+     * update to 10.2
+     *
+     * Add fulltext index for description field of sales_contracts
+     */
+    public function update_1()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('sales_contracts', $declaration);
+
+        $this->setTableVersion('sales_contracts', 9);
+        $this->setApplicationVersion('Sales', '10.2');
+    }
+
+    /**
+     * update to 10.3
+     *
+     * Add fulltext index for description field of sales_products
+     */
+    public function update_2()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('sales_products', $declaration);
+
+        $this->setTableVersion('sales_products', 7);
+        $this->setApplicationVersion('Sales', '10.3');
+    }
+
+    /**
+     * update to 10.4
+     *
+     * Add fulltext index for description field of sales_customers
+     */
+    public function update_3()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('sales_customers', $declaration);
+
+        $this->setTableVersion('sales_customers', 2);
+        $this->setApplicationVersion('Sales', '10.4');
+    }
+
+    /**
+     * update to 10.5
+     *
+     * Add fulltext index for description field of sales_suppliers
+     */
+    public function update_4()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('sales_suppliers', $declaration);
+
+        $this->setTableVersion('sales_suppliers', 2);
+        $this->setApplicationVersion('Sales', '10.5');
+    }
+
+    /**
+     * update to 10.6
+     *
+     * Add fulltext index for description field of sales_purchase_invoices
+     */
+    public function update_5()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('sales_purchase_invoices', $declaration);
+
+        $this->setTableVersion('sales_purchase_invoices', 5);
+        $this->setApplicationVersion('Sales', '10.6');
+    }
+
+    /**
+     * update to 10.7
+     *
+     * Add fulltext index for description field of sales_sales_invoices
+     */
+    public function update_6()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('sales_sales_invoices', $declaration);
+
+        $this->setTableVersion('sales_sales_invoices', 7);
+        $this->setApplicationVersion('Sales', '10.7');
+    }
+    
+    /**
+     * update to 10.8
+     *
+     * Add fulltext index for description field of sales_offers
+     */
+    public function update_7()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('sales_offers', $declaration);
+
+        $this->setTableVersion('sales_offers', 2);
+        $this->setApplicationVersion('Sales', '10.8');
+    }
 }
index affe9d2..09e0867 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <application>
     <name>Sales</name>
-    <version>10.1</version>
+    <version>10.8</version>
     <order>50</order>
     <status>enabled</status>
     <tables>
@@ -55,7 +55,7 @@
         </table>
         <table>
             <name>sales_contracts</name>
-            <version>8</version>
+            <version>9</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <field>id</field>
                     </reference>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
         <table>
             <name>sales_products</name>
-            <version>6</version>
+            <version>7</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>id</name>
                     </field>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
         <table>
         </table>
         <table>
             <name>sales_customers</name>
-            <version>1</version>
+            <version>2</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>id</name>
                     </field>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
         <table>
             <name>sales_suppliers</name>
-            <version>1</version>
+            <version>2</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>id</name>
                     </field>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
         <table>
         </table>
         <table>
             <name>sales_purchase_invoices</name>
-            <version>4</version>
+            <version>5</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>id</name>
                     </field>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
         <table>
             <name>sales_sales_invoices</name>
-            <version>6</version>
+            <version>7</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>id</name>
                     </field>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
         <table>
         </table>
         <table>
             <name>sales_offers</name>
-            <version>1</version>
+            <version>2</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>id</name>
                     </field>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
     </tables>
index f71baa4..00b4c51 100644 (file)
@@ -49,7 +49,7 @@ class Tasks_Model_TaskFilter extends Tinebase_Model_Filter_FilterGroup
         'organizer'            => array('filter' => 'Tinebase_Model_Filter_User'),
         'status'               => array('filter' => 'Tinebase_Model_Filter_Text'),
         'due'                  => array('filter' => 'Tinebase_Model_Filter_DateTime'),
-        'description'          => array('filter' => 'Tinebase_Model_Filter_Text'),
+        'description'          => array('filter' => 'Tinebase_Model_Filter_FullText'),
         'summary'              => array('filter' => 'Tinebase_Model_Filter_Text'),
         'tag'                  => array('filter' => 'Tinebase_Model_Filter_Tag', 'options' => array(
             'idProperty' => 'tasks.id',
diff --git a/tine20/Tasks/Setup/Update/Release10.php b/tine20/Tasks/Setup/Update/Release10.php
new file mode 100644 (file)
index 0000000..af98fd6
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Tasks
+ * @subpackage  Setup
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL3
+ * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Michael Spahn <m.spahn@metaways.de>
+ */
+class Tasks_Setup_Update_Release10 extends Setup_Update_Abstract
+{
+    /**
+     * update to 10.1
+     *
+     * Add fulltext index for description field
+     */
+    public function update_0()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('tasks', $declaration);
+
+        $this->setTableVersion('tasks', 9);
+        $this->setApplicationVersion('Tasks', '10.1');
+    }
+}
index 1d05b56..9547597 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <application>
     <name>Tasks</name>
-    <version>10.0</version>
+    <version>10.1</version>
     <order>30</order>
     <depends>
         <application>Admin</application>
@@ -9,7 +9,7 @@
     <tables>
         <table>
             <name>tasks</name>
-            <version>8</version>
+            <version>9</version>
             <declaration>
                 <field>
                     <name>id</name>
                     </field>
                 </index>
                 <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
+                <index>
                     <name>etag</name>
                     <field>
                         <name>etag</name>
index 1fed5d7..ffdcdf4 100644 (file)
@@ -92,7 +92,12 @@ class Timetracker_Controller_Timesheet extends Tinebase_Controller_Record_Abstra
         }
         
         return self::$_instance;
-    }        
+    }
+
+    public static function unsetInstance()
+    {
+        self::$_instance = null;
+    }
 
     /****************************** functions ************************/
 
@@ -301,7 +306,8 @@ class Timetracker_Controller_Timesheet extends Tinebase_Controller_Record_Abstra
                 
                 if ($hasGrant) {
                     foreach ($this->_fieldGrants as $field => $config) {
-                        if (isset($_record->$field) && $_record->$field != $config['default']) {
+                        $fieldValue = $_record->$field;
+                        if (isset($fieldValue) && $fieldValue != $config['default']) {
                             $hasGrant &= Timetracker_Model_TimeaccountGrants::hasGrant($_record->timeaccount_id, $config['requiredGrant']);
                         }
                     }
index c94066c..b874c8f 100644 (file)
@@ -115,8 +115,9 @@ class Timetracker_Model_Timeaccount extends Sales_Model_Accountable_Abstract
             ),
             'description'       => array(
                 'label'                 => 'Description', // _('Description')
-                'type'                  => 'text',
+                'type'                  => 'fulltext',
                 'showInDetailsPanel'    => TRUE,
+                'queryFilter'           => true,
                 'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
             ),
             'budget'            => array(
index 3e9ec5f..022b0fd 100644 (file)
@@ -165,7 +165,7 @@ class Timetracker_Model_Timesheet extends Tinebase_Record_Abstract implements Sa
             ),
             'description'           => array(
                 'label'                 => 'Description', // _('Description')
-                'type'                  => 'text',
+                'type'                  => 'fulltext',
                 'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
                 'queryFilter'           => TRUE
             ),
diff --git a/tine20/Timetracker/Setup/Update/Release10.php b/tine20/Timetracker/Setup/Update/Release10.php
new file mode 100644 (file)
index 0000000..e0a97f3
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Timetracker
+ * @subpackage  Setup
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL3
+ * @copyright   Copyright (c) 2017 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Michael Spahn <m.spahn@metaways.de>
+ */
+class Timetracker_Setup_Update_Release10 extends Setup_Update_Abstract
+{
+    /**
+     * update to 10.1
+     *
+     * Add fulltext index to field description of timesheet
+     */
+    public function update_0()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('timetracker_timesheet', $declaration);
+
+        $this->setTableVersion('timetracker_timesheet', '6');
+        $this->setApplicationVersion('Timetracker', '10.1');
+    }
+
+    /**
+     * update to 10.2
+     *
+     * Add fulltext index to field description of timesheet
+     */
+    public function update_1()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('timetracker_timeaccount', $declaration);
+
+        $this->setTableVersion('timetracker_timeaccount', '11');
+        $this->setApplicationVersion('Timetracker', '10.2');
+    }
+}
index a2555f4..216f72a 100644 (file)
@@ -2,7 +2,7 @@
 <application>
     <name>Timetracker</name>
     <!-- gettext('Timetracker') -->   
-    <version>10.0</version>
+    <version>10.2</version>
     <order>60</order>
     <status>enabled</status>
     <depends>
@@ -11,7 +11,7 @@
     <tables>
         <table>
             <name>timetracker_timeaccount</name>
-            <version>10</version>
+            <version>11</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <field>id</field>
                     </reference>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
         <table>
             <name>timetracker_timesheet</name>
-            <version>5</version>
+            <version>6</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <field>id</field>
                     </reference>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
     </tables>
index 1a1df5c..0a400c6 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Cache
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2015-2016 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2015-2017 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  *
  * TODO refactor persistent cache visiblity handling as this is not quite easy to read/understand
@@ -183,7 +183,7 @@ class Tinebase_Cache_PerRequest
         // lookup in in-memory cache
         if (isset($this->_inMemoryCache[$class]) &&
             isset($this->_inMemoryCache[$class][$method]) &&
-            (isset($this->_inMemoryCache[$class][$method][$cacheId]) || array_key_exists($cacheId, $this->_inMemoryCache[$class][$method]))
+            isset($this->_inMemoryCache[$class][$method][$cacheId])
         ) {
             return $this->_inMemoryCache[$class][$method][$cacheId]['value'];
         };
index c51d918..dc0d157 100644 (file)
@@ -102,7 +102,7 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
     /**
      * add new custom field
      *
-     * @param Tinebase_Model_CustomField_Config $_customField
+     * @param Tinebase_Model_CustomField_Config $_record
      * @return Tinebase_Model_CustomField_Config
      */
     public function addCustomField(Tinebase_Model_CustomField_Config $_record)
@@ -119,7 +119,7 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
     /**
      * update custom field
      *
-     * @param Tinebase_Model_CustomField_Config $_customField
+     * @param Tinebase_Model_CustomField_Config $_record
      * @return Tinebase_Model_CustomField_Config
      */
     public function updateCustomField(Tinebase_Model_CustomField_Config $_record)
@@ -138,6 +138,7 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
      */
     public function getCustomField($_customFieldId)
     {
+        /** @noinspection PhpIncompatibleReturnTypeInspection */
         return $this->_backendConfig->get($_customFieldId);
     }
 
@@ -305,8 +306,8 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
     /**
      * saves multiple Custom Fields
      * @param String $_modelName
-     * @param Array $_recordIds
-     * @param Array $_customFieldValues
+     * @param array $_recordIds
+     * @param array $_customFieldValues
      */
     
     public function saveMultipleCustomFields($_modelName, $_recordIds, $_customFieldValues) 
@@ -365,11 +366,12 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
         }
         $this->_clearCache();
       }
-    
+
     /**
      * save custom fields of record in its custom fields table
      *
      * @param Tinebase_Record_Interface $_record
+     * @throws Tinebase_Exception_Record_Validation
      */
     public function saveRecordCustomFields(Tinebase_Record_Interface $_record)
     {
@@ -398,6 +400,7 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
                     $modelName  = $modelParts[1] . '_Model_' . $modelParts[3];
                     
                     if (is_array($value)) {
+                        /** @var Tinebase_Record_Interface $model */
                         $model = new $modelName(array(), TRUE);
                         $value = $value[$model->getIdProperty()];
                     }
@@ -458,8 +461,7 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
         
         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
             . ' Adding ' . count($customFields) . ' customfields to record  ' . $_record->getId());
-        
-        $result = array();
+
         foreach ($customFields as $customField) {
             $this->_setCfValueInRecord($_record, $customField, $_configs);
         }
@@ -468,11 +470,11 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
     /**
      * set customfield value in record
      * 
-     * @param Tinebase_Record_Abstract $record
+     * @param Tinebase_Record_Interface $record
      * @param Tinebase_Model_CustomField_Value $customField
      * @param Tinebase_Record_RecordSet $configs
      */
-    protected function _setCfValueInRecord(Tinebase_Record_Abstract $record, Tinebase_Model_CustomField_Value $customField, Tinebase_Record_RecordSet $configs)
+    protected function _setCfValueInRecord(Tinebase_Record_Interface $record, Tinebase_Model_CustomField_Value $customField, Tinebase_Record_RecordSet $configs)
     {
         $recordCfs = $record->customfields;
         $idx = $configs->getIndexById($customField->customfield_id);
@@ -543,6 +545,7 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
         // NOTE: as filtering is currently very slow, we have to loop the customfields and add the value to the record.
         // @see 0007496: timeout when opening multiedit dlg and assigning records to events/projects/email
         // @see 0007558: reactivate indices in Tinebase_Record_RecordSet
+        /** @var Tinebase_Record_Interface $record */
         $record = NULL;
         foreach ($customFields as $customField) {
             if (! $record || $record->getId() !== $customField->record_id) {
@@ -559,7 +562,7 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
      * get custom fields of record(s)
      *
      * @param string|array $_recordId
-     * @param array $_recordId
+     * @param array|null $_configIds
      * @return Tinebase_Record_RecordSet of Tinebase_Model_CustomField_Value
      */
     protected function _getCustomFields($_recordId, $_configIds = NULL)
@@ -596,6 +599,7 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
      * @param   string $_accountType
      * @param   int $_accountId
      * @return  void
+     * @throws Tinebase_Exception_Backend
      */
     public function setGrants($_customfieldId, $_grants = array(), $_accountType = NULL, $_accountId = NULL)
     {
@@ -671,7 +675,8 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
     * 
     * @todo this needs to clear in a more efficient way
     */
-    public function clearCacheForConfig(Tinebase_Model_CustomField_Config $record)
+    public function clearCacheForConfig(/** @noinspection PhpUnusedParameterInspection */
+        Tinebase_Model_CustomField_Config $record)
     {
         $this->_clearCache();
         
@@ -727,12 +732,12 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
      * get list of custom field values
      *
      * @param Tinebase_Model_Filter_FilterGroup $_filter
-     * @param Tinebase_Model_Pagination $_pagination
+     * @param Tinebase_Record_Interface $_pagination
      * @param bool $_getRelations (unused)
-     * @param boolean $_onlyIds (unused)
+     * @param bool $_onlyIds (unused)
      * @return Tinebase_Record_RecordSet
      */
-    public function searchConfig(Tinebase_Model_Filter_FilterGroup $_filter = NULL, Tinebase_Record_Interface $_pagination = NULL, $_getRelations = FALSE, $_onlyIds = FALSE)
+    public function searchConfig(Tinebase_Model_Filter_FilterGroup $_filter = NULL, Tinebase_Record_Interface $_pagination = NULL, /** @noinspection PhpUnusedParameterInspection */ $_getRelations = FALSE, /** @noinspection PhpUnusedParameterInspection */ $_onlyIds = FALSE)
     {
         $result = $this->_backendConfig->search($_filter, $_pagination);
         return $result;
index 0775628..d747558 100644 (file)
@@ -570,6 +570,14 @@ class Tinebase_Model_Filter_FilterGroup implements Iterator
     {
         return $this->_concatenationCondition;
     }
+
+    /**
+     * @param string $_condition
+     */
+    public function setCondition($_condition)
+    {
+        $this->_concatenationCondition = $_condition === self::CONDITION_OR ? self::CONDITION_OR : ($_condition === self::CONDITION_AND ? self::CONDITION_AND : ($this->_concatenationCondition !== NULL ? $this->_concatenationCondition : self::CONDITION_AND));
+    }
     
     /**
      * set id
index 8712271..eca3182 100644 (file)
@@ -10,7 +10,7 @@
  */
 
 /**
- * Tinebase_Model_Filter_Text
+ * Tinebase_Model_Filter_FullText
  *
  * filters one filterstring in one property
  *
@@ -24,6 +24,13 @@ class Tinebase_Model_Filter_FullText extends Tinebase_Model_Filter_Abstract
      */
     protected $_operators = array(
         0 => 'contains',
+        1 => 'notcontains',
+        2 => 'equals',
+        3 => 'not',
+        4 => 'startswith',
+        5 => 'endswith',
+        6 => 'notin',
+        7 => 'in'
     );
 
     /**
@@ -37,49 +44,97 @@ class Tinebase_Model_Filter_FullText extends Tinebase_Model_Filter_Abstract
     {
         $db = $_select->getAdapter();
 
-        // mysql supports full text for InnoDB as of 5.6
-        if ( ! Setup_Backend_Factory::factory()->supports('mysql >= 5.6.4') ) {
+        $not = false;
+        if (false !== strpos($this->_operator, 'not')) {
+            $not = true;
+        }
+        if ($this->_operator !== 'contains' && $this->_operator !== 'notcontains') {
+            if (true === $not) {
+                $this->_operator = 'notcontains';
+            } else {
+                $this->_operator = 'contains';
+            }
+        }
 
-            if (Setup_Core::isLogLevel(Zend_Log::NOTICE)) Setup_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ .
+        $values = array();
+        foreach ((array)$this->_value as $value) {
+            //replace full text meta characters
+            //$value = str_replace(array('+', '-', '<', '>', '~', '*', '(', ')', '"'), ' ', $value);
+            $value = preg_replace('#\W#u', ' ', $value);
+            // replace multiple spaces with just one
+            $value = preg_replace('# +#u', ' ', trim($value));
+            $values = array_merge($values, explode(' ', $value));
+        }
+
+        // mysql supports full text for InnoDB as of 5.6.4
+        // full text can't do a pure negative search...
+        if (true === $not || ! Setup_Backend_Factory::factory()->supports('mysql >= 5.6.4') ) {
+
+            if (true !== $not && Setup_Core::isLogLevel(Zend_Log::NOTICE)) Setup_Core::getLogger()->notice(__METHOD__ . '::' . __LINE__ .
                 ' full text search is only supported on mysql/mariadb 5.6.4+ ... do yourself a favor and migrate. This query now maybe very slow for larger amount of data!');
 
-            $filterGroup = new Tinebase_Model_Filter_FilterGroup();
+            $values = array_filter($values, function($val) {
+                return mb_strlen($val) >= 3;
+            });
 
-            if (!is_array($this->_value)) {
-                $this->_value = array($this->_value);
+            if (count($values) === 0) {
+                $_select->where('1 = 1');
+                return;
             }
-            foreach ($this->_value as $value) {
-                //replace full text meta characters
-                //$value = str_replace(array('+', '-', '<', '>', '~', '*', '(', ')', '"'), ' ', $value);
-                $value = preg_replace('#\W#u', ' ', $value);
-                // replace multiple spaces with just one
-                $value = preg_replace('# +#u', ' ', trim($value));
-                $values = explode(' ', $value);
-                foreach($values as $value) {
-                    $filter = new Tinebase_Model_Filter_Text($this->_field, 'contains', $value, isset($this->_options['tablename']) ? array('tablename' => $this->_options['tablename']) : array());
-                    $filterGroup->addFilter($filter);
-                }
+
+            $filterGroup = new Tinebase_Model_Filter_FilterGroup();
+
+            foreach ($values as $value) {
+                $filter = new Tinebase_Model_Filter_Text($this->_field, $this->_operator, $value, isset($this->_options['tablename']) ? array('tablename' => $this->_options['tablename']) : array());
+                $filterGroup->addFilter($filter);
             }
 
             Tinebase_Backend_Sql_Filter_FilterGroup::appendFilters($_select, $filterGroup, $_backend);
+
         } else {
+            $ftConfig = $this->getMySQLFullTextConfig();
+            $values = array_filter($values, function($val) use ($ftConfig) {
+                return mb_strlen($val) >= $ftConfig['tokenSize'] && !in_array($val, $ftConfig['stopWords']);
+            });
 
-            $field = $this->_getQuotedFieldName($_backend);
+            if (count($values) === 0) {
+                $_select->where('1 = 1');
+                return;
+            }
 
+            $field = $this->_getQuotedFieldName($_backend);
             $searchTerm = '';
-            if (!is_array($this->_value)) {
-                $this->_value = array($this->_value);
-            }
-            foreach ($this->_value as $value) {
-                //replace full text meta characters
-                //$value = str_replace(array('+', '-', '<', '>', '~', '*', '(', ')', '"'), ' ', $value);
-                $value = preg_replace('#\W#u', ' ', $value);
-                // replace multiple spaces with just one
-                $value = preg_replace('# +#u', ' +', trim($value));
-                $searchTerm .= ($searchTerm === '' ? '+' : ' +') . $value . '*';
+
+            foreach ($values as $value) {
+                $searchTerm .= ($searchTerm !== '' ? ', ' : '') . '+' . $value . '*';
             }
 
             $_select->where('MATCH (' . $field . $db->quoteInto(') AGAINST (? IN BOOLEAN MODE)', $searchTerm));
         }
     }
+
+    /**
+     * this looks up the default mysql innodb full text stop word list and returns the contents as array
+     *
+     * TODO implement retrieving non default / configured stop word tables
+     *
+     * @return array
+     */
+    protected function getMySQLFullTextConfig()
+    {
+        $cacheId = 'mysqlFullTextConfig';
+
+        try {
+            return Tinebase_Cache_PerRequest::getInstance()->load(__CLASS__, __METHOD__, $cacheId, Tinebase_Cache_PerRequest::VISIBILITY_SHARED);
+        } catch (Tinebase_Exception_NotFound $tenf) {}
+
+        $db = Tinebase_Core::getDb();
+
+        $result['stopWords'] = $db->query('SELECT `value` FROM INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD')->fetchAll(Zend_Db::FETCH_COLUMN, 0);
+        $result['tokenSize'] = $db->query('SELECT @@innodb_ft_min_token_size')->fetchColumn(0);
+
+        Tinebase_Cache_PerRequest::getInstance()->save(__CLASS__, __METHOD__, $cacheId, $result, Tinebase_Cache_PerRequest::VISIBILITY_SHARED);
+
+        return $result;
+    }
 }
index ca58b18..4de2a6e 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Filter
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2007-2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2017 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Cornelius Weiss <c.weiss@metaways.de>
  */
 
@@ -38,6 +38,7 @@ class Tinebase_Model_Filter_Int extends Tinebase_Model_Filter_Abstract
         7 => 'notin',
         8 => 'notnull',
         9 => 'isnull',
+        10=> 'contains'
     );
     
     /**
@@ -45,6 +46,7 @@ class Tinebase_Model_Filter_Int extends Tinebase_Model_Filter_Abstract
      */
     protected $_opSqlMap = array(
         'equals'     => array('sqlop' => ' = ?'   ,     'wildcards' => '?'  ),
+        'contains'   => array('sqlop' => ' LIKE ?',     'wildcards' => '%?%'),
         'startswith' => array('sqlop' => ' LIKE ?',     'wildcards' => '?%' ),
         'endswith'   => array('sqlop' => ' LIKE ?',     'wildcards' => '%?' ),
         'greater'    => array('sqlop' => ' > ?',        'wildcards' => '?'  ),
index 5c9af0d..051c2da 100644 (file)
@@ -50,7 +50,7 @@ class Tinebase_Model_Tree_Node_Filter extends Tinebase_Model_Filter_GrantsFilter
     protected $_filterModel = array(
         'query'                => array(
             'filter' => 'Tinebase_Model_Filter_Query', 
-            'options' => array('fields' => array('name', 'content'))
+            'options' => array('fields' => array('name', 'content', 'description'))
         ),
         'id'                   => array('filter' => 'Tinebase_Model_Filter_Id'),
         'path'                 => array('filter' => 'Tinebase_Model_Tree_Node_PathFilter'),
@@ -87,7 +87,7 @@ class Tinebase_Model_Tree_Node_Filter extends Tinebase_Model_Filter_GrantsFilter
             'options' => array('tablename' => 'tree_fileobjects')
         ),
         'description'          => array(
-            'filter' => 'Tinebase_Model_Filter_Text',
+            'filter' => 'Tinebase_Model_Filter_FullText',
             'options' => array('tablename' => 'tree_fileobjects')
         ),
     // tree_filerevisions table
index 9e3a09f..7971409 100644 (file)
@@ -617,6 +617,7 @@ class Tinebase_Setup_Update_Release10 extends Setup_Update_Abstract
         $this->setApplicationVersion('Tinebase', '10.13');
     }
 
+
     /**
      * update to 10.14
      *
@@ -852,4 +853,30 @@ class Tinebase_Setup_Update_Release10 extends Setup_Update_Abstract
                 . ' Removed old container ' . $container->name);
         }
     }
+
+
+
+
+    /**
+     * update to 10.18
+     *
+     * Add fulltext index for description field
+     */
+    public function update_17()
+    {
+        $declaration = new Setup_Backend_Schema_Index_Xml('
+            <index>
+                <name>description</name>
+                <fulltext>true</fulltext>
+                <field>
+                    <name>description</name>
+                </field>
+            </index>
+        ');
+
+        $this->_backend->addIndex('tree_fileobjects', $declaration);
+
+        $this->setTableVersion('tree_fileobjects', '5');
+        $this->setApplicationVersion('Tinebase', '10.18');
+    }
 }
index 7ea64a8..80f3036 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <application>
     <name>Tinebase</name>
-    <version>10.17</version>
+    <version>10.18</version>
     <tables>
         <table>
             <name>applications</name>
         
         <table>
             <name>tree_fileobjects</name>
-            <version>4</version>
+            <version>5</version>
             <declaration>
                 <field>
                     <name>id</name>
                         <name>is_deleted</name>
                     </field>
                 </index>
+                <index>
+                    <name>description</name>
+                    <fulltext>true</fulltext>
+                    <field>
+                        <name>description</name>
+                    </field>
+                </index>
             </declaration>
         </table>
 
                 </index>
             </declaration>
         </table>
+
         <table>
             <name>tree_nodes</name>
             <version>2</version>