Merge branch '2015.11-develop' into 2016.11
authorPhilipp Schüle <p.schuele@metaways.de>
Thu, 20 Oct 2016 11:34:42 +0000 (13:34 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Thu, 20 Oct 2016 11:34:42 +0000 (13:34 +0200)
18 files changed:
tests/tine20/Tinebase/ApplicationTest.php
tests/tine20/Tinebase/Frontend/CliTest.php
tine20/ActiveSync/Controller.php
tine20/Calendar/Setup/DemoData.php
tine20/Courses/Controller.php
tine20/Felamimail/Controller.php
tine20/Filemanager/Controller.php
tine20/Filemanager/Frontend/Download.php
tine20/Tinebase/Application.php
tine20/Tinebase/CustomField.php
tine20/Tinebase/Frontend/Cli.php
tine20/Tinebase/Model/CustomField/Config.php
tine20/Tinebase/Model/CustomField/Value.php
tine20/Tinebase/Model/Filter/Abstract.php
tine20/Tinebase/Model/Note.php
tine20/Tinebase/Model/Tree/Node/Filter.php
tine20/Tinebase/Notes.php
tine20/Tinebase/Setup/DemoData/Abstract.php

index ec46257..984868e 100644 (file)
@@ -256,4 +256,232 @@ class Tinebase_ApplicationTest extends TestCase
             }
         }
     }
+
+    /**
+     * Test
+     */
+    public function testGetModelsOfAllApplications()
+    {
+        $models = Tinebase_Application::getInstance()->getModelsOfAllApplications();
+        $applications = Tinebase_Application::getInstance()->getApplicationsByState(Tinebase_Application::ENABLED);
+        $appNames = $applications->name;
+
+        $expectedData = array(
+            'ActiveSync' => array(
+                'ActiveSync_Model_Policy',
+                'ActiveSync_Model_Device',
+            ),
+            'Addressbook' => array(
+                'Addressbook_Model_Salutation',
+                'Addressbook_Model_List',
+                'Addressbook_Model_ListRole',
+                'Addressbook_Model_ListMemberRole',
+                'Addressbook_Model_Contact',
+            ),
+            'Admin' => array(
+                'Admin_Model_Config',
+                'Admin_Model_SambaMachine',
+            ),
+            'Calendar' => array(
+                'Calendar_Model_Resource',
+                'Calendar_Model_iMIP',
+                'Calendar_Model_Rrule',
+                'Calendar_Model_AttendeeRole',
+                'Calendar_Model_Event',
+                'Calendar_Model_FreeBusy',
+                'Calendar_Model_Exdate',
+                'Calendar_Model_Attender',
+                'Calendar_Model_AttendeeStatus',
+            ),
+            'CoreData' => array(
+                'CoreData_Model_CoreData',
+            ),
+            'Courses' => array(
+                'Courses_Model_Course',
+            ),
+            'Crm' => array(
+                'Crm_Model_LeadType',
+                'Crm_Model_LeadSource',
+                'Crm_Model_LeadState',
+                'Crm_Model_Lead',
+            ),
+            'ExampleApplication' => array(
+                'ExampleApplication_Model_ExampleRecord',
+                'ExampleApplication_Model_Status',
+            ),
+            'Expressomail' => array(
+                'Expressomail_Model_Account',
+                'Expressomail_Model_Sieve_Vacation',
+                'Expressomail_Model_Sieve_Rule',
+                'Expressomail_Model_PreparedMessagePart',
+                'Expressomail_Model_Message',
+                'Expressomail_Model_Folder',
+            ),
+            'Felamimail' => array(
+                'Felamimail_Model_Account',
+                'Felamimail_Model_Sieve_Vacation',
+                'Felamimail_Model_Sieve_Rule',
+                'Felamimail_Model_PreparedMessagePart',
+                'Felamimail_Model_Message',
+                'Felamimail_Model_Folder',
+            ),
+            'Filemanager' => array(
+                'Filemanager_Model_Node',
+                'Filemanager_Model_DownloadLink',
+            ),
+            'HumanResources' => array(
+                'HumanResources_Model_ExtraFreeTime',
+                'HumanResources_Model_Account',
+                'HumanResources_Model_Employee',
+                'HumanResources_Model_FreeTimeType',
+                'HumanResources_Model_ExtraFreeTimeType',
+                'HumanResources_Model_FreeTimeStatus',
+                'HumanResources_Model_Contract',
+                'HumanResources_Model_CostCenter',
+                'HumanResources_Model_FreeDay',
+                'HumanResources_Model_WorkingTime',
+                'HumanResources_Model_FreeTime',
+            ),
+            'Inventory' => array(
+                'Inventory_Model_Status',
+                'Inventory_Model_InventoryItem',
+            ),
+            'Phone' => array(
+                'Phone_Model_Call',
+                'Phone_Model_MyPhone',
+            ),
+            'Projects' => array(
+                'Projects_Model_Project',
+                'Projects_Model_AttendeeRole',
+                'Projects_Model_Status',
+            ),
+            'Sales' => array(
+                'Sales_Model_Number',
+                'Sales_Model_Config',
+                'Sales_Model_PurchaseInvoice',
+                'Sales_Model_Supplier',
+                'Sales_Model_PaymentMethod',
+                'Sales_Model_OrderConfirmation',
+                'Sales_Model_Customer',
+                'Sales_Model_Address',
+                'Sales_Model_ProductCategory',
+                'Sales_Model_InvoiceCleared',
+                'Sales_Model_InvoiceType',
+                'Sales_Model_Invoice',
+                'Sales_Model_Contract',
+                'Sales_Model_InvoicePosition',
+                'Sales_Model_Division',
+                'Sales_Model_ProductAggregate',
+                'Sales_Model_Offer',
+                'Sales_Model_CostCenter',
+                'Sales_Model_Product',
+            ),
+            'SimpleFAQ' => array(
+                'SimpleFAQ_Model_Faq',
+                'SimpleFAQ_Model_Config',
+            ),
+            'Tasks' => array(
+                'Tasks_Model_Task',
+                'Tasks_Model_Priority',
+                'Tasks_Model_Pagination',
+                'Tasks_Model_Status',
+            ),
+            'Timetracker' => array(
+                'Timetracker_Model_TimeaccountGrants',
+                'Timetracker_Model_Timesheet',
+                'Timetracker_Model_Timeaccount',
+            ),
+            'Tinebase' => array(
+                'Tinebase_Model_AccessLog',
+                'Tinebase_Model_ContainerContent',
+                'Tinebase_Model_Application',
+                'Tinebase_Model_Registration',
+                'Tinebase_Model_Image',
+                'Tinebase_Model_Tree_Node',
+                'Tinebase_Model_Tree_FileObject',
+                'Tinebase_Model_ModificationLog',
+                'Tinebase_Model_Config',
+                'Tinebase_Model_Group',
+                'Tinebase_Model_State',
+                'Tinebase_Model_CredentialCache',
+                'Tinebase_Model_PersistentFilterGrant',
+                'Tinebase_Model_AsyncJob',
+                'Tinebase_Model_CustomField_Value',
+                'Tinebase_Model_CustomField_Config',
+                'Tinebase_Model_CustomField_Grant',
+                'Tinebase_Model_Container',
+                'Tinebase_Model_Tag',
+                'Tinebase_Model_Relation',
+                'Tinebase_Model_TagRight',
+                'Tinebase_Model_NoteType',
+                'Tinebase_Model_Alarm',
+                'Tinebase_Model_FullTag',
+                'Tinebase_Model_SAMGroup',
+                'Tinebase_Model_EmailUser',
+                'Tinebase_Model_Department',
+                'Tinebase_Model_ImportException',
+                'Tinebase_Model_User',
+                'Tinebase_Model_Role',
+                'Tinebase_Model_Note',
+                'Tinebase_Model_RoleRight',
+                'Tinebase_Model_Pagination',
+                'Tinebase_Model_TempFile',
+                'Tinebase_Model_ImportExportDefinition',
+                'Tinebase_Model_OpenId_Association',
+                'Tinebase_Model_OpenId_TrustedSite',
+                'Tinebase_Model_FullUser',
+                'Tinebase_Model_Import',
+                'Tinebase_Model_UpdateMultipleException',
+                'Tinebase_Model_SAMUser',
+                'Tinebase_Model_Path',
+                'Tinebase_Model_Preference',
+                'Tinebase_Model_PersistentObserver',
+                'Tinebase_Model_Grants',
+            ),
+        );
+
+        // remove bogus apps
+        $remove = array('Voipmanager', 'RequestTracker', 'Sipgate', 'Expressodriver');
+        foreach($remove as $r)
+        {
+            if (($key = array_search($r, $appNames)) !== false) {
+                unset($appNames[$key]);
+            }
+        }
+
+        // check all expected models are there
+        foreach($expectedData as $appName => $expectedModels) {
+            if (array_search($appName, $appNames) !== false) {
+                foreach ($expectedModels as $expectedModel) {
+                    $this->assertTrue(array_search($expectedModel, $models) !== false, 'did not find model: ' . $expectedModel);
+                }
+            }
+        }
+
+        // if there is at least one model, remove the app
+        foreach($models as $model) {
+            list($appName) = explode('_', $model);
+            if (($key = array_search($appName, $appNames)) !== false) {
+                unset($appNames[$key]);
+            }
+        }
+
+        // no apps should remain => we found models for each app, expect the bogus ones from above
+        $this->assertEquals(0, count($appNames), 'applications found for which no models where found: '.print_r($appNames, true));
+
+        // check if we found to much models
+        $appNames = $applications->name;
+        foreach($expectedData as $appName => $expectedModels) {
+            if (array_search($appName, $appNames) !== false) {
+                foreach ($expectedModels as $expectedModel) {
+                    if (($key = array_search($expectedModel, $models)) !== false) {
+                        unset($models[$key]);
+                    }
+                }
+            }
+        }
+
+        // no models should remain
+        $this->assertEquals(0, count($models), 'unexpected models found: '.print_r($models, true));
+    }
 }
index 208ac10..2812ad9 100644 (file)
@@ -306,4 +306,191 @@ class Tinebase_Frontend_CliTest extends TestCase
         $this->assertGreaterThan(1, count($matches));
         $this->assertGreaterThanOrEqual(1, $matches[1], 'at least unittest user should have logged in once');
     }
+
+    /**
+     * test cleanNotes
+     */
+    public function testCleanNotes()
+    {
+        // initial clean... tests don't clean up properly
+        ob_start();
+        $this->_cli->cleanNotes();
+        $out = ob_get_clean();
+
+        $noteController = Tinebase_Notes::getInstance();
+        $models = Tinebase_Application::getInstance()->getModelsOfAllApplications();
+
+        $allNotes = $noteController->getAllNotes();
+        $dbArtifacts = $allNotes->count();
+
+        $notesCreated = 0;
+        $realDataNotes = 0;
+        foreach($models as $model) {
+            /** @var Tinebase_Record_Interface $instance */
+            $instance = new $model();
+            if ($instance->has('notes')) {
+
+                if (strpos($model, 'Tinebase') === 0) {
+                    continue;
+                }
+
+                //create dead notes for each of those models
+                $note = new Tinebase_Model_Note(array(
+                    'note_type_id' => 1,
+                    'note'  => 'test note text',
+                    'record_id' => Tinebase_Record_Abstract::generateUID(),
+                    'record_model' => $model,
+                ));
+
+                $noteController->addNote($note);
+                ++$notesCreated;
+            }
+        }
+
+        // add some real data
+        $contact = new Addressbook_Model_Contact(array(
+            'n_family' => 'someone',
+            'notes' => array(array(
+                'note_type_id' => 1,
+                'note'  => 'test note text for real record',
+            ))
+        ));
+        Addressbook_Controller_Contact::getInstance()->create($contact);
+        $realDataNotes += 2; //created and custom note
+
+        $event = new Calendar_Model_Event(array(
+            'organizer' => 'a@b.shooho',
+            'dtstart'   => '2015-01-01 00:00:00',
+            'dtend'     => '2015-01-01 01:00:00',
+            'summary'   => 'test event',
+            'notes' => array(array(
+                'note_type_id' => 1,
+                'note'  => 'test note text for real record',
+            ))
+        ));
+        Calendar_Controller_Event::getInstance()->create($event);
+        $realDataNotes += 2; //created and custom note
+
+        $allNotes = $noteController->getAllNotes();
+        $this->assertEquals($notesCreated + $realDataNotes + $dbArtifacts, $allNotes->count(), 'notes created and notes in DB mismatch');
+
+        ob_start();
+        $this->_cli->cleanNotes();
+        $out = ob_get_clean();
+
+        $this->assertTrue('' === $out, 'CLI job produced output: ' . $out);
+
+        $allNotes = $noteController->getAllNotes();
+        $this->assertEquals($realDataNotes + $dbArtifacts, $allNotes->count(), 'notes not completely cleaned');
+    }
+
+
+    /**
+     * test cleanCustomfields
+     */
+    public function testCleanCustomfields()
+    {
+        $customFieldController = Tinebase_CustomField::getInstance();
+        $models = Tinebase_Application::getInstance()->getModelsOfAllApplications();
+
+        $customFieldConfigs = $customFieldController->searchConfig();
+        foreach($customFieldConfigs as $customFieldConfig) {
+            $filter = new Tinebase_Model_CustomField_ValueFilter(array(
+                array('field' => 'customfield_id', 'operator' => 'equals', 'value' => $customFieldConfig->id)
+            ));
+            $customFieldValues = $customFieldController->search($filter);
+
+            $this->assertEquals(0, $customFieldValues->count(), 'custom field values found!');
+        }
+
+        $customFieldsCreated = 0;
+        $realDataCustomFields = 0;
+        foreach($models as $model) {
+            /** @var Tinebase_Record_Interface $instance */
+            $instance = new $model();
+            list($appName) = explode('_', $model);
+
+            if ($instance->has('customfields')) {
+
+                if (strpos($model, 'Tinebase') === 0) {
+                    continue;
+                }
+
+                $cf = $customFieldController->addCustomField(
+                    new Tinebase_Model_CustomField_Config(array(
+                        'application_id'    => Tinebase_Application::getInstance()->getApplicationByName($appName)->getId(),
+                        'model'             => $model,
+                        'name'              => $model,
+                        'definition'        => array(
+                            'label'             => $model,
+                            'length'            => 255,
+                            'required'          => false,
+                            'type'              => 'string',
+                        ),
+                    ))
+                );
+
+                //create dead customfield value for each of those models
+                $customFieldValue = new Tinebase_Model_CustomField_Value(array(
+                    'record_id' => Tinebase_Record_Abstract::generateUID(),
+                    'customfield_id' => $cf->getId(),
+                    'value' => 'shoo value',
+                ));
+
+                $customFieldController->saveCustomFieldValue($customFieldValue);
+                ++$customFieldsCreated;
+            }
+        }
+
+        // add some real data
+        $contact = new Addressbook_Model_Contact(array(
+            'n_family' => 'someone',
+            'customfields' => array(
+                'Addressbook_Model_Contact' => 'test customfield text for real record',
+            )
+        ));
+        Addressbook_Controller_Contact::getInstance()->create($contact);
+        $realDataCustomFields += 1;
+
+        $event = new Calendar_Model_Event(array(
+            'organizer' => 'a@b.shooho',
+            'dtstart'   => '2015-01-01 00:00:00',
+            'dtend'     => '2015-01-01 01:00:00',
+            'summary'   => 'test event',
+            'customfields' => array(
+                'Calendar_Model_Event' => 'test customfield text for real record',
+            )
+        ));
+        Calendar_Controller_Event::getInstance()->create($event);
+        $realDataCustomFields += 1;
+
+        $sum = 0;
+        $customFieldConfigs = $customFieldController->searchConfig();
+        foreach($customFieldConfigs as $customFieldConfig) {
+            $filter = new Tinebase_Model_CustomField_ValueFilter(array(
+                array('field' => 'customfield_id', 'operator' => 'equals', 'value' => $customFieldConfig->id)
+            ));
+            $customFieldValues = $customFieldController->search($filter);
+
+            $sum += $customFieldValues->count();
+        }
+        $this->assertEquals($customFieldsCreated + $realDataCustomFields, $sum, 'customfields created and customfields in DB mismatch');
+
+        ob_start();
+        $this->_cli->cleanCustomfields();
+        $out = ob_get_clean();
+
+        $this->assertTrue('' === $out, 'CLI job produced output: ' . $out);
+
+        $sum = 0;
+        foreach($customFieldConfigs as $customFieldConfig) {
+            $filter = new Tinebase_Model_CustomField_ValueFilter(array(
+                array('field' => 'customfield_id', 'operator' => 'equals', 'value' => $customFieldConfig->id)
+            ));
+            $customFieldValues = $customFieldController->search($filter);
+
+            $sum += $customFieldValues->count();
+        }
+        $this->assertEquals($realDataCustomFields, $sum, 'customfields not completely cleaned');
+    }
 }
index 272586c..9c6dae7 100644 (file)
 class ActiveSync_Controller extends Tinebase_Controller_Abstract
 {
     /**
+     * application name (is needed in checkRight())
+     *
+     * @var string
+     */
+    protected $_applicationName = 'ActiveSync';
+
+    /**
      * holds the instance of the singleton
      *
      * @var ActiveSync_Controller
index be77262..59e0917 100644 (file)
@@ -288,8 +288,7 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
 
         // create shared events
         foreach($this->sharedEventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
     }
 
@@ -402,8 +401,7 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
 
         );
         foreach($eventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
 
         $cal = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
@@ -439,8 +437,7 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
         );
 
         foreach($eventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
     }
     
@@ -538,8 +535,7 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
 
         );
         foreach($eventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
 
         $cal = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
@@ -580,8 +576,7 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
         );
 
         foreach($eventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
     }
 
@@ -699,8 +694,7 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
             )
         );
         foreach($eventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
 
         $cal = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
@@ -749,8 +743,7 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
         );
 
         foreach($eventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
 
     }
@@ -885,8 +878,7 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
             ),
         );
         foreach($eventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
 
     }
@@ -1026,8 +1018,7 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
             )
         );
         foreach($eventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
 
         $cal = Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
@@ -1079,8 +1070,20 @@ class Calendar_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
         );
 
         foreach($eventsData as $eData) {
-            $event = new Calendar_Model_Event($eData);
-            $this->_controller->create($event, false);
+            $this->_createEvent($eData, false);
         }
     }
+
+    protected function _createEvent($eData, $checkBusy = true)
+    {
+        $tz = static::$_de ? 'Europe/Berlin' : 'UTC';
+
+        $eData['originator_tz'] = $tz;
+        date_default_timezone_set($tz);
+        $event = new Calendar_Model_Event($eData);
+        $event->setTimezone('UTC');
+        date_default_timezone_set('UTC');
+
+        $this->_controller->create($event, $checkBusy);
+    }
 }
index 3a38a7f..bf35caf 100644 (file)
@@ -32,6 +32,13 @@ class Courses_Controller extends Tinebase_Controller_Event
      * @var string
      */
     protected static $_defaultModel = 'Courses_Model_Course';
+
+    /**
+     * application name (is needed in checkRight())
+     *
+     * @var string
+     */
+    protected $_applicationName = 'Courses';
     
     /**
      * constructor (get current user)
index be6548c..61e1b56 100644 (file)
@@ -25,6 +25,13 @@ class Felamimail_Controller extends Tinebase_Controller_Event
      * @var string
      */
     protected static $_defaultModel = 'Felamimail_Model_Message';
+
+    /**
+     * application name (is needed in checkRight())
+     *
+     * @var string
+     */
+    protected $_applicationName = 'Felamimail';
     
     /**
      * holds the instance of the singleton
index 4a1ab81..3091e6d 100644 (file)
@@ -25,6 +25,13 @@ class Filemanager_Controller extends Tinebase_Controller_Event implements Tineba
      * @var string
      */
     protected static $_defaultModel = 'Filemanager_Model_Node';
+
+    /**
+     * application name (is needed in checkRight())
+     *
+     * @var string
+     */
+    protected $_applicationName = 'Filemanager';
     
     /**
      * holds the instance of the singleton
index 50abd93..94af62c 100644 (file)
@@ -162,6 +162,8 @@ class Filemanager_Frontend_Download extends Tinebase_Frontend_Http_Abstract
         if ($path !== null) {
             $view->path = (empty($path)) ? '/' . $node->name : '/' . implode('/', $path);
         }
+        
+        return $view;
     }
     
     /**
index a2f8b98..ebb330d 100644 (file)
@@ -570,4 +570,33 @@ class Tinebase_Application
         
         return $this->_db;
     }
+
+    /**
+     * returns the Models of all installed applications
+     * uses Tinebase_Application::getApplicationsByState
+     * and Tinebase_Controller_Abstract::getModels
+     *
+     * @return array
+     */
+    public function getModelsOfAllApplications()
+    {
+        $models = array();
+
+        $apps = $this->getApplicationsByState(Tinebase_Application::ENABLED);
+
+        /** @var Tinebase_Model_Application $app */
+        foreach($apps as $app) {
+            $controllerClass = $app->name . '_Controller';
+            if (!class_exists(($controllerClass))) {
+                continue;
+            }
+
+            $appModels = $controllerClass::getInstance()->getModels();
+            if (is_array($appModels)) {
+                $models = array_merge($models, $appModels);
+            }
+        }
+
+        return $models;
+    }
 }
index 1eda4f2..4264ed4 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  CustomField
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2007-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schüle <p.schuele@metaways.de>
  * 
  * @todo        add join to cf config to value backend to get name
@@ -697,8 +697,8 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
     /**
      * get list of custom field values
      *
-     * @param Tinebase_Model_Filter_FilterGroup|optional $_filter
-     * @param Tinebase_Model_Pagination|optional $_pagination
+     * @param Tinebase_Model_Filter_FilterGroup $_filter
+     * @param Tinebase_Model_Pagination $_pagination
      * @param bool $_getRelations (unused)
      * @param boolean $_onlyIds (unused)
      * @return Tinebase_Record_RecordSet
@@ -720,4 +720,32 @@ class Tinebase_CustomField implements Tinebase_Controller_SearchInterface
         $count = $this->_backendValue->searchCount($_filter);
         return $count;
     }
+
+    /**
+     * get list of custom field values
+     *
+     * @param Tinebase_Model_Filter_FilterGroup $_filter
+     * @param Tinebase_Model_Pagination $_pagination
+     * @param bool $_getRelations (unused)
+     * @param boolean $_onlyIds (unused)
+     * @return Tinebase_Record_RecordSet
+     */
+    public function searchConfig(Tinebase_Model_Filter_FilterGroup $_filter = NULL, Tinebase_Record_Interface $_pagination = NULL, $_getRelations = FALSE, $_onlyIds = FALSE)
+    {
+        $result = $this->_backendConfig->search($_filter, $_pagination);
+        return $result;
+    }
+
+    public function deleteCustomFieldValue($_ids)
+    {
+        return $this->_backendValue->delete($_ids);
+    }
+
+    public function saveCustomFieldValue(Tinebase_Model_CustomField_Value $_record)
+    {
+        if (!empty($_record->getId())) {
+            return $this->_backendValue->update($_record);
+        }
+        return $this->_backendValue->create($_record);
+    }
 }
index acc6dce..8bce7e8 100644 (file)
@@ -503,12 +503,164 @@ class Tinebase_Frontend_Cli extends Tinebase_Frontend_Cli_Abstract
 
             echo "\nCleaning modlog...";
             $this->cleanModlog();
+
+            echo "\nCleaning customfields...";
+            $this->cleanCustomfields();
+
+            echo "\nCleaning notes...";
+            $this->cleanNotes();
         }
 
         echo "\n\n";
         
         return TRUE;
     }
+
+    public function cleanNotes()
+    {
+        $notesController = Tinebase_Notes::getInstance();
+        $notes = $notesController->getAllNotes();
+        $controllers = array();
+        $models = array();
+        $deleteIds = array();
+
+        /** @var Tinebase_Model_Note $note */
+        foreach ($notes as $note) {
+            if (!isset($controllers[$note->record_model])) {
+                if (strpos($note->record_model, 'Tinebase') === 0) {
+                    continue;
+                }
+                try {
+                    $controllers[$note->record_model] = Tinebase_Core::getApplicationInstance($note->record_model);
+                } catch(Tinebase_Exception_AccessDenied $e) {
+                    // TODO log
+                    continue;
+                }
+                $oldACLCheckValue = $controllers[$note->record_model]->doContainerACLChecks(false);
+                $models[$note->record_model] = array(
+                    0 => new $note->record_model(),
+                    1 => class_exists($note->record_model . 'Filter'),
+                    2 => $note->record_model . 'Filter',
+                    3 => $oldACLCheckValue
+                );
+            }
+            $controller = $controllers[$note->record_model];
+            $model = $models[$note->record_model];
+
+            if ($model[1]) {
+                $filter = new $model[2](array(
+                    array('field' => $model[0]->getIdProperty(), 'operator' => 'equals', 'value' => $note->record_id)
+                ));
+                if ($model[0]->has('is_deleted')) {
+                    $filter->addFilter(new Tinebase_Model_Filter_Int(array('field' => 'is_deleted', 'operator' => 'notnull', 'value' => NULL)));
+                }
+                $result = $controller->searchCount($filter);
+
+                if (is_bool($result) || (is_string($result) && $result === ((string)intval($result)))) {
+                    $result = (int)$result;
+                }
+
+                if (!is_int($result)) {
+                    if (is_array($result) && isset($result['totalcount'])) {
+                        $result = (int)$result['totalcount'];
+                    } elseif(is_array($result) && isset($result['count'])) {
+                        $result = (int)$result['count'];
+                    } else {
+                        // todo log
+                        // dummy line, remove!
+                        $result = 1;
+                    }
+                }
+
+                if ($result === 0) {
+                    $deleteIds[] = $note->getId();
+                }
+            } else {
+                try {
+                    $controller->get($note->record_id, null, false, true);
+                } catch(Tinebase_Exception_NotFound $tenf) {
+                    $deleteIds[] = $note->getId();
+                }
+            }
+        }
+
+        if (count($deleteIds) > 0) {
+            $notesController->purgeNotes($deleteIds);
+        }
+
+        foreach($controllers as $model => $controller) {
+            $controller->doContainerACLChecks($models[$model][3]);
+        }
+    }
+
+    public function cleanCustomfields()
+    {
+        $customFieldController = Tinebase_CustomField::getInstance();
+        $customFieldConfigs = $customFieldController->searchConfig();
+
+        /** @var Tinebase_Model_CustomField_Config $customFieldConfig */
+        foreach($customFieldConfigs as $customFieldConfig) {
+            $controller = Tinebase_Core::getApplicationInstance($customFieldConfig->model);
+            $oldACLCheckValue = $controller->doContainerACLChecks(false);
+            $filterClass = $customFieldConfig->model . 'Filter';
+
+            $filter = new Tinebase_Model_CustomField_ValueFilter(array(
+                array('field' => 'customfield_id', 'operator' => 'equals', 'value' => $customFieldConfig->id)
+            ));
+            $customFieldValues = $customFieldController->search($filter);
+            $deleteIds = array();
+
+            if (class_exists($filterClass)) {
+                $model = new $customFieldConfig->model();
+                /** @var Tinebase_Model_CustomField_Value $customFieldValue */
+                foreach ($customFieldValues as $customFieldValue) {
+                    $filter = new $filterClass(array(
+                        array('field' => $model->getIdProperty(), 'operator' => 'equals', 'value' => $customFieldValue->record_id)
+                    ));
+                    if ($model->has('is_deleted')) {
+                        $filter->addFilter(new Tinebase_Model_Filter_Int(array('field' => 'is_deleted', 'operator' => 'notnull', 'value' => NULL)));
+                    }
+
+                    $result = $controller->searchCount($filter);
+
+                    if (is_bool($result) || (is_string($result) && $result === ((string)intval($result)))) {
+                        $result = (int)$result;
+                    }
+
+                    if (!is_int($result)) {
+                        if (is_array($result) && isset($result['totalcount'])) {
+                            $result = (int)$result['totalcount'];
+                        } elseif(is_array($result) && isset($result['count'])) {
+                            $result = (int)$result['count'];
+                        } else {
+                            // todo log
+                            // dummy line, remove!
+                            $result = 1;
+                        }
+                    }
+
+                    if ($result === 0) {
+                        $deleteIds[] = $customFieldValue->getId();
+                    }
+                }
+            } else {
+                /** @var Tinebase_Model_CustomField_Value $customFieldValue */
+                foreach ($customFieldValues as $customFieldValue) {
+                    try {
+                        $controller->get($customFieldValue->record_id, null, false, true);
+                    } catch(Tinebase_Exception_NotFound $tenf) {
+                        $deleteIds[] = $customFieldValue->getId();
+                    }
+                }
+            }
+
+            if (count($deleteIds) > 0) {
+                $customFieldController->deleteCustomFieldValue($deleteIds);
+            }
+
+            $controller->doContainerACLChecks($oldACLCheckValue);
+        }
+    }
     
     /**
      * get all app tables
index be848cf..a5c9222 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Record
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2007-2010 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schuele <p.schuele@metaways.de>
  */
 
  * 
  * @package     Tinebase
  * @subpackage  Record
+ *
+ * @property    string      id
+ * @property    string      application_id
+ * @property    string      model
+ * @property    string      name
+ * @property    string      definition
+ * @property    string      account_grants
  */
 class Tinebase_Model_CustomField_Config extends Tinebase_Record_Abstract 
 {
@@ -58,8 +65,9 @@ class Tinebase_Model_CustomField_Config extends Tinebase_Record_Abstract
     }
     
     /**
-     * (non-PHPdoc)
      * @see tine20/Tinebase/Record/Abstract.php::setFromArray()
+     * @param array $_data
+     * @return array
      */
     public function setFromArray(array $_data)
     {
index 5e47895..f8a2e81 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Record
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2009-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schuele <p.schuele@metaways.de>
  */
 
  * 
  * @package     Tinebase
  * @subpackage  Record
+ *
+ * @property    string      id
+ * @property    string      record_id
+ * @property    string      customfield_id
+ * @property    string      value
  */
 class Tinebase_Model_CustomField_Value extends Tinebase_Record_Abstract 
 {
index 2e3bb87..aa42c9e 100644 (file)
@@ -406,7 +406,9 @@ abstract class Tinebase_Model_Filter_Abstract
         $returnValue = str_replace(array('*', '_'),  $this->_dbCommand->setDatabaseJokerCharacters(), $value);
 
         // add wildcard to value according to operator
-        $returnValue = str_replace('?', $returnValue, $action['wildcards']);
+        if (isset($action['wildcards'])) {
+            $returnValue = str_replace('?', $returnValue, $action['wildcards']);
+        }
 
         return $returnValue;
     }
index 8431c94..9ac4062 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Notes
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2008-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schüle <p.schuele@metaways.de>
  */
 
  * 
  * @package     Tinebase
  * @subpackage  Notes
+ *
+ * @property    string      $id
+ * @property    string      $note_type_id
+ * @property    string      $note
+ * @property    string      $record_id
+ * @property    string      $record_model
+ * @property    string      $record_backend
  */
 class Tinebase_Model_Note extends Tinebase_Record_Abstract
 {
index c483440..3ba431c 100644 (file)
@@ -90,6 +90,10 @@ class Tinebase_Model_Tree_Node_Filter extends Tinebase_Model_Filter_FilterGroup
     // recursive search
         'recursive' => array(
             'filter' => 'Tinebase_Model_Filter_Bool'
-        )
+        ),
+        'tag'                  => array('filter' => 'Tinebase_Model_Filter_Tag', 'options' => array(
+            'idProperty' => 'tree_nodes.id',
+            'applicationName' => 'Tinebase',
+        )),
     );
 }
index 350e8c7..bbe0e04 100644 (file)
@@ -666,5 +666,43 @@ class Tinebase_Notes implements Tinebase_Backend_Sql_Interface
     {
         $this->_noteTypesTable->delete($this->_db->quoteInto($this->_db->quoteIdentifier('id') . ' = ?', $_noteTypeId));
     }
-    
+
+    /**
+     * get all Notes, including deleted ones, no ACL check
+     *
+     * @ param boolean $ignoreACL
+     * @ param boolean $getDeleted
+     * @return Tinebase_Record_RecordSet subtype Tinebase_Model_Note
+     */
+    public function getAllNotes()
+    {
+        $select = $this->_db->select()
+            ->from(array('notes' => SQL_TABLE_PREFIX . 'notes'))
+            //->where($this->_db->quoteIdentifier('is_deleted') . ' = 0')
+            ;
+
+        /*if (! $ignoreACL) {
+            $this->_checkFilterACL($_filter);
+        }*/
+
+        //Tinebase_Backend_Sql_Filter_FilterGroup::appendFilters($select, $_filter, $this);
+
+        $stmt = $this->_db->query($select);
+        $rows = $stmt->fetchAll(Zend_Db::FETCH_ASSOC);
+
+        $result = new Tinebase_Record_RecordSet('Tinebase_Model_Note', $rows, true);
+
+        return $result;
+    }
+
+    /**
+     * permanently delete notes by id
+     *
+     * @param array $_ids
+     * @return int
+     */
+    public function purgeNotes(array $_ids)
+    {
+        return $this->_db->delete(SQL_TABLE_PREFIX . 'notes', $this->_db->quoteInto('id IN (?)', $_ids));
+    }
 }
index b96d078..ad5fbc5 100644 (file)
@@ -522,6 +522,8 @@ abstract class Tinebase_Setup_DemoData_Abstract
         $this->_wednesday2week->addWeek(1);
         $this->_friday2week = clone $this->_nextFriday;
         $this->_friday2week->addWeek(1);
+
+
     }
 
     /**