public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); $super = SecurityTestHelper::createSuperAdmin(); Yii::app()->user->userModel = User::getByUsername('super'); $headquarters = AccountTestHelper::createAccountByNameForOwner('Headquarters', $super); $division1 = AccountTestHelper::createAccountByNameForOwner('Division1', $super); $division2 = AccountTestHelper::createAccountByNameForOwner('Division2', $super); $ceo = ContactTestHelper::createContactWithAccountByNameForOwner('ceo', $super, $headquarters); $div1President = ContactTestHelper::createContactWithAccountByNameForOwner('div1 President', $super, $division1); $div2President = ContactTestHelper::createContactWithAccountByNameForOwner('div2 President', $super, $division2); $opportunity = OpportunityTestHelper::createOpportunityWithAccountByNameForOwner('big opp', $super, $headquarters); $opportunityDiv1 = OpportunityTestHelper::createOpportunityWithAccountByNameForOwner('div1 small opp', $super, $division1); $opportunityDiv2 = OpportunityTestHelper::createOpportunityWithAccountByNameForOwner('div2 small opp', $super, $division2); //attach divisions to headquarters $headquarters->accounts->add($division1); $headquarters->accounts->add($division2); assert($headquarters->save()); // Not Coding Standard //attach opportunities to contacts $opportunity->contacts->add($ceo); assert($opportunity->save()); // Not Coding Standard //Forget models to ensure relations are known on the next retrieval $headquarters->forget(); $division1->forget(); $division2->forget(); $ceo->forget(); }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); SecurityTestHelper::createSuperAdmin(); SecurityTestHelper::createUsers(); SecurityTestHelper::createGroups(); SecurityTestHelper::createRoles(); RedBeanModel::forgetAll(); //do the rebuild to ensure the tables get created properly. ReadPermissionsOptimizationUtil::rebuild(); //Manually build the test model munge tables. ReadPermissionsOptimizationUtil::recreateTable(ReadPermissionsOptimizationUtil::getMungeTableName('OwnedSecurableTestItem')); ReadPermissionsOptimizationUtil::recreateTable(ReadPermissionsOptimizationUtil::getMungeTableName('OwnedSecurableTestItem2')); $benny = User::getByUsername('benny'); $model = new OwnedSecurableTestItem(); $model->member = 'test'; assert($model->save()); // Not Coding Standard $model = new OwnedSecurableTestItem(); $model->member = 'test2'; assert($model->save()); // Not Coding Standard $model = new OwnedSecurableTestItem(); $model->member = 'test3'; $model->owner = $benny; assert($model->save()); // Not Coding Standard assert(OwnedSecurableTestItem::getCount() == 3); // Not Coding Standard $model = new OwnedSecurableTestItem2(); $model->member = 'test5'; assert($model->save()); // Not Coding Standard }
/** * At some point if performance is a problem with rebuilding activity models, then the stored procedure * needs to be refactored to somehow support more joins dynamically. * @see https://www.pivotaltracker.com/story/show/38804909 * @param boolean $forcePhp */ public static function rebuild($forcePhp = false) { //Forcing php way until we can fix failing tests here: AccountReadPermissionsOptimizationScenariosTest $forcePhp = true; assert('is_bool($forcePhp)'); foreach (self::getMungableModelClassNames() as $modelClassName) { if (!SECURITY_OPTIMIZED || $forcePhp) { self::rebuildViaSlowWay($modelClassName); } else { //models that extend activity are special and can only be done with the PHP process. They cannot //be done using the stored procedure because it does not support the extra joins needed to determine //which securable items to look at. if (is_subclass_of($modelClassName, 'Activity')) { self::rebuildViaSlowWay($modelClassName); } else { $modelTableName = RedBeanModel::getTableName($modelClassName); $mungeTableName = self::getMungeTableName($modelClassName); if (!is_subclass_of($modelClassName, 'OwnedSecurableItem')) { throw new NotImplementedException($message, $code, $previous); } if (is_subclass_of($modelClassName, 'Person')) { if ($modelClassName != 'Contact') { throw new NotSupportedException(); } else { $modelTableName = Person::getTableName('Person'); } } ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts("rebuild('{$modelTableName}', '{$mungeTableName}')"); } } } }
public static function forgetAll($forgetDbLevelCache = true) { if (static::supportsAndAllowsDatabaseCaching() && $forgetDbLevelCache) { ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts("clear_cache_actual_rights()"); } parent::forgetAll(); }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); Yii::app()->user->userModel = SecurityTestHelper::createSuperAdmin(); SecurityTestHelper::createUsers(); }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); SecurityTestHelper::createSuperAdmin(); Yii::app()->user->userModel = User::getByUsername('super'); $billy = UserTestHelper::createBasicUser('billy'); }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); SecurityTestHelper::createSuperAdmin(); UserTestHelper::createBasicUser('billy'); UserTestHelper::createBasicUser('sally'); }
/** * @return bool */ public function generateCampaignItemsForDueCampaigns() { $nowTimestamp = DateTimeUtil::convertTimestampToDbFormatDateTime(time()); // Begin Not Coding Standard $sql = "`generate_campaign_items`(" . Campaign::STATUS_ACTIVE . "," . Campaign::STATUS_PROCESSING . ",'{$nowTimestamp}')"; // End Not Coding Standard ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts($sql); Yii::app()->jobQueue->add('CampaignQueueMessagesInOutbox', 5); return true; }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); SecurityTestHelper::createSuperAdmin(); //do the rebuild to ensure the tables get created properly. AllPermissionsOptimizationUtil::rebuild(); //Manually build the test model munge tables. ReadPermissionsOptimizationUtil::recreateTable(ReadPermissionsOptimizationUtil::getMungeTableName('OwnedSecurableTestItem')); }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); SecurityTestHelper::createSuperAdmin(); Yii::app()->user->userModel = User::getByUsername('super'); $loaded = ContactsModule::loadStartingData(); assert('$loaded'); // Not Coding Standard }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); SecurityTestHelper::createSuperAdmin(); Yii::app()->user->userModel = User::getByUsername('super'); SecurityTestHelper::createUsers(); SecurityTestHelper::createGroups(); SecurityTestHelper::createAccounts(); AllPermissionsOptimizationUtil::rebuild(); }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); SecurityTestHelper::createSuperAdmin(); SecurityTestHelper::createUsers(); SecurityTestHelper::createGroups(); SecurityTestHelper::createRoles(); //Forget the cache, otherwise user/role/group information is not properly reflected in the cache. RedBeanModel::forgetAll(); }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); SecurityTestHelper::createSuperAdmin(); $billy = UserTestHelper::createBasicUser('billy'); $group = Group::getByName('Super Administrators'); $group->users->add($billy); $saved = $group->save(); assert($saved); // Not Coding Standard UserTestHelper::createBasicUser('sally'); }
/** * At some point if performance is a problem with rebuilding activity models, then the stored procedure * needs to be refactored to somehow support more joins dynamically. * @see https://www.pivotaltracker.com/story/show/38804909 * @param boolean $overwriteExistingTables * @param boolean $forcePhp */ public static function rebuild($overwriteExistingTables = true, $forcePhp = false, $messageStreamer = null) { //Forcing php way until we can fix failing tests here: AccountReadPermissionsOptimizationScenariosTest $forcePhp = true; assert('is_bool($overwriteExistingTables)'); assert('is_bool($forcePhp)'); foreach (PathUtil::getAllMungableModelClassNames() as $modelClassName) { $mungeTableName = self::getMungeTableName($modelClassName); $readTableExists = ZurmoRedBean::$writer->doesTableExist($mungeTableName); if (!$overwriteExistingTables && $readTableExists) { if (isset($messageStreamer)) { $messageStreamer->add(Zurmo::t('ZurmoModule', "Skipping {{tableName}}", array('{{tableName}}' => $mungeTableName))); } // skip if we don't want to overwrite existing tables and table already exists continue; } if (isset($messageStreamer)) { $messageStreamer->add(Zurmo::t('ZurmoModule', "Building {{tableName}}", array('{{tableName}}' => $mungeTableName))); } if (!SECURITY_OPTIMIZED || $forcePhp) { self::rebuildViaSlowWay($modelClassName); } else { //models that extend activity are special and can only be done with the PHP process. They cannot //be done using the stored procedure because it does not support the extra joins needed to determine //which securable items to look at. if (is_subclass_of($modelClassName, 'Activity')) { self::rebuildViaSlowWay($modelClassName); } else { $modelTableName = $modelClassName::getTableName(); if (!is_subclass_of($modelClassName, 'OwnedSecurableItem')) { throw new NotImplementedException(); } if (is_subclass_of($modelClassName, 'Person')) { if ($modelClassName != 'Contact') { throw new NotSupportedException(); } else { $modelTableName = Person::getTableName(); } } ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts("rebuild('{$modelTableName}', '{$mungeTableName}')"); } } } }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::createActualPermissionsCacheTable(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); SecurityTestHelper::createSuperAdmin(); $super = User::getByUsername('super'); Yii::app()->user->userModel = $super; StarredUtil::createStarredTables(); $account = new Account(); $account->owner = $super; $account->name = 'Test Account0'; $account->officePhone = '1234567890'; $account->save(); StarredUtil::markModelAsStarred($account); $account = new Account(); $account->owner = $super; $account->name = 'Test Account1'; $account->officePhone = '1234567891'; $account->save(); }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::createActualPermissionsCacheTable(); ZurmoDatabaseCompatibilityUtil::createNamedSecurableActualPermissionsCacheTable(); ZurmoDatabaseCompatibilityUtil::createActualRightsCacheTable(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); PermissionsCache::forgetAll(); AllPermissionsOptimizationCache::forgetAll(); RightsCache::forgetAll(); PoliciesCache::forgetAll(); Currency::resetCaches(); //php only cache Permission::resetCaches(); //php only cache self::$activitiesObserver = new ActivitiesObserver(); self::$activitiesObserver->init(); //runs init(); self::$conversationsObserver = new ConversationsObserver(); self::$conversationsObserver->init(); //runs init(); self::$emailMessagesObserver = new EmailMessagesObserver(); self::$emailMessagesObserver->init(); //runs init(); self::$contactLatestActivityDateTimeObserver = new ContactLatestActivityDateTimeObserver(); self::$contactLatestActivityDateTimeObserver->init(); //runs init(); self::$accountLatestActivityDateTimeObserver = new AccountLatestActivityDateTimeObserver(); self::$accountLatestActivityDateTimeObserver->init(); //runs init(); self::$accountContactAffiliationObserver = new AccountContactAffiliationObserver(); self::$accountContactAffiliationObserver->init(); //runs init(); Yii::app()->gameHelper; Yii::app()->gamificationObserver; //runs init(); Yii::app()->gameHelper->resetDeferredPointTypesAndValuesByUserIdToAdd(); Yii::app()->emailHelper->sendEmailThroughTransport = false; Yii::app()->jobQueue->deleteAll(); }
public static function setUpBeforeClass() { parent::setUpBeforeClass(); ZurmoDatabaseCompatibilityUtil::createActualPermissionsCacheTable(); ZurmoDatabaseCompatibilityUtil::dropStoredFunctionsAndProcedures(); PermissionsCache::forgetAll(); RightsCache::forgetAll(); PoliciesCache::forgetAll(); Currency::resetCaches(); //php only cache $activitiesObserver = new ActivitiesObserver(); $activitiesObserver->init(); //runs init(); $conversationsObserver = new ConversationsObserver(); $conversationsObserver->init(); //runs init(); Yii::app()->gameHelper; Yii::app()->gamificationObserver; //runs init(); Yii::app()->gameHelper->resetDeferredPointTypesAndValuesByUserIdToAdd(); Yii::app()->emailHelper->sendEmailThroughTransport = false; }
/** * Auto builds the database. Must manually set AuditEvent first to avoid issues building the AuditEvent * table. This is because AuditEvent is specially optimized during this build process to reduce how * long this takes to do. */ public static function autoBuildDatabase(&$messageLogger) { $rootModels = array(); $rootModels[] = 'AuditEvent'; foreach (Module::getModuleObjects() as $module) { $moduleAndDependenciesRootModelNames = $module->getRootModelNamesIncludingDependencies(); $rootModels = array_merge($rootModels, array_diff($moduleAndDependenciesRootModelNames, $rootModels)); } ZurmoDatabaseCompatibilityUtil::createStoredFunctionsAndProcedures(); RedBeanDatabaseBuilderUtil::autoBuildModels($rootModels, $messageLogger); ZurmoDatabaseCompatibilityUtil::createIndexes(); }
public function getInheritedActualRight($moduleName, $rightName) { assert('is_string($moduleName)'); assert('is_string($rightName)'); assert('$moduleName != ""'); assert('$rightName != ""'); if ($this->isEveryone) { return Right::NONE; } if (!SECURITY_OPTIMIZED) { return parent::getInheritedActualRight($moduleName, $rightName); } else { // Optimizations work on the database, // anything not saved will not work. assert('$this->id > 0'); return intval(ZurmoDatabaseCompatibilityUtil::callFunction("get_group_inherited_actual_right({$this->id}, '{$moduleName}', '{$rightName}')")); } }
/** * @param string $moduleName * @param string $policyName * @return mixed|null|string */ public function getExplicitActualPolicy($moduleName, $policyName) { assert('is_string($moduleName)'); assert('is_string($policyName)'); assert('$moduleName != ""'); assert('$policyName != ""'); if (!SECURITY_OPTIMIZED) { // The slow way will remain here as documentation // for what the optimized way is doing. foreach ($this->policies as $policy) { if ($policy->moduleName == $moduleName && $policy->name == $policyName) { return $policy->value; } } return null; } else { $permitableId = $this->getClassId('Permitable'); try { // not using $default because computing default value would involve extra cpu cycles each time. return PoliciesCache::getEntry($permitableId . $moduleName . $policyName . 'ExplicitActualPolicy'); } catch (NotFoundException $e) { $explictActualPolicy = ZurmoDatabaseCompatibilityUtil::callFunction("get_permitable_explicit_actual_policy(\n {$permitableId}, '{$moduleName}', '{$policyName}')"); } PoliciesCache::cacheEntry($permitableId . $moduleName . $policyName . 'ExplicitActualPolicy', $explictActualPolicy); return $explictActualPolicy; } }
public static function autoBuildDatabase(&$messageLogger, $autoBuildTestModels = false) { ZurmoDatabaseCompatibilityUtil::createStoredFunctionsAndProcedures(); ZurmoDatabaseCompatibilityUtil::createActualPermissionsCacheTable(); ZurmoDatabaseCompatibilityUtil::createNamedSecurableActualPermissionsCacheTable(); ZurmoDatabaseCompatibilityUtil::createActualRightsCacheTable(); $messageLogger->addInfoMessage(Zurmo::t('InstallModule', 'Searching for models')); $rootModels = PathUtil::getAllCanHaveBeanModelClassNames(); $messageLogger->addInfoMessage(Zurmo::t('InstallModule', 'Models catalog built.')); RedBeanModelsToTablesAdapter::generateTablesFromModelClassNames($rootModels, $messageLogger); ReadPermissionsSubscriptionUtil::buildTables(); if ($autoBuildTestModels) { TestSuite::buildDependentTestModels($messageLogger); } }
protected static function fastContainsUserByGroupName($groupName, $userId) { // Optimizations work on the database, // anything not saved will not work. assert('$userId > 0'); // Not Coding Standard assert('is_string($groupName) && $groupName != ""'); // Not Coding Standard assert('is_int($userId) && $userId > 0'); // Not Coding Standard return intval(ZurmoDatabaseCompatibilityUtil::callFunction("named_group_contains_user('{$groupName}', {$userId})")) == 1; }
public function testPermissionsCachingBasics() { if (!SECURITY_OPTIMIZED) { return; } $accounts = Account::getAll(); $account = $accounts[0]; $user = User::getByUsername('bobby'); $this->assertNotEquals($account->owner->id, $user->id); $account->addPermissions($user, Permission::READ); $this->assertTrue($account->save()); $securableItemId = $account->getClassId('SecurableItem'); $permitableId = $user->getClassId('Permitable'); R::exec("call get_securableitem_cached_actual_permissions_for_permitable({$securableItemId}, {$permitableId}, @allow_permissions, @deny_permissions)"); $allow_permissions = intval(R::getCell('select @allow_permissions')); $deny_permissions = intval(R::getCell('select @deny_permissions')); $this->assertEquals(Permission::NONE, $allow_permissions); $this->assertEquals(Permission::NONE, $deny_permissions); ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts("cache_securableitem_actual_permissions_for_permitable({$securableItemId}, {$permitableId}, 1, 0)"); R::exec("call get_securableitem_cached_actual_permissions_for_permitable({$securableItemId}, {$permitableId}, @allow_permissions, @deny_permissions)"); $allow_permissions = intval(R::getCell('select @allow_permissions')); $deny_permissions = intval(R::getCell('select @deny_permissions')); $this->assertEquals(Permission::READ, $allow_permissions); $this->assertEquals(Permission::NONE, $deny_permissions); ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts("clear_cache_securableitem_actual_permissions({$securableItemId})"); R::exec("call get_securableitem_cached_actual_permissions_for_permitable({$securableItemId}, {$permitableId}, @allow_permissions, @deny_permissions)"); $allow_permissions = intval(R::getCell('select @allow_permissions')); $deny_permissions = intval(R::getCell('select @deny_permissions')); $this->assertEquals(Permission::NONE, $allow_permissions); $this->assertEquals(Permission::NONE, $deny_permissions); $account->removeAllPermissions(); $this->assertTrue($account->save()); $this->assertEquals(Permission::NONE, $account->getEffectivePermissions($user)); $this->assertEquals(array(Permission::NONE, Permission::NONE), $account->getExplicitActualPermissions($user)); $this->assertEquals(array(Permission::NONE, Permission::NONE), $account->getInheritedActualPermissions($user)); }
public static function forgetAll($forgetDbLevelCache = true) { if (static::supportsAndAllowsPhpCaching()) { static::$securableItemToPermitableToCombinedPermissions = array(); static::$namedSecurableItemActualPermissions = array(); } if (SECURITY_OPTIMIZED && static::supportsAndAllowsDatabaseCaching() && $forgetDbLevelCache) { ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts("clear_cache_all_actual_permissions()"); } if (static::supportsAndAllowsDatabaseCaching() && $forgetDbLevelCache) { ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts("clear_cache_named_securable_all_actual_permissions()"); } static::clearMemcacheCache(); }
public function getActualPermissions($permitable = null) { assert('$permitable === null || $permitable instanceof Permitable'); if ($permitable === null) { $permitable = Yii::app()->user->userModel; if (!$permitable instanceof User) { throw new NoCurrentUserSecurityException(); } } if (!SECURITY_OPTIMIZED) { // The slow way will remain here as documentation // for what the optimized way is doing. $allowPermissions = Permission::NONE; $denyPermissions = Permission::NONE; if (Group::getByName(Group::SUPER_ADMINISTRATORS_GROUP_NAME)->contains($permitable)) { $allowPermissions = Permission::ALL; } else { foreach ($this->unrestrictedGet('permissions') as $permission) { $effectivePermissions = $permission->getEffectivePermissions($permitable); if ($permission->type == Permission::ALLOW) { $allowPermissions |= $effectivePermissions; } else { $denyPermissions |= $effectivePermissions; } } $allowPermissions |= $this->getPropagatedActualAllowPermissions($permitable); if (!$this instanceof NamedSecurableItem) { foreach (array(get_class($this), static::getModuleClassName()) as $securableItemName) { try { $securableType = NamedSecurableItem::getByName($securableItemName); $typeAllowPermissions = Permission::NONE; $typeDenyPermissions = Permission::NONE; foreach ($securableType->unrestrictedGet('permissions') as $permission) { $effectivePermissions = $permission->getEffectivePermissions($permitable); if ($permission->type == Permission::ALLOW) { $typeAllowPermissions |= $effectivePermissions; } else { $typeDenyPermissions |= $effectivePermissions; } // We shouldn't see something that isn't owned having CHANGE_OWNER. // assert('$typeAllowPermissions & Permission::CHANGE_OWNER == Permission::NONE'); } $allowPermissions |= $typeAllowPermissions; $denyPermissions |= $typeDenyPermissions; } catch (NotFoundException $e) { } } } } } else { try { $combinedPermissions = PermissionsCache::getCombinedPermissions($this, $permitable); } catch (NotFoundException $e) { $securableItemId = $this->getClassId('SecurableItem'); $permitableId = $permitable->getClassId('Permitable'); // Optimizations work on the database, // anything not saved will not work. assert('$permitableId > 0'); $className = get_class($this); $moduleName = static::getModuleClassName(); $cachingOn = DB_CACHING_ON ? 1 : 0; $combinedPermissions = intval(ZurmoDatabaseCompatibilityUtil::callFunction("get_securableitem_actual_permissions_for_permitable({$securableItemId}, {$permitableId}, '{$className}', '{$moduleName}', {$cachingOn})")); PermissionsCache::cacheCombinedPermissions($this, $permitable, $combinedPermissions); } $allowPermissions = $combinedPermissions >> 8 & Permission::ALL; $denyPermissions = $combinedPermissions & Permission::ALL; } assert("({$allowPermissions} & ~Permission::ALL) == 0"); assert("({$denyPermissions} & ~Permission::ALL) == 0"); return array($allowPermissions, $denyPermissions); }
public static function forgetAll($forgetDbLevelCache = true) { if (PHP_CACHING_ON) { self::$securableItemToPermitableToCombinedPermissions = array(); self::$namedSecurableItemActualPermissions = array(); } if (SECURITY_OPTIMIZED && DB_CACHING_ON && $forgetDbLevelCache) { ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts("clear_cache_all_actual_permissions()"); } if (MEMCACHE_ON && Yii::app()->cache !== null) { self::incrementCacheIncrementValue(static::$cacheType); } }
/** * Updates the email message using stored procedure * @param EmailMessage $emailMessage */ protected function updateEmailMessageForSending(EmailMessage $emailMessage, $useSQL = false) { if (!$useSQL) { Yii::log("EmailMessage should have been saved by this point. Anyways, saving now", CLogger::LEVEL_INFO); // we save it and return. No need to call SP as the message is saved already; $emailMessage->save(false); return; } $nowTimestamp = "'" . DateTimeUtil::convertTimestampToDbFormatDateTime(time()) . "'"; $sendAttempts = $emailMessage->sendAttempts ? $emailMessage->sendAttempts : 1; $sentDateTime = $emailMessage->sentDateTime ? "'" . $emailMessage->sentDateTime . "'" : 'null'; $serializedData = $emailMessage->error->serializedData ? "'" . $emailMessage->error->serializedData . "'" : 'null'; $sql = '`update_email_message_for_sending`( ' . $emailMessage->id . ', ' . $sendAttempts . ', ' . $sentDateTime . ', ' . $emailMessage->folder->id . ', ' . $serializedData . ', ' . $nowTimestamp . ')'; ZurmoDatabaseCompatibilityUtil::callProcedureWithoutOuts($sql); $emailMessage->forget(); }