0011522: improve handling of group-lists
authorPaul Mehrer <p.mehrer@metaways.de>
Fri, 15 Jul 2016 12:17:40 +0000 (14:17 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Mon, 5 Sep 2016 14:49:51 +0000 (16:49 +0200)
Admin / Addressbook: fix group - list concept

* Addressbook_Controller_List
** adding or removing list members now checks manage
   accounts right if list is a grouplist
** changing list properties now checks manage accounts
   right if list is a grouplist and property is a group
   property too.
** for grouplists above changes are send to the group
   controller too, to change associated group too.
** _inspectBeforeCreate allows creation of lists of type
   group, checks for rights & proper group_id

* Addressbook_Controller_Contact
** _inspectBeforeUpdate checks if account data will be
   changed, if so, rights are checked

https://forge.tine20.org/view.php?id=11522

Change-Id: I56a4a1a9c4dd2963543bd33370cd754c904efbca
Reviewed-on: http://gerrit.tine20.com/customers/3363
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
tests/tine20/Addressbook/ListControllerTest.php
tine20/Addressbook/Backend/Ldap.php
tine20/Addressbook/Backend/List.php
tine20/Addressbook/Controller/Contact.php
tine20/Addressbook/Controller/List.php
tine20/Addressbook/Model/Contact.php
tine20/Addressbook/Model/List.php
tine20/Admin/Controller/Group.php
tine20/Tinebase/Backend/Sql/Abstract.php
tine20/Tinebase/Record/RecordSet.php

index 34b9185..78e8615 100644 (file)
@@ -4,19 +4,14 @@
  * 
  * @package     Addressbook
  * @license     http://www.gnu.org/licenses/agpl.html
- * @copyright   Copyright (c) 2010-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2010-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  */
 
 /**
- * Test helper
+ * Test class for Addressbook_Controller_List
  */
-require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php';
-
-/**
- * Test class for Tinebase_Group
- */
-class Addressbook_ListControllerTest extends PHPUnit_Framework_TestCase
+class Addressbook_ListControllerTest extends TestCase
 {
     /**
      * @var array test objects
@@ -24,25 +19,6 @@ class Addressbook_ListControllerTest extends PHPUnit_Framework_TestCase
     protected $objects = array();
 
     /**
-     * set geodata for contacts
-     * 
-     * @var boolean
-     */
-    protected $_geodata = FALSE;
-    
-    /**
-     * Runs the test methods of this class.
-     *
-     * @access public
-     * @static
-     */
-    public static function main()
-    {
-        $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Addressbook List Controller Tests');
-        PHPUnit_TextUI_TestRunner::run($suite);
-    }
-
-    /**
      * Sets up the fixture.
      * This method is called before a test is executed.
      *
@@ -50,9 +26,8 @@ class Addressbook_ListControllerTest extends PHPUnit_Framework_TestCase
      */
     protected function setUp()
     {
-        Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
-        $this->_geodata = Addressbook_Controller_Contact::getInstance()->setGeoDataForContacts($this->_geodata);
-        
+        parent::setUp();
+
         $personalContainer = Tinebase_Container::getInstance()->getPersonalContainer(
             Zend_Registry::get('currentAccount'), 
             'Addressbook', 
@@ -155,18 +130,6 @@ class Addressbook_ListControllerTest extends PHPUnit_Framework_TestCase
     }
 
     /**
-     * Tears down the fixture
-     * This method is called after a test is executed.
-     *
-     * @access protected
-     */
-    protected function tearDown()
-    {
-        Addressbook_Controller_Contact::getInstance()->setGeoDataForContacts($this->_geodata);
-        Tinebase_TransactionManager::getInstance()->rollBack();
-    }
-    
-    /**
      * try to add a list
      */
     public function testAddList()
@@ -267,4 +230,30 @@ class Addressbook_ListControllerTest extends PHPUnit_Framework_TestCase
         $userContact = Addressbook_Controller_Contact::getInstance()->getContactByUserId(Tinebase_Core::getUser()->getId());
         Addressbook_Controller_Contact::getInstance()->delete($userContact->getId());
     }
+
+    /**
+     * @see 0011522: improve handling of group-lists
+     */
+    public function testChangeListWithoutManageGrant()
+    {
+        // try to set memberships without MANAGE_ACCOUNTS
+        $this->_removeRoleRight('Admin', Admin_Acl_Rights::MANAGE_ACCOUNTS, true);
+
+        $listId = Tinebase_Group::getInstance()->getDefaultGroup()->list_id;
+        try {
+            Addressbook_Controller_List::getInstance()->addListMember($listId, array($this->objects['contact1']->getId()));
+            $this->fail('should not be possible to add list member to system group');
+        } catch (Tinebase_Exception_AccessDenied $tead) {
+            $this->assertEquals('No permission to add list member.', $tead->getMessage());
+        }
+
+        $list = Addressbook_Controller_List::getInstance()->get($listId);
+        $list->name = 'my new name';
+        try {
+            Addressbook_Controller_List::getInstance()->update($list);
+            $this->fail('should not be possible to set name of system group');
+        } catch (Tinebase_Exception_AccessDenied $tead) {
+            $this->assertEquals('You are not allowed to MANAGE_ACCOUNTS in application Admin !', $tead->getMessage());
+        }
+    }
 }
index 0ee1381..d61b2e2 100755 (executable)
@@ -14,7 +14,7 @@
 /**
  * contacts ldap backend
  * 
- * NOTE: LDAP charset is allways UTF-8 (RFC2253) so we don't have to cope with
+ * NOTE: LDAP charset is always UTF-8 (RFC2253) so we don't have to cope with
  *       charset conversions here ;-)
  * 
  * @package     Addressbook
index 5546354..bd722c5 100644 (file)
@@ -83,6 +83,9 @@ class Addressbook_Backend_List extends Tinebase_Backend_Sql_Abstract
     {
         parent::__construct($_dbAdapter, $_options);
 
+        /**
+         * TODO move this code somewhere and make it optionally. Maybe even make it a new controller / frontend action and request the data async
+         */
         if (Addressbook_Config::getInstance()->featureEnabled(Addressbook_Config::FEATURE_LIST_VIEW)) {
             $this->_additionalColumns['emails'] = new Zend_Db_Expr('(' .
                 $this->_db->select()
@@ -127,7 +130,7 @@ class Addressbook_Backend_List extends Tinebase_Backend_Sql_Abstract
             return $list;
         }
         
-        $newMembers = $this->_getIdsFromMixed($_newMembers);
+        $newMembers = Tinebase_Record_RecordSet::getIdsFromMixed($_newMembers);
         $idsToAdd   = array_diff($newMembers, $list->members);
         
         $listId     = Tinebase_Record_Abstract::convertId($_listId, $this->_modelName);
@@ -166,19 +169,19 @@ class Addressbook_Backend_List extends Tinebase_Backend_Sql_Abstract
      * remove members from list
      * 
      * @param  mixed  $_listId
-     * @param  mixed  $_newMembers
+     * @param  mixed  $_membersToRemove
      * @return Addressbook_Model_List
      */
-    public function removeListMember($_listId, $_oldMembers)
+    public function removeListMember($_listId, $_membersToRemove)
     {
         $list = $this->get($_listId);
         
-        if (empty($_oldMembers)) {
+        if (empty($_membersToRemove)) {
             return $list;
         }
         
-        $oldMembers  = $this->_getIdsFromMixed($_oldMembers);
-        $idsToRemove = array_intersect($list->members, $oldMembers);
+        $removeMembers  = Tinebase_Record_RecordSet::getIdsFromMixed($_membersToRemove);
+        $idsToRemove = array_intersect($list->members, $removeMembers);
         $listId      = Tinebase_Record_Abstract::convertId($_listId, $this->_modelName);
         
         $transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
index 57aeae0..40c2397 100644 (file)
@@ -260,7 +260,7 @@ class Addressbook_Controller_Contact extends Tinebase_Controller_Record_Abstract
         
         return $userProfile;
     }
-    
+
     /**
      * inspect update of one record (after update)
      *
@@ -347,6 +347,25 @@ class Addressbook_Controller_Contact extends Tinebase_Controller_Record_Abstract
         if (isset($_oldRecord->type) && $_oldRecord->type == Addressbook_Model_Contact::CONTACTTYPE_USER) {
             $_record->type = Addressbook_Model_Contact::CONTACTTYPE_USER;
         }
+
+        if (! empty($_record->account_id) || $_record->type == Addressbook_Model_Contact::CONTACTTYPE_USER) {
+
+            // first check if something changed that requires special rights
+            $changeAccount = false;
+            foreach (Addressbook_Model_Contact::getManageAccountFields() as $field) {
+                if ($_record->{$field} != $_oldRecord->{$field}) {
+                    $changeAccount = true;
+                    break;
+                }
+            }
+
+            // if so, check rights
+            if ($changeAccount) {
+                if (!Tinebase_Core::getUser()->hasRight('Admin', Admin_Acl_Rights::MANAGE_ACCOUNTS)) {
+                    throw new Tinebase_Exception_AccessDenied('No permission to change account properties.');
+                }
+            }
+        }
     }
     
     /**
index ac09ba6..acbb827 100644 (file)
@@ -175,24 +175,46 @@ class Addressbook_Controller_List extends Tinebase_Controller_Record_Abstract
      *
      * @param  mixed $_listId
      * @param  mixed $_newMembers
+     * @param  boolean $_addToGroup
      * @return Addressbook_Model_List
      */
-    public function addListMember($_listId, $_newMembers)
+    public function addListMember($_listId, $_newMembers, $_addToGroup = true)
     {
         try {
             $list = $this->get($_listId);
         } catch (Tinebase_Exception_AccessDenied $tead) {
-            $list = $this->_fixEmptyContainerId($_listId);
+            $this->_fixEmptyContainerId($_listId);
             $list = $this->get($_listId);
         }
 
         $this->_checkGrant($list, 'update', TRUE, 'No permission to add list member.');
+        $this->_checkGroupGrant($list, TRUE, 'No permission to add list member.');
 
         $list = $this->_backend->addListMember($_listId, $_newMembers);
 
+        if (true === $_addToGroup && ! empty($list->group_id)) {
+            foreach (Tinebase_Record_RecordSet::getIdsFromMixed($_newMembers) as $userId) {
+                Admin_Controller_Group::getInstance()->addGroupMember($list->group_id, $userId, false);
+            }
+        }
+
         return $this->get($list->getId());
     }
 
+    protected function _checkGroupGrant($_list, $_throw = false, $_msg = '')
+    {
+        if (! empty($_list->group_id)) {
+            if (!Tinebase_Core::getUser()->hasRight('Admin', Admin_Acl_Rights::MANAGE_ACCOUNTS)) {
+                if ($_throw) {
+                    throw new Tinebase_Exception_AccessDenied($_msg);
+                } else {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
     /**
      * fixes empty container ids / perhaps this can be removed later as all lists should have a container id!
      *
@@ -233,15 +255,24 @@ class Addressbook_Controller_List extends Tinebase_Controller_Record_Abstract
      * remove members from list
      *
      * @param  mixed $_listId
-     * @param  mixed $_newMembers
+     * @param  mixed $_removeMembers
+     * @param  boolean $_removeFromGroup
      * @return Addressbook_Model_List
      */
-    public function removeListMember($_listId, $_newMembers)
+    public function removeListMember($_listId, $_removeMembers, $_removeFromGroup = true)
     {
         $list = $this->get($_listId);
 
         $this->_checkGrant($list, 'update', TRUE, 'No permission to remove list member.');
-        $list = $this->_backend->removeListMember($_listId, $_newMembers);
+        $this->_checkGroupGrant($list, TRUE, 'No permission to remove list member.');
+
+        $list = $this->_backend->removeListMember($_listId, $_removeMembers);
+
+        if (true === $_removeFromGroup && ! empty($list->group_id)) {
+            foreach (Tinebase_Record_RecordSet::getIdsFromMixed($_removeMembers) as $userId) {
+                Admin_Controller_Group::getInstance()->removeGroupMember($list->group_id, $userId, false);
+            }
+        }
 
         return $this->get($list->getId());
     }
@@ -254,8 +285,16 @@ class Addressbook_Controller_List extends Tinebase_Controller_Record_Abstract
      */
     protected function _inspectBeforeCreate(Tinebase_Record_Interface $_record)
     {
-        if (isset($record->type) && $record->type == Addressbook_Model_List::LISTTYPE_GROUP) {
-            throw new Addressbook_Exception_InvalidArgument('can not add list of type ' . Addressbook_Model_List::LISTTYPE_GROUP);
+        if (isset($_record->type) && $_record->type == Addressbook_Model_List::LISTTYPE_GROUP) {
+            if (empty($_record->group_id)) {
+                throw Tinebase_Exception_UnexpectedValue('group_id is empty, must not happen for list type group');
+            }
+
+            // check rights
+            $this->_checkGroupGrant($_record, TRUE, 'can not add list of type ' . Addressbook_Model_List::LISTTYPE_GROUP);
+
+            // check if group is there, if not => not found exception
+            Admin_Controller_Group::getInstance()->get($_record->group_id);
         }
     }
 
@@ -280,8 +319,28 @@ class Addressbook_Controller_List extends Tinebase_Controller_Record_Abstract
      */
     protected function _inspectBeforeUpdate($_record, $_oldRecord)
     {
-        if (isset($record->type) && $record->type == Addressbook_Model_List::LISTTYPE_GROUP) {
-            throw new Addressbook_Exception_InvalidArgument('can not update list of type ' . Addressbook_Model_List::LISTTYPE_GROUP);
+        if (! empty($_record->group_id)) {
+
+            // first check if something changed that requires special rights
+            $changeGroup = false;
+            foreach (Addressbook_Model_List::getManageAccountFields() as $field) {
+                if ($_record->{$field} != $_oldRecord->{$field}) {
+                    $changeGroup = true;
+                    break;
+                }
+            }
+
+            // then do the update, the group controller will check manage accounts right
+            if ($changeGroup) {
+                $groupController = Admin_Controller_Group::getInstance();
+                $group = $groupController->get($_record->group_id);
+
+                foreach (Addressbook_Model_List::getManageAccountFields() as $field) {
+                    $group->{$field} = $_record->{$field};
+                }
+
+                $groupController->update($group, false);
+            }
         }
     }
 
index c9a6e80..dfd3c7b 100644 (file)
@@ -265,6 +265,19 @@ class Addressbook_Model_Contact extends Tinebase_Record_Abstract
      * @var array list of telephone country codes
      */
     protected static $countryCodes = array('+1','+7','+20','+27','+30','+31','+32','+33','+34','+36','+39','+40','+41','+43','+44','+45','+46','+47','+48','+49','+51','+52','+53','+54','+55','+56','+57','+58','+60','+61','+62','+63','+64','+65','+66','+76','+77','+81','+82','+84','+86','+90','+91','+92','+93','+94','+95','+98','+211','+212','+213','+216','+218','+220','+221','+222','+223','+224','+225','+226','+227','+228','+229','+230','+231','+232','+233','+234','+235','+236','+237','+238','+239','+240','+241','+242','+243','+244','+245','+246','+248','+249','+250','+251','+252','+253','+254','+255','+256','+257','+258','+260','+261','+262','+263','+264','+265','+266','+267','+268','+269','+291','+297','+298','+299','+350','+351','+352','+353','+354','+355','+356','+357','+358','+359','+370','+371','+372','+373','+374','+375','+376','+377','+378','+379','+380','+381','+382','+383','+385','+386','+387','+389','+420','+421','+423','+500','+501','+502','+503','+504','+505','+506','+507','+508','+509','+590','+591','+592','+593','+594','+595','+596','+597','+598','+670','+672','+673','+674','+675','+676','+677','+678','+679','+680','+681','+682','+683','+685','+686','+687','+688','+689','+690','+691','+692','+850','+852','+853','+855','+856','+880','+886','+960','+961','+962','+963','+964','+965','+966','+967','+968','+970','+971','+972','+973','+974','+975','+976','+977','+992','+993','+994','+995','+996','+998','+1242','+1246','+1264','+1268','+1284','+1340','+1345','+1441','+1473','+1649','+1664','+1670','+1671','+1684','+1721','+1758','+1767','+1784','+1787','+1809','+1829','+1849','+1868','+1869','+1876','+1939','+4779','+5999','+3906698');
+
+    /**
+     * name of fields which require manage accounts to be updated
+     *
+     * @var array list of fields which require manage accounts to be updated
+     */
+    protected static $_manageAccountsFields = array(
+        'email',
+        'n_fileas',
+        'n_fn',
+        'n_given',
+        'n_family',
+    );
     
     /**
     * overwrite constructor to add more filters
@@ -283,7 +296,15 @@ class Addressbook_Model_Contact extends Tinebase_Record_Abstract
     
         parent::__construct($_data, $_bypassFilters, $_convertDates);
     }
-    
+
+    /**
+     * @return array
+     */
+    static public function getManageAccountFields()
+    {
+        return self::$_manageAccountsFields;
+    }
+
     /**
      * returns prefered email address of given contact
      * 
index e07e63c..f5b5d60 100644 (file)
@@ -50,6 +50,17 @@ class Addressbook_Model_List extends Tinebase_Record_Abstract
      * @var string
      */
     const LISTTYPE_GROUP = 'group';
+
+    /**
+     * name of fields which require manage accounts to be updated
+     *
+     * @var array list of fields which require manage accounts to be updated
+     */
+    protected static $_manageAccountsFields = array(
+        'name',
+        'description',
+        'email',
+    );
     
     /**
      * list of zend validator
@@ -105,7 +116,15 @@ class Addressbook_Model_List extends Tinebase_Record_Abstract
         'last_modified_time',
         'deleted_time'
     );
-    
+
+    /**
+     * @return array
+     */
+    static public function getManageAccountFields()
+    {
+        return self::$_manageAccountsFields;
+    }
+
     /**
      * converts a string or Addressbook_Model_List to a list id
      *
index e31e913..5653c5f 100644 (file)
@@ -209,9 +209,10 @@ class Admin_Controller_Group extends Tinebase_Controller_Abstract
      * update existing group
      *
      * @param Tinebase_Model_Group $_group
+     * @param boolean $_updateList
      * @return Tinebase_Model_Group
      */
-    public function update(Tinebase_Model_Group $_group)
+    public function update(Tinebase_Model_Group $_group, $_updateList = true)
     {
         $this->checkRight('MANAGE_ACCOUNTS');
         
@@ -229,7 +230,7 @@ class Admin_Controller_Group extends Tinebase_Controller_Abstract
         
         $transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
         
-        if (Tinebase_Application::getInstance()->isInstalled('Addressbook') === true) {
+        if (true === $_updateList && Tinebase_Application::getInstance()->isInstalled('Addressbook') === true) {
             $_group->list_id = $oldGroup->list_id;
             $list = $this->createOrUpdateList($_group);
             $_group->list_id = $list->getId();
@@ -255,15 +256,16 @@ class Admin_Controller_Group extends Tinebase_Controller_Abstract
      *
      * @param int $_groupId
      * @param int $_accountId
+     * @param  boolean $_addToList
      * @return void
      */
-    public function addGroupMember($_groupId, $_userId)
+    public function addGroupMember($_groupId, $_userId, $_addToList = true)
     {
         $this->checkRight('MANAGE_ACCOUNTS');
         
         Tinebase_Group::getInstance()->addGroupMember($_groupId, $_userId);
         
-        if (Tinebase_Application::getInstance()->isInstalled('Addressbook') === true) {
+        if (true === $_addToList && Tinebase_Application::getInstance()->isInstalled('Addressbook') === true) {
             $group = $this->get($_groupId);
             $user  = Tinebase_User::getInstance()->getUserById($_userId);
             
@@ -273,7 +275,7 @@ class Admin_Controller_Group extends Tinebase_Controller_Abstract
                         . ' Could not add member to list ' . $group->list_id . ' (it does not exist)');
                 } else {
                     $aclChecking = Addressbook_Controller_List::getInstance()->doContainerACLChecks(FALSE);
-                    Addressbook_Controller_List::getInstance()->addListMember($group->list_id, $user->contact_id);
+                    Addressbook_Controller_List::getInstance()->addListMember($group->list_id, $user->contact_id, false);
                     Addressbook_Controller_List::getInstance()->doContainerACLChecks($aclChecking);
                 }
             }
@@ -290,22 +292,23 @@ class Admin_Controller_Group extends Tinebase_Controller_Abstract
      *
      * @param int $_groupId
      * @param int $_accountId
+     * @param  boolean $_removeFromList
      * @return void
      */
-    public function removeGroupMember($_groupId, $_userId)
+    public function removeGroupMember($_groupId, $_userId, $_removeFromList = true)
     {
         $this->checkRight('MANAGE_ACCOUNTS');
         
         Tinebase_Group::getInstance()->removeGroupMember($_groupId, $_userId);
         
-        if (Tinebase_Application::getInstance()->isInstalled('Addressbook') === true) {
+        if (true === $_removeFromList && Tinebase_Application::getInstance()->isInstalled('Addressbook') === true) {
             $group = $this->get($_groupId);
             $user  = Tinebase_User::getInstance()->getUserById($_userId);
             
             if (!empty($user->contact_id) && !empty($group->list_id)) {
                 try {
                     $aclChecking = Addressbook_Controller_List::getInstance()->doContainerACLChecks(FALSE);
-                    Addressbook_Controller_List::getInstance()->removeListMember($group->list_id, $user->contact_id);
+                    Addressbook_Controller_List::getInstance()->removeListMember($group->list_id, $user->contact_id, false);
                     Addressbook_Controller_List::getInstance()->doContainerACLChecks($aclChecking);
                 } catch (Tinebase_Exception_NotFound $tenf) {
                     if (Tinebase_Core::isLogLevel(Zend_Log::WARN)) 
index 5c33c9e..8073def 100644 (file)
@@ -1180,7 +1180,7 @@ abstract class Tinebase_Backend_Sql_Abstract extends Tinebase_Backend_Abstract i
                 $idsToRemove = array();
                 
                 if (!empty($_record->$modelName)) {
-                    $idsToAdd = $this->_getIdsFromMixed($_record->$modelName);
+                    $idsToAdd = Tinebase_Record_RecordSet::getIdsFromMixed($_record->$modelName);
                 }
                 
                 $transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
@@ -1224,33 +1224,6 @@ abstract class Tinebase_Backend_Sql_Abstract extends Tinebase_Backend_Abstract i
             }
         }
     }
-
-    /**
-     * convert recordset, array of ids or records to array of ids
-     * 
-     * @param  mixed  $_mixed
-     * @return array
-     */
-    protected function _getIdsFromMixed($_mixed)
-    {
-        if ($_mixed instanceof Tinebase_Record_RecordSet) { // Record set
-            $ids = $_mixed->getArrayOfIds();
-            
-        } elseif (is_array($_mixed)) { // array
-            foreach ($_mixed as $mixed) {
-                if ($mixed instanceof Tinebase_Record_Abstract) {
-                    $ids[] = $mixed->getId();
-                } else {
-                    $ids[] = $mixed;
-                }
-            }
-            
-        } else { // string
-            $ids[] = $_mixed instanceof Tinebase_Record_Abstract ? $_mixed->getId() : $_mixed;
-        }
-        
-        return $ids;
-    }
     
     /**
      * do something after creation of record
index d99b3d4..b77d3c0 100644 (file)
@@ -795,4 +795,31 @@ class Tinebase_Record_RecordSet implements IteratorAggregate, Countable, ArrayAc
             $record->translate();
         }
     }
+
+    /**
+     * convert recordset, array of ids or records to array of ids
+     *
+     * @param  mixed  $_mixed
+     * @return array
+     */
+    public static function getIdsFromMixed($_mixed)
+    {
+        if ($_mixed instanceof Tinebase_Record_RecordSet) { // Record set
+            $ids = $_mixed->getArrayOfIds();
+
+        } elseif (is_array($_mixed)) { // array
+            foreach ($_mixed as $mixed) {
+                if ($mixed instanceof Tinebase_Record_Abstract) {
+                    $ids[] = $mixed->getId();
+                } else {
+                    $ids[] = $mixed;
+                }
+            }
+
+        } else { // string
+            $ids[] = $_mixed instanceof Tinebase_Record_Abstract ? $_mixed->getId() : $_mixed;
+        }
+
+        return $ids;
+    }
 }