Merge branch '2013.10' into 2014.11
authorPhilipp Schüle <p.schuele@metaways.de>
Thu, 6 Aug 2015 09:31:58 +0000 (11:31 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Thu, 6 Aug 2015 09:31:58 +0000 (11:31 +0200)
Conflicts:
tine20/Tinebase/User/Sql.php

Change-Id: I3b26f0f8bc33562cc078ecb31c91332985058b2c

1  2 
tests/tine20/TestCase.php
tine20/Setup/Frontend/Cli.php
tine20/Setup/Server/Cli.php
tine20/Tinebase/User.php
tine20/Tinebase/User/Sql.php

@@@ -106,8 -106,10 +106,10 @@@ abstract class TestCase extends PHPUnit
          }
          
          Addressbook_Controller_Contact::getInstance()->setGeoDataForContacts(true);
-         
-         Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser);
+         if ($this->_originalTestUser instanceof Tinebase_Model_User) {
+             Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser);
+         }
          
          Tinebase_Cache_PerRequest::getInstance()->resetCache();
      }
      }
      
      /**
 +     * get tag
 +     *
 +     * @param string $tagType
 +     * @param string $tagName
 +     * @param array $contexts
 +     * @return Tinebase_Model_Tag
 +     */
 +    protected function _getTag($tagType = Tinebase_Model_Tag::TYPE_SHARED, $tagName = NULL, $contexts = NULL)
 +    {
 +        if ($tagName) {
 +            try {
 +                $tag = Tinebase_Tags::getInstance()->getTagByName($tagName);
 +                return $tag;
 +            } catch (Tinebase_Exception_NotFound $tenf) {
 +            }
 +        } else {
 +            $tagName = Tinebase_Record_Abstract::generateUID();
 +        }
 +    
 +        $targ = array(
 +            'type'          => $tagType,
 +            'name'          => $tagName,
 +            'description'   => 'testTagDescription',
 +            'color'         => '#009B31',
 +        );
 +    
 +        if ($contexts) {
 +            $targ['contexts'] = $contexts;
 +        }
 +    
 +        return new Tinebase_Model_Tag($targ);
 +    }
 +    
 +    /**
       * delete groups and their members
       * 
       * - also deletes groups and users in sync backends
       */
      protected function _deleteUsers()
      {
          foreach ($this->_usernamesToDelete as $username) {
              try {
+                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+                     . ' Trying to delete user: ' . $username);
                  Tinebase_User::getInstance()->deleteUser(Tinebase_User::getInstance()->getUserByLoginName($username));
              } catch (Exception $e) {
                  if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
-                     . ' error while deleting user: ' . $e->getMessage());
+                     . ' Error while deleting user: ' . $e->getMessage());
              }
          }
      }
      protected function _getMailDomain()
      {
          $testconfig = Zend_Registry::get('testConfig');
 -        return ($testconfig && isset($testconfig->maildomain)) ? $testconfig->maildomain : 'tine20.org';
 +        
 +        if ($testconfig && isset($testconfig->maildomain)) {
 +            return $testconfig->maildomain;
 +        }
 +        
 +        if (!empty(Tinebase_Core::getUser()->accountEmailAddress)) {
 +            list($user, $domain) = explode('@', Tinebase_Core::getUser()->accountEmailAddress, 2);
 +            
 +            return $domain;
 +        }
 +        
 +        return 'tine20.org';
      }
      
      /**
          $tempFile = $tempFileBackend->createTempFile(dirname(__FILE__) . '/Filemanager/files/test.txt');
          return $tempFile;
      }
 +    
 +    /**
 +     * set grants for a persona
 +     * 
 +     * @param integer $containerId
 +     * @param string $persona
 +     * @param string $adminGrant
 +     */
 +    protected function _setPersonaGrantsForTestContainer($containerId, $persona, $adminGrant = false)
 +    {
 +        $grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(array(
 +            'account_id'    => $this->_personas[$persona]->getId(),
 +            'account_type'  => 'user',
 +            Tinebase_Model_Grants::GRANT_READ     => true,
 +            Tinebase_Model_Grants::GRANT_ADD      => true,
 +            Tinebase_Model_Grants::GRANT_EDIT     => true,
 +            Tinebase_Model_Grants::GRANT_DELETE   => true,
 +            Tinebase_Model_Grants::GRANT_ADMIN    => $adminGrant,
 +        ), array(
 +            'account_id'    => Tinebase_Core::getUser()->getId(),
 +            'account_type'  => 'user',
 +            Tinebase_Model_Grants::GRANT_READ     => true,
 +            Tinebase_Model_Grants::GRANT_ADD      => true,
 +            Tinebase_Model_Grants::GRANT_EDIT     => true,
 +            Tinebase_Model_Grants::GRANT_DELETE   => true,
 +            Tinebase_Model_Grants::GRANT_ADMIN    => true,
 +        )));
 +        
 +        Tinebase_Container::getInstance()->setGrants($containerId, $grants, TRUE);
 +    }
  }
@@@ -57,7 -57,7 +57,7 @@@ class Setup_Frontend_Cl
          } elseif(isset($_opts->uninstall)) {
              $this->_uninstall($_opts);
          } elseif(isset($_opts->list)) {
 -            $this->_listInstalled();
 +            $result = $this->_listInstalled();
          } elseif(isset($_opts->sync_accounts_from_ldap)) {
              $this->_importAccounts($_opts);
          } elseif(isset($_opts->sync_passwords_from_ldap)) {
              $this->_createAdminUser($_opts);
          } elseif(isset($_opts->getconfig)) {
              $this->_getConfig($_opts);
 +        } elseif(isset($_opts->reset_demodata)) {
 +            $this->_resetDemodata($_opts);
 +        } elseif(isset($_opts->updateAllImportExportDefinitions)) {
 +            $this->_updateAllImportExportDefinitions($_opts);
          }
          
          if ($exitAfterHandle) {
                  }
              }
              
 -            // initial password
 -            if (! isset($_options['adminPassword'])) {
 +            // initial password / can be empty => will trigger password change dialogue
 +            if (! array_key_exists('adminPassword', $_options)) {
                  $_options['adminPassword'] = $this->_promptPassword();
              }
          }
          }
          
          $controller->uninstallApplications($applications->name);
 -
 +        
          echo "Successfully uninstalled " . count($applications) . " applications.\n";
      }
 +    
 +    /**
 +     * reinstall applications
 +     * and reset Demodata
 +     * php setup.php --reset_demodata USERNAME
 +     * 
 +     * @param Zend_Console_Getopt $_opts
 +     */
 +    protected function _resetDemodata(Zend_Console_Getopt $_opts)
 +    {
 +        $controller = Setup_Controller::getInstance();
 +        $userController = Admin_Controller_User::getInstance();
 +        $containerController = Tinebase_Container::getInstance();
 +        $cli = new Tinebase_Frontend_Cli();
 +        
 +        //Don't reset this applications
 +        $fixedApplications = array('Tinebase', 'Admin', 'Addressbook');
 +        
 +        //Log in
 +        $opts = $_opts->getRemainingArgs();
 +        $username = $opts[0];
 +        if (empty($username)) {
 +            echo "Username is missing!\n";
 +            exit;
 +        }
 +        $user = Tinebase_User::getInstance()->getUserByLoginName($username);
 +        Tinebase_Core::set(Tinebase_Core::USER, $user);
 +        
 +        //get all applications and remove some
 +        $applications = Tinebase_Application::getInstance()->getApplications(NULL, 'id');
 +        
 +        foreach ($applications as $key => &$application) {
 +            if (in_array($application, $fixedApplications)) {
 +                unset($applications[$key]);
 +            }
 +        }
 +        
 +        //get set rights
 +        $users = Tinebase_Acl_Roles::getInstance()->getRoleByName('user role');
 +        $rights = Tinebase_Acl_Roles::getInstance()->getRoleRights($users->getId());
 +        
 +        //Uninstall Applications
 +        try {
 +            $controller->uninstallApplications($applications->name);
 +            echo "Successfully uninstalled " . count($applications) . " applications.\n";
 +        } catch (Tinebase_Exception_NotFound $e) {
 +        }
 +        //Install Applications
 +        try {
 +            $controller->installApplications($applications->name);
 +            echo "Successfully installed " . count($applications) . " applications.\n";
 +        } catch (Tinebase_Exception_NotFound $e) {
 +        }
 +        
 +        //set rights
 +        foreach ($applications as &$application) {
 +            $newApplicationId = Tinebase_Application::getInstance()->getApplicationByName($application->name)->getId();
 +            
 +            foreach ($rights as &$right) {
 +                if ($right['application_id'] == $application->id) {
 +                    $right['application_id'] = $newApplicationId;
 +                }
 +            }
 +            
 +        }
 +        
 +        Tinebase_Acl_Roles::getInstance()->setRoleRights($users->getId(), $rights);
 +        echo "Successfully restored user rights.\n";
 +        
 +        //Clean up addressbooks
 +        $internalContacts = $userController->getDefaultInternalAddressbook();
 +        $containers = $containerController->getAll();
 +        foreach ($containers as $key => &$container) {
 +            if ($container->id == $internalContacts) {
 +                // Do nothing
 +            } else {
 +                try {
 +                    $containerController->deleteContainer($container, true);
 +                } catch (Tinebase_Exception_NotFound $e) {
 +                }
 +            }
 +        }
 +        echo "Successfully cleand up containers.\n";
 +        
 +        //remove state
 +        $db = Tinebase_Core::getDb();
 +        $statement = "TRUNCATE TABLE " . $db->quoteIdentifier(SQL_TABLE_PREFIX . 'state');
 +        $db->query($statement);
 +        echo "Successfully truncated state table.\n";
 +        
 +        //Get Demodata
 +        $cli->createAllDemoData();
 +        
 +        //clear Cache
 +        Tinebase_Core::getCache()->clean(Zend_Cache::CLEANING_MODE_ALL);
 +        echo "Successfully cleared Cache.\n";
 +        
 +        echo "Every thing done!\n";
 +    }
  
      /**
 +     * Update Import Export Definitions for all applications
 +     */
 +    protected function _updateAllImportExportDefinitions(Zend_Console_Getopt $_opts){
 +        //get all applications
 +        $applications = Tinebase_Application::getInstance()->getApplications(NULL, 'id');
 +        foreach ($applications as $application) {
 +            Setup_Controller::getInstance()->createImportExportDefinitions($application);
 +            echo "Update definitions for " . $application->name . "...\n";
 +        }
 +    }
 +    
 +    /**
       * list installed apps
       */
      protected function _listInstalled()
              $applications = Tinebase_Application::getInstance()->getApplications(NULL, 'id');
          } catch (Zend_Db_Statement_Exception $e) {
              echo "No applications installed\n";
 -            return;
 +            return 1;
          }
          
          echo "Currently installed applications:\n";
          foreach($applications as $application) {
              echo "* $application\n";
          }
 +        
 +        return 0;
      }
      
      /**
                  new Tinebase_EmailUser_Smtp_LdapDbmailSchema()
              );
          }
+         if ($_opts->syncdeletedusers) {
+             $options['deleteUsers'] = true;
+         }
          Tinebase_User::syncUsers($options);
      }
      
              $errors[] = 'Missing argument: configvalue';
          }
          $configKey = (string)$options['configkey'];
 -        $configValue = (is_json($options['configvalue'])) ? Zend_Json::decode($options['configvalue']) : self::parseConfigValue($options['configvalue']);
 +        $configValue = self::parseConfigValue($options['configvalue']);
          $applicationName = (isset($options['app'])) ? $options['app'] : 'Tinebase';
          
          if (empty($errors)) {
      {
          if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__ . ' ' . print_r($_value, TRUE));
          
 +        // check value is json encoded
 +        if (Tinebase_Helper::is_json($_value)) {
 +            return Zend_Json::decode($_value); 
 +        }
 +        
          $result = array(
              'active' => 1
          );
  class Setup_Server_Cli implements Tinebase_Server_Interface
  {
      /**
 -     * handler for command line scripts
 -     * 
 -     * @return boolean
 +     * (non-PHPdoc)
 +     * @see Tinebase_Server_Interface::handle()
       */
 -    public function handle()
 +    public function handle(\Zend\Http\Request $request = null, $body = null)
      {
          try {
              $opts = new Zend_Console_Getopt(
                  'sync_accounts_from_ldap'   => 'Import user and groups from ldap',
                      'dbmailldap'            => 'Only usable with sync_accounts_from_ldap. Fetches dbmail email user data from LDAP.',
                      'onlyusers'             => 'Only usable with sync_accounts_from_ldap. Fetches only users and no groups from LDAP.',
+                     'syncdeletedusers'      => 'Only usable with sync_accounts_from_ldap. Removes users from Tine 2.0 DB that no longer exist in LDAP',
                  'sync_passwords_from_ldap'  => 'Synchronize user passwords from ldap',
                  'egw14import'               => 'Import user and groups from egw14
                           Examples: 
 -                          setup.php --egw14import /path/to/config.ini'
 +                          setup.php --egw14import /path/to/config.ini',
 +                'reset_demodata'            => 'reinstall applications and install Demodata (Needs Admin user)',
 +                'updateAllImportExportDefinitions' => 'update ImportExport definitions for all applications'
              ));
              $opts->parse();
          } catch (Zend_Console_Getopt_Exception $e) {
@@@ -70,8 -70,6 +71,8 @@@
              empty($opts->sync_passwords_from_ldap) && 
              empty($opts->egw14import) && 
              empty($opts->check_requirements) && 
 +            empty($opts->reset_demodata) &&
 +            empty($opts->updateAllImportExportDefinitions) &&
              empty($opts->create_admin) && 
              empty($opts->setconfig) && 
              empty($opts->getconfig))) 
diff --combined tine20/Tinebase/User.php
@@@ -484,8 -484,7 +484,8 @@@ class Tinebase_Use
                      self::createContactForSyncedUser($currentUser);
                  }
              }
 -        
 +            
 +            Tinebase_Timemachine_ModificationLog::setRecordMetaData($currentUser, 'update');
              $syncedUser = $userBackend->updateUserInSqlBackend($currentUser);
              if (! empty($user->container_id)) {
                  $syncedUser->container_id = $user->container_id;
              if ($user->visibility !== Tinebase_Model_FullUser::VISIBILITY_HIDDEN) {
                  self::createContactForSyncedUser($user);
              }
 +            Tinebase_Timemachine_ModificationLog::setRecordMetaData($user, 'create');
              $syncedUser = $userBackend->addUserInSqlBackend($user);
              $userBackend->addPluginUser($syncedUser, $user);
          }
              if (! $diff->isEmpty()) {
                  // add modlog info
                  Tinebase_Timemachine_ModificationLog::setRecordMetaData($contact, 'update');
 -                Tinebase_Container::getInstance()->increaseContentSequence($contact->container_id);
 +                if ($contact->container_id !== null) {
 +                    Tinebase_Container::getInstance()->increaseContentSequence($contact->container_id);
 +                }
                  
                  if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
                      . ' Updating contact data for user ' . $syncedUser->accountLoginName);
      public static function syncUsers($options)
      {
          if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ 
-             .' Start synchronizing users ...');
+             .' Start synchronizing users with options ' . print_r($options, true));
          
          $users = Tinebase_User::getInstance()->getUsersFromSyncBackend(NULL, NULL, 'ASC', NULL, NULL, 'Tinebase_Model_FullUser');
          
                      . $ten->getMessage());
              }
          }
+         if (isset($options['deleteUsers']) && $options['deleteUsers']) {
+             self::_syncDeletedUsers($users);
+         }
          
          // @todo this should be improved: only the cache of synced users + group memberships should be cleaned
          if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
      }
  
      /**
+      * deletes user in tine20 db that no longer exist in sync backend
+      *
+      * @param Tinebase_Record_RecordSet $usersInSyncBackend
+      */
+     protected static function _syncDeletedUsers(Tinebase_Record_RecordSet $usersInSyncBackend)
+     {
+         $userIdsInSqlBackend = Tinebase_User::getInstance()->getAllUserIdsFromSqlBackend();
+         $deletedInSyncBackend = array_diff($userIdsInSqlBackend, $usersInSyncBackend->getArrayOfIds());
+         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
+             . ' About to delete ' . count($deletedInSyncBackend) . ' users in SQL backend...');
+         foreach ($deletedInSyncBackend as $userToDelete) {
+             Tinebase_User::getInstance()->deleteUserInSqlBackend($userToDelete);
+         }
+     }
+     /**
       * get all user passwords from ldap
       * - set pw for user (in sql and sql plugins)
       * - do not encrypt the pw again as it is encrypted in LDAP
       *
       * @param array $_options [hash that may contain override values for admin user name and password]
       * @return void
+      * @throws Tinebase_Exception_InvalidArgument
       */
      public static function createInitialAccounts($_options)
      {
          // update or create user in local sql backend
          try {
              $userBackend->getUserByProperty('accountLoginName', $adminLoginName);
 +            Tinebase_Timemachine_ModificationLog::setRecordMetaData($user, 'update');
              $user = $userBackend->updateUserInSqlBackend($user);
          } catch (Tinebase_Exception_NotFound $ten) {
              // call addUser here to make sure, sql user plugins (email, ...) are triggered
 +            Tinebase_Timemachine_ModificationLog::setRecordMetaData($user, 'create');
              $user = $userBackend->addUser($user);
          }
          
          // set the password for the account
 -        Tinebase_User::getInstance()->setPassword($user, $adminPassword);
 +        // empty password triggers password change dialogue during first login
 +        if (!empty($adminPassword)) {
 +            Tinebase_User::getInstance()->setPassword($user, $adminPassword);
 +        }
  
          // add the admin account to all groups
          Tinebase_Group::getInstance()->addGroupMember($adminGroup, $user);
@@@ -216,8 -216,7 +216,8 @@@ class Tinebase_User_Sql extends Tinebas
              } catch (Tinebase_Exception_NotFound $tenf) {
                  // do nothing
              } catch (Exception $e) {
 -                if (Tinebase_Core::isLogLevel(Zend_Log::CRIT)) Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' User sql plugin failure: ' . $e);
 +                if (Tinebase_Core::isLogLevel(Zend_Log::CRIT)) Tinebase_Core::getLogger()->crit(__METHOD__ . '::' . __LINE__ . ' User sql plugin failure');
 +                Tinebase_Exception::log($e);
              }
          }
              
       * @return  Tinebase_Model_User the user object
       * @throws  Tinebase_Exception_NotFound
       * @throws  Tinebase_Exception_Record_Validation
 +     * @throws  Tinebase_Exception_InvalidArgument
       */
      public function getUserByPropertyFromSqlBackend($_property, $_value, $_accountClass = 'Tinebase_Model_User')
      {
          if ($row === false) {
              throw new Tinebase_Exception_NotFound('User with ' . $_property . ' = ' . $value . ' not found.');
          }
 -        
 +
          try {
              $account = new $_accountClass(NULL, TRUE);
              $account->setFromArray($row);
      protected function _getUserSelectObject()
      {
          /*
 -         * CASE WHEN `status` = 'enabled' THEN (CASE WHEN NOW() > `expires_at` THEN 'expired' 
 -         * WHEN (`login_failures` > 5 AND `last_login_failure_at` + INTERVAL 15 MINUTE > NOW()) 
 -         * THEN 'blocked' ELSE 'enabled' END) ELSE 'disabled' END
 +         * CASE WHEN `status` = 'enabled' THEN (CASE WHEN DATE(NOW()) > `expires_at` THEN 'expired'
 +         * WHEN ( `login_failures` > 5 AND DATE(`last_login_failure_at`) + INTERVAL '15' MINUTE > DATE(NOW())) THEN 'blocked'
 +         * ELSE 'enabled' END) WHEN `status` = 'expired' THEN 'expired' ELSE 'disabled' END
           */
          
          $maxLoginFailures = Tinebase_Config::getInstance()->get(Tinebase_Config::MAX_LOGIN_FAILURES, 5);
              $loginFailuresCondition = '';
          }
          
 -        $statusSQL = 'CASE WHEN ' . $this->_db->quoteIdentifier($this->rowNameMapping['accountStatus']) . ' = ' . $this->_db->quote('enabled') . ' THEN (';
 -        $statusSQL .= 'CASE WHEN '.$this->_dbCommand->setDate('NOW()') .' > ' . $this->_db->quoteIdentifier($this->rowNameMapping['accountExpires'])
 +        $statusSQL = 'CASE WHEN ' . $this->_db->quoteIdentifier($this->rowNameMapping['accountStatus']) . ' = ' . $this->_db->quote('enabled') . ' THEN ('
 +            . 'CASE WHEN '.$this->_dbCommand->setDate('NOW()') .' > ' . $this->_db->quoteIdentifier($this->rowNameMapping['accountExpires'])
              . ' THEN ' . $this->_db->quote('expired')
 -            . $loginFailuresCondition
 -            . ' ELSE ' . $this->_db->quote('enabled') . ' END) ELSE ' . $this->_db->quote('disabled') . ' END';
 +            . ' ' . $loginFailuresCondition
 +            . ' ELSE ' . $this->_db->quote('enabled') . ' END)'
 +            . ' WHEN ' . $this->_db->quoteIdentifier($this->rowNameMapping['accountStatus']) . ' = ' . $this->_db->quote('expired')
 +                . ' THEN ' . $this->_db->quote('expired')
 +            . ' ELSE ' . $this->_db->quote('disabled') . ' END';
          
          $select = $this->_db->select()
              ->from(SQL_TABLE_PREFIX . 'accounts', 
                      'loginFailures'         => $this->rowNameMapping['loginFailures'],
                      'contact_id',
                      'openid',
 -                    'visibility'
 +                    'visibility',
 +                    'created_by',
 +                    'creation_time',
 +                    'last_modified_by',
 +                    'last_modified_time',
 +                    'is_deleted',
 +                    'deleted_time',
 +                    'deleted_by',
 +                    'seq',
                  )
              )
              ->joinLeft(
                      'container_id'            => 'container_id'
                  )
              );
 -        
 +
          return $select;
      }
      
       *
       * @param mixed   $_accountId
       * @param string  $_status
 -     * @return unknown
 +     * @return integer
 +     * @throws Tinebase_Exception_InvalidArgument
       */
      public function setStatus($_accountId, $_status)
      {
                  break;
                  
              case 'expired':
 -                $accountData['expires_at'] = Tinebase_DateTime::getTimestamp();
 +                $accountData['expires_at'] = Tinebase_DateTime::now()->getTimestamp();
                  break;
              
              default:
       *
       * @param int $_accountId
       * @param string $_ipAddress
 -     * @return void
 +     * @return integer
       */
      public function setLoginTime($_accountId, $_ipAddress) 
      {
       * update contact data(first name, last name, ...) of user in local sql storage
       * 
       * @param Addressbook_Model_Contact $contact
 +     * @return integer
 +     * @throws Exception
       */
      public function updateContactInSqlBackend(Addressbook_Model_Contact $_contact)
      {
          $contactId = $_contact->getId();
  
 -        $accountsTable = new Tinebase_Db_Table(array('name' => SQL_TABLE_PREFIX . 'accounts'));
 -
          $accountData = array(
              $this->rowNameMapping['accountDisplayName']  => $_contact->n_fileas,
              $this->rowNameMapping['accountFullName']     => $_contact->n_fn,
              $where = array(
                  $this->_db->quoteInto($this->_db->quoteIdentifier('contact_id') . ' = ?', $contactId)
              );
 -            $accountsTable->update($accountData, $where);
 +            return $accountsTable->update($accountData, $where);
  
          } catch (Exception $e) {
              Tinebase_TransactionManager::getInstance()->rollBack();
  
          $oldUser = $this->getFullUserById($accountId);
          
 -        $accountsTable = new Tinebase_Db_Table(array('name' => SQL_TABLE_PREFIX . 'accounts'));
 -        
          if (empty($_user->contact_id)) {
              $_user->visibility = 'hidden';
              $_user->contact_id = null;
          }
 -        
 -        $accountData = array(
 -            'login_name'        => $_user->accountLoginName,
 -            'expires_at'        => ($_user->accountExpires instanceof DateTime ? $_user->accountExpires->get(Tinebase_Record_Abstract::ISO8601LONG) : NULL),
 -            'primary_group_id'  => $_user->accountPrimaryGroup,
 -            'home_dir'          => $_user->accountHomeDirectory,
 -            'login_shell'       => $_user->accountLoginShell,
 -            'openid'            => $_user->openid,
 -            'visibility'        => $_user->visibility,
 -            'contact_id'        => $_user->contact_id,
 -            $this->rowNameMapping['accountDisplayName']  => $_user->accountDisplayName,
 -            $this->rowNameMapping['accountFullName']     => $_user->accountFullName,
 -            $this->rowNameMapping['accountFirstName']    => $_user->accountFirstName,
 -            $this->rowNameMapping['accountLastName']     => $_user->accountLastName,
 -            $this->rowNameMapping['accountEmailAddress'] => $_user->accountEmailAddress,
 -        );
 +        $accountData = $this->_recordToRawData($_user);
 +        // don't update id
 +        unset($accountData['id']);
          
          // ignore all other states (expired and blocked)
          if ($_user->accountStatus == Tinebase_User::STATUS_ENABLED) {
              $_user->contact_id = null;
          }
          
 +        $accountData = $this->_recordToRawData($_user);
 +        
 +        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Adding user to SQL backend: ' . $_user->accountLoginName);
 +        
 +        $accountsTable->insert($accountData);
 +            
 +        return $this->getUserById($_user->getId(), 'Tinebase_Model_FullUser');
 +    }
 +    
 +    /**
 +     * converts record into raw data for adapter
 +     *
 +     * @param  Tinebase_Record_Abstract $_user
 +     * @return array
 +     */
 +    protected function _recordToRawData($_user)
 +    {
          $accountData = array(
              'id'                => $_user->accountId,
              'login_name'        => $_user->accountLoginName,
              'home_dir'          => $_user->accountHomeDirectory,
              'login_shell'       => $_user->accountLoginShell,
              'openid'            => $_user->openid,
 -            'visibility'        => $_user->visibility, 
 +            'visibility'        => $_user->visibility,
              'contact_id'        => $_user->contact_id,
              $this->rowNameMapping['accountDisplayName']  => $_user->accountDisplayName,
              $this->rowNameMapping['accountFullName']     => $_user->accountFullName,
              $this->rowNameMapping['accountFirstName']    => $_user->accountFirstName,
              $this->rowNameMapping['accountLastName']     => $_user->accountLastName,
              $this->rowNameMapping['accountEmailAddress'] => $_user->accountEmailAddress,
 +            'created_by'            => $_user->created_by,
 +            'creation_time'         => $_user->creation_time,
 +            'last_modified_by'      => $_user->last_modified_by,
 +            'last_modified_time'    => $_user->last_modified_time,
 +            'is_deleted'            => $_user->is_deleted,
 +            'deleted_time'          => $_user->deleted_time,
 +            'deleted_by'            => $_user->deleted_by,
 +            'seq'                   => $_user->seq,
          );
          
 -        if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Adding user to SQL backend: ' . $_user->accountLoginName);
 +        $unsetIfEmpty = array('seq', 'creation_time', 'created_by', 'last_modified_by', 'last_modified_time', 'is_deleted', 'deleted_time', 'deleted_by');
 +        foreach ($unsetIfEmpty as $property) {
 +            if (empty($accountData[$property])) {
 +                unset($accountData[$property]);
 +            }
 +        }
          
 -        $accountsTable->insert($accountData);
 -            
 -        return $this->getUserById($_user->getId(), 'Tinebase_Model_FullUser');
 +        return $accountData;
      }
      
      /**
              );
              $accountsTable->delete($where);
  
 -            $where  = array(
 -                $this->_db->quoteInto($this->_db->quoteIdentifier('login_name') . ' = ?', $user->accountLoginName),
 -            );
 -            
              Tinebase_TransactionManager::getInstance()->commitTransaction($transactionId);
          } catch (Exception $e) {
              if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' error while deleting account ' . $e->__toString());
      }
  
      /**
-         if (! isset($schema['creation_time'])) {
++<<<<<<< HEAD
 +     * send deactivation email to user
 +     * 
 +     * @param mixed $accountId
 +     */
 +    public function sendDeactivationNotification($accountId)
 +    {
 +        if (! Tinebase_Config::getInstance()->get(Tinebase_Config::ACCOUNT_DEACTIVATION_NOTIFICATION)) {
 +            if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
 +                __METHOD__ . '::' . __LINE__ . ' Deactivation notification disabled.');
 +            return;
 +        }
 +        
 +        try {
 +            $user = $this->getFullUserById($accountId);
 +            
 +            if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(
 +                __METHOD__ . '::' . __LINE__ . ' Send deactivation notification to user ' . $user->accountLoginName);
 +            
 +            $translate = Tinebase_Translation::getTranslation('Tinebase');
 +            
 +            $view = new Zend_View();
 +            $view->setScriptPath(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'views');
 +            
 +            $view->translate            = $translate;
 +            $view->accountLoginName     = $user->accountLoginName;
 +            // TODO add this?
 +            //$view->deactivationDate     = $user->deactivationDate;
 +            $view->tine20Url            = Tinebase_Core::getHostname();
 +            
 +            $messageBody = $view->render('deactivationNotification.php');
 +            $messageSubject = $translate->_('Your Tine 2.0 account has been deactivated');
 +            
 +            $recipient = Addressbook_Controller_Contact::getInstance()->getContactByUserId($user->getId(), /* $_ignoreACL = */ true);
 +            Tinebase_Notification::getInstance()->send(/* sender = */ null, array($recipient), $messageSubject, $messageBody);
 +            
 +        } catch (Exception $e) {
 +            Tinebase_Exception::log($e);
 +        }
 +    }
 +
 +
 +    /**
 +     * returns number of current non-system users
 +     *
 +     * @return number
 +     */
 +    public function countNonSystemUsers()
 +    {
 +        $select = $select = $this->_db->select()
 +            ->from(SQL_TABLE_PREFIX . 'accounts', 'COUNT(id)')
 +            ->where($this->_db->quoteIdentifier('login_name') . " not in ('cronuser', 'calendarscheduling')");
 +        $userCount = $this->_db->fetchOne($select);
 +        return $userCount;
 +    }
 +
 +    /**
 +     * fetch creation time of the first/oldest user
 +     *
 +     * @return Tinebase_DateTime
 +     */
 +    public function getFirstUserCreationTime()
 +    {
 +        $schema = Tinebase_Db_Table::getTableDescriptionFromCache($this->_db->table_prefix . $this->_tableName, $this->_db);
 +        $fallback = new Tinebase_DateTime('2014-12-01');
-         $result = (! empty($creationTime)) ? new Tinebase_DateTime($creationTime) : $fallback;
++        if (!isset($schema['creation_time'])) {
 +            return $fallback;
 +        }
 +
 +        $select = $select = $this->_db->select()
 +            ->from(SQL_TABLE_PREFIX . 'accounts', 'creation_time')
 +            ->where($this->_db->quoteIdentifier('login_name') . " not in ('cronuser', 'calendarscheduling')")
 +            ->where($this->_db->quoteIdentifier('creation_time') . " is not null")
 +            ->order('creation_time ASC')
 +            ->limit(1);
 +        $creationTime = $this->_db->fetchOne($select);
 +
++        $result = (!empty($creationTime)) ? new Tinebase_DateTime($creationTime) : $fallback;
 +        return $result;
 +    }
++
++    /**
+      * fetch all user ids from accounts table
+      *
+      * @return array
+      */
+     public function getAllUserIdsFromSqlBackend()
+     {
+         $sqlbackend = new Tinebase_Backend_Sql(array(
+             'modelName' => 'Tinebase_Model_FullUser',
+             'tableName' => $this->_tableName,
+         ));
+         $userIds = $sqlbackend->search(null, null, Tinebase_Backend_Sql_Abstract::IDCOL);
+         return $userIds;
+     }
  }