Log an operation into the log table.
public static insert ( string $Operation, string $RecordType, array $Data, array $Options = [] ) : integer | false | ||
$Operation | string | The operation being performed. This is usually one of: - Delete: The record has been deleted. - Edit: The record has been edited. - Spam: The record has been marked spam. - Moderate: The record requires moderation. - Pending: The record needs pre-moderation. |
$RecordType | string | The type of record being logged. This usually correspond to the tablename of the record. |
$Data | array | The record data. - If you are logging just one row then pass the row as an array. - You can pass an additional _New element to tell the logger what the new data is. |
$Options | array | Additional options to affect the insert. |
return | integer | false | The log ID or **false** if there was a problem. |
public static function _($message, $channel = Ajde_Log::CHANNEL_INFO, $level = Ajde_Log::LEVEL_INFORMATIONAL, $description = '', $code = '', $trace = '') { // don't use db writer on db error if (substr_count($message, 'SQLSTATE')) { return false; } $log = new LogModel(); $log->populate(['message' => $message, 'channel' => $channel, 'level' => $level, 'description' => $description, 'code' => $code, 'trace' => $trace, 'request' => self::getRequest(), 'user_agent' => self::getUserAgent(), 'referer' => self::getReferer(), 'ip' => self::getIP()]); return $log->insert(); }
/** * 添加 * */ public function addAction() { // 实例化Model $log = new LogModel(); // 处理post数据 if ($this->getRequest()->isPost()) { // 获取所有post数据 $pdata = $this->getAllPost(); // 处理图片等特殊数据 // 验证 $result = $log->validation->validate($pdata, 'add'); $log->parseAttributes($pdata); // 通过验证 if ($result) { // 入库前数据处理 // Model转换成数组 $data = $log->toArray($pdata); $result = $log->insert($data); if ($result) { // 提示信息并跳转到列表 Tools_help::setSession('Message', '添加成功!'); $this->redirect('/backend/log/index'); } else { // 验证失败 $this->_view->assign('ErrorMessage', '添加失败!'); $this->_view->assign("errors", $log->validation->getErrorSummary()); } } else { // 验证失败 $this->_view->assign('ErrorMessage', '添加失败!'); $this->_view->assign("errors", $log->validation->getErrorSummary()); } } // 格式化表单数据 // 模版分配数据 $this->_view->assign("log", $log); $this->_view->assign("pageTitle", '添加'); }
/** * Delete a comment. * * This is a hard delete that completely removes it from the database. * Events: DeleteComment, BeforeDeleteComment. * * @since 2.0.0 * @access public * * @param int $CommentID Unique ID of the comment to be deleted. * @param array $Options Additional options for the delete. * @param bool Always returns TRUE. */ public function delete($CommentID, $Options = array()) { $this->EventArguments['CommentID'] = $CommentID; $Comment = $this->getID($CommentID, DATASET_TYPE_ARRAY); if (!$Comment) { return false; } $Discussion = $this->SQL->getWhere('Discussion', array('DiscussionID' => $Comment['DiscussionID']))->firstRow(DATASET_TYPE_ARRAY); // Decrement the UserDiscussion comment count if the user has seen this comment $Offset = $this->GetOffset($CommentID); $this->SQL->update('UserDiscussion')->set('CountComments', 'CountComments - 1', false)->where('DiscussionID', $Comment['DiscussionID'])->where('CountComments >', $Offset)->put(); $this->EventArguments['Discussion'] = $Discussion; $this->fireEvent('DeleteComment'); $this->fireEvent('BeforeDeleteComment'); // Log the deletion. $Log = val('Log', $Options, 'Delete'); LogModel::insert($Log, 'Comment', $Comment, val('LogOptions', $Options, array())); // Delete the comment. $this->SQL->delete('Comment', array('CommentID' => $CommentID)); // Update the comment count $this->UpdateCommentCount($Discussion, array('Slave' => false)); // Update the user's comment count $this->UpdateUser($Comment['InsertUserID']); // Update the category. $Category = CategoryModel::categories(val('CategoryID', $Discussion)); if ($Category && $Category['LastCommentID'] == $CommentID) { $CategoryModel = new CategoryModel(); $CategoryModel->SetRecentPost($Category['CategoryID']); } // Clear the page cache. $this->RemovePageCache($Comment['DiscussionID']); return true; }
/** * Delete a discussion. Update and/or delete all related data. * * Events: DeleteDiscussion. * * @since 2.0.0 * @access public * * @param int $DiscussionID Unique ID of discussion to delete. * @return bool Always returns TRUE. */ public function delete($DiscussionID, $Options = array()) { // Retrieve the users who have bookmarked this discussion. $BookmarkData = $this->GetBookmarkUsers($DiscussionID); $Data = $this->SQL->select('*')->from('Discussion')->where('DiscussionID', $DiscussionID)->get()->firstRow(DATASET_TYPE_ARRAY); $UserID = false; $CategoryID = false; if ($Data) { $UserID = $Data['InsertUserID']; $CategoryID = $Data['CategoryID']; } // Prep and fire event $this->EventArguments['DiscussionID'] = $DiscussionID; $this->EventArguments['Discussion'] = $Data; $this->fireEvent('DeleteDiscussion'); // Execute deletion of discussion and related bits $this->SQL->delete('Draft', array('DiscussionID' => $DiscussionID)); $Log = val('Log', $Options, true); $LogOptions = val('LogOptions', $Options, array()); if ($Log === true) { $Log = 'Delete'; } LogModel::BeginTransaction(); // Log all of the comment deletes. $Comments = $this->SQL->getWhere('Comment', array('DiscussionID' => $DiscussionID))->resultArray(); if (count($Comments) > 0 && count($Comments) < 50) { // A smaller number of comments should just be stored with the record. $Data['_Data']['Comment'] = $Comments; LogModel::insert($Log, 'Discussion', $Data, $LogOptions); } else { LogModel::insert($Log, 'Discussion', $Data, $LogOptions); foreach ($Comments as $Comment) { LogModel::insert($Log, 'Comment', $Comment, $LogOptions); } } LogModel::EndTransaction(); $this->SQL->delete('Comment', array('DiscussionID' => $DiscussionID)); $this->SQL->delete('Discussion', array('DiscussionID' => $DiscussionID)); $this->SQL->delete('UserDiscussion', array('DiscussionID' => $DiscussionID)); $this->UpdateDiscussionCount($CategoryID); // Get the user's discussion count. $this->UpdateUserDiscussionCount($UserID); // Update bookmark counts for users who had bookmarked this discussion foreach ($BookmarkData->result() as $User) { $this->SetUserBookmarkCount($User->UserID); } return true; }
/** * Delete a user's content across many contexts. * * @param int $UserID * @param array $Options * @param array $Content * @return bool|int */ public function deleteContent($UserID, $Options = [], $Content = []) { $Log = val('Log', $Options); if ($Log === true) { $Log = 'Delete'; } $Result = false; // Fire an event so applications can remove their associated user data. $this->EventArguments['UserID'] = $UserID; $this->EventArguments['Options'] = $Options; $this->EventArguments['Content'] =& $Content; $this->fireEvent('BeforeDeleteUser'); $User = $this->getID($UserID, DATASET_TYPE_ARRAY); if (!$Log) { $Content = null; } // Remove invitations $this->getDelete('Invitation', ['InsertUserID' => $UserID], $Content); $this->getDelete('Invitation', ['AcceptedUserID' => $UserID], $Content); // Remove activities $this->getDelete('Activity', ['InsertUserID' => $UserID], $Content); // Remove activity comments. $this->getDelete('ActivityComment', ['InsertUserID' => $UserID], $Content); // Remove comments in moderation queue $this->getDelete('Log', ['RecordUserID' => $UserID, 'Operation' => 'Pending'], $Content); // Clear out information on the user. $this->setField($UserID, ['About' => null, 'Title' => null, 'Location' => null]); if ($Log) { $User['_Data'] = $Content; unset($Content); // in case data gets copied $Result = LogModel::insert($Log, 'User', $User, val('LogOptions', $Options, [])); } return $Result; }
/** * * * @return bool|null * @throws Exception */ public function save() { if (!$this->Dirty) { return null; } $this->EventArguments['ConfigDirty'] =& $this->Dirty; $this->EventArguments['ConfigNoSave'] = false; $this->EventArguments['ConfigType'] = $this->Type; $this->EventArguments['ConfigSource'] = $this->Source; $this->EventArguments['ConfigData'] = $this->Settings; $this->fireEvent('BeforeSave'); if ($this->EventArguments['ConfigNoSave']) { $this->Dirty = false; return true; } // Check for and fire callback if one exists if ($this->Callback && is_callable($this->Callback)) { $CallbackOptions = array(); if (!is_array($this->CallbackOptions)) { $this->CallbackOptions = array(); } $CallbackOptions = array_merge($CallbackOptions, $this->CallbackOptions, array('ConfigDirty' => $this->Dirty, 'ConfigType' => $this->Type, 'ConfigSource' => $this->Source, 'ConfigData' => $this->Settings, 'SourceObject' => $this)); $ConfigSaved = call_user_func($this->Callback, $CallbackOptions); if ($ConfigSaved) { $this->Dirty = false; return true; } } switch ($this->Type) { case 'file': if (empty($this->Source)) { trigger_error(errorMessage('You must specify a file path to be saved.', 'Configuration', 'Save'), E_USER_ERROR); } $CheckWrite = $this->Source; if (!file_exists($CheckWrite)) { $CheckWrite = dirname($CheckWrite); } if (!is_writable($CheckWrite)) { throw new Exception(sprintf(t("Unable to write to config file '%s' when saving."), $this->Source)); } $Group = $this->Group; $Data =& $this->Settings; ksort($Data); // Check for the case when the configuration is the group. if (is_array($Data) && count($Data) == 1 && array_key_exists($Group, $Data)) { $Data = $Data[$Group]; } // Do a sanity check on the config save. if ($this->Source == Gdn::config()->defaultPath()) { // Log root config changes try { $LogData = $this->Initial; $LogData['_New'] = $this->Settings; LogModel::insert('Edit', 'Configuration', $LogData); } catch (Exception $Ex) { } if (!isset($Data['Database'])) { if ($Pm = Gdn::pluginManager()) { $Pm->EventArguments['Data'] = $Data; $Pm->EventArguments['Backtrace'] = debug_backtrace(); $Pm->fireEvent('ConfigError'); } return false; } } // Write config data to string format, ready for saving $FileContents = Gdn_Configuration::format($Data, array('VariableName' => $Group, 'WrapPHP' => true, 'ByLine' => true)); if ($FileContents === false) { trigger_error(errorMessage('Failed to define configuration file contents.', 'Configuration', 'Save'), E_USER_ERROR); } // Save to cache if we're into that sort of thing $FileKey = sprintf(Gdn_Configuration::CONFIG_FILE_CACHE_KEY, $this->Source); if ($this->Configuration && $this->Configuration->caching() && Gdn::cache()->type() == Gdn_Cache::CACHE_TYPE_MEMORY && Gdn::cache()->activeEnabled()) { $CachedConfigData = Gdn::cache()->store($FileKey, $Data, array(Gdn_Cache::FEATURE_NOPREFIX => true, Gdn_Cache::FEATURE_EXPIRY => 3600)); } $TmpFile = tempnam(PATH_CONF, 'config'); $Result = false; if (file_put_contents($TmpFile, $FileContents) !== false) { chmod($TmpFile, 0775); $Result = rename($TmpFile, $this->Source); } if ($Result) { if (function_exists('apc_delete_file')) { // This fixes a bug with some configurations of apc. @apc_delete_file($this->Source); } elseif (function_exists('opcache_invalidate')) { @opcache_invalidate($this->Source); } } $this->Dirty = false; return $Result; break; case 'json': case 'array': case 'string': /** * How would these even save? String config data must be handled by * an event hook or callback, if at all. */ $this->Dirty = false; return false; break; } }
/** * * * @param $UserID * @param array $Options * @param array $Content * @return bool|int * @throws Exception */ public function deleteContent($UserID, $Options = array(), $Content = array()) { $Log = val('Log', $Options); if ($Log === true) { $Log = 'Delete'; } $Result = false; // Fire an event so applications can remove their associated user data. $this->EventArguments['UserID'] = $UserID; $this->EventArguments['Options'] = $Options; $this->EventArguments['Content'] =& $Content; $this->fireEvent('BeforeDeleteUser'); $User = $this->getID($UserID, DATASET_TYPE_ARRAY); if (!$Log) { $Content = null; } // Remove photos /*$PhotoData = $this->SQL->select()->from('Photo')->where('InsertUserID', $UserID)->get(); foreach ($PhotoData->result() as $Photo) { @unlink(PATH_UPLOADS.DS.$Photo->Name); } $this->SQL->delete('Photo', array('InsertUserID' => $UserID)); */ // Remove invitations $this->GetDelete('Invitation', array('InsertUserID' => $UserID), $Content); $this->GetDelete('Invitation', array('AcceptedUserID' => $UserID), $Content); // Remove activities $this->GetDelete('Activity', array('InsertUserID' => $UserID), $Content); // Remove activity comments. $this->GetDelete('ActivityComment', array('InsertUserID' => $UserID), $Content); // Remove comments in moderation queue $this->GetDelete('Log', array('RecordUserID' => $UserID, 'Operation' => 'Pending'), $Content); // Clear out information on the user. $this->setField($UserID, array('About' => null, 'Title' => null, 'Location' => null)); if ($Log) { $User['_Data'] = $Content; unset($Content); // in case data gets copied $Result = LogModel::insert($Log, 'User', $User, val('LogOptions', $Options, array())); } return $Result; }
/** * Insert a SPAM Queue entry for the specified record and delete the record, if possible. * * @param string $recordType The type of record we're flagging: Discussion or Comment. * @param int $id ID of the record we're flagging. * @param object|array $data Properties used for updating/overriding the record's current values. * * @throws Exception If an invalid record type is specified, throw an exception. */ protected static function flagForReview($recordType, $id, $data) { // We're planning to purge the spammy record. $deleteRow = true; /** * We're only handling two types of content: discussions and comments. Both need some special setup. * Error out if we're not dealing with a discussion or comment. */ switch ($recordType) { case 'Comment': $model = new CommentModel(); $row = $model->getID($id, DATASET_TYPE_ARRAY); break; case 'Discussion': $model = new DiscussionModel(); $row = $model->getID($id, DATASET_TYPE_ARRAY); /** * If our discussion has more than three comments, it might be worth saving. Hold off on deleting and * just flag it. If we have between 0 and 3 comments, save them along with the discussion. */ if ($row['CountComments'] > 3) { $deleteRow = false; } elseif ($row['CountComments'] > 0) { $comments = Gdn::database()->sql()->getWhere('Comment', array('DiscussionID' => $id))->resultArray(); if (!array_key_exists('_Data', $row)) { $row['_Data'] = array(); } $row['_Data']['Comment'] = $comments; } break; default: throw notFoundException($recordType); } $overrideFields = array('Name', 'Body'); foreach ($overrideFields as $fieldName) { if (($fieldValue = val($fieldName, $data, false)) !== false) { $row[$fieldName] = $fieldValue; } } $logOptions = array('GroupBy' => array('RecordID')); if ($deleteRow) { // Remove the record to the log. $model->deleteID($id); } LogModel::insert('Spam', $recordType, $row, $logOptions); }
/** * * * @param array $Data * @param bool $Preference * @param array $Options * @return array * @throws Exception */ public function save($Data, $Preference = false, $Options = array()) { trace('ActivityModel->save()'); $Activity = $Data; $this->_touch($Activity); if ($Activity['ActivityUserID'] == $Activity['NotifyUserID'] && !val('Force', $Options)) { trace('Skipping activity because it would notify the user of something they did.'); return; // don't notify users of something they did. } // Check the user's preference. if ($Preference) { list($Popup, $Email) = self::notificationPreference($Preference, $Activity['NotifyUserID'], 'both'); if ($Popup && !$Activity['Notified']) { $Activity['Notified'] = self::SENT_PENDING; } if ($Email && !$Activity['Emailed']) { $Activity['Emailed'] = self::SENT_PENDING; } if (!$Activity['Notified'] && !$Activity['Emailed'] && !val('Force', $Options)) { trace("Skipping activity because the user has no preference set."); return; } } $ActivityType = self::getActivityType($Activity['ActivityType']); $ActivityTypeID = val('ActivityTypeID', $ActivityType); if (!$ActivityTypeID) { trace("There is no {$ActivityType} activity type.", TRACE_WARNING); $ActivityType = self::getActivityType('Default'); $ActivityTypeID = val('ActivityTypeID', $ActivityType); } $Activity['ActivityTypeID'] = $ActivityTypeID; $NotificationInc = 0; if ($Activity['NotifyUserID'] > 0 && $Activity['Notified']) { $NotificationInc = 1; } // Check to see if we are sharing this activity with another one. if ($CommentActivityID = val('CommentActivityID', $Activity['Data'])) { $CommentActivity = $this->getID($CommentActivityID); $Activity['Data']['CommentNotifyUserID'] = $CommentActivity['NotifyUserID']; } // Make sure this activity isn't a duplicate. if (val('CheckRecord', $Options)) { // Check to see if this record already notified so we don't notify multiple times. $Where = arrayTranslate($Activity, array('NotifyUserID', 'RecordType', 'RecordID')); $Where['DateUpdated >'] = Gdn_Format::toDateTime(strtotime('-2 days')); // index hint $CheckActivity = $this->SQL->getWhere('Activity', $Where)->firstRow(); if ($CheckActivity) { return false; } } // Check to share the activity. if (val('Share', $Options)) { $this->Share($Activity); } // Group he activity. if ($GroupBy = val('GroupBy', $Options)) { $GroupBy = (array) $GroupBy; $Where = array(); foreach ($GroupBy as $ColumnName) { $Where[$ColumnName] = $Activity[$ColumnName]; } $Where['NotifyUserID'] = $Activity['NotifyUserID']; // Make sure to only group activities by day. $Where['DateInserted >'] = Gdn_Format::toDateTime(strtotime('-1 day')); // See if there is another activity to group these into. $GroupActivity = $this->SQL->getWhere('Activity', $Where)->firstRow(DATASET_TYPE_ARRAY); if ($GroupActivity) { $GroupActivity['Data'] = @unserialize($GroupActivity['Data']); $Activity = $this->mergeActivities($GroupActivity, $Activity); $NotificationInc = 0; } } $Delete = false; if ($Activity['Emailed'] == self::SENT_PENDING) { $this->email($Activity); $Delete = val('_Delete', $Activity); } $ActivityData = $Activity['Data']; if (isset($Activity['Data']) && is_array($Activity['Data'])) { $Activity['Data'] = serialize($Activity['Data']); } $this->defineSchema(); $Activity = $this->filterSchema($Activity); $ActivityID = val('ActivityID', $Activity); if (!$ActivityID) { if (!$Delete) { $this->addInsertFields($Activity); touchValue('DateUpdated', $Activity, $Activity['DateInserted']); $this->EventArguments['Activity'] =& $Activity; $this->EventArguments['ActivityID'] = null; $Handled = false; $this->EventArguments['Handled'] =& $Handled; $this->fireEvent('BeforeSave'); if (count($this->validationResults()) > 0) { return false; } if ($Handled) { // A plugin handled this activity so don't save it. return $Activity; } if (val('CheckSpam', $Options)) { // Check for spam $Spam = SpamModel::isSpam('Activity', $Activity); if ($Spam) { return SPAM; } // Check for approval $ApprovalRequired = CheckRestriction('Vanilla.Approval.Require'); if ($ApprovalRequired && !val('Verified', Gdn::session()->User)) { LogModel::insert('Pending', 'Activity', $Activity); return UNAPPROVED; } } $ActivityID = $this->SQL->insert('Activity', $Activity); $Activity['ActivityID'] = $ActivityID; } } else { $Activity['DateUpdated'] = Gdn_Format::toDateTime(); unset($Activity['ActivityID']); $this->EventArguments['Activity'] =& $Activity; $this->EventArguments['ActivityID'] = $ActivityID; $this->fireEvent('BeforeSave'); if (count($this->validationResults()) > 0) { return false; } $this->SQL->put('Activity', $Activity, array('ActivityID' => $ActivityID)); $Activity['ActivityID'] = $ActivityID; } $Activity['Data'] = $ActivityData; if (isset($CommentActivity)) { $CommentActivity['Data']['SharedActivityID'] = $Activity['ActivityID']; $CommentActivity['Data']['SharedNotifyUserID'] = $Activity['NotifyUserID']; $this->setField($CommentActivity['ActivityID'], 'Data', $CommentActivity['Data']); } if ($NotificationInc > 0) { $CountNotifications = Gdn::userModel()->getID($Activity['NotifyUserID'])->CountNotifications + $NotificationInc; Gdn::userModel()->setField($Activity['NotifyUserID'], 'CountNotifications', $CountNotifications); } // If this is a wall post then we need to notify on that. if (val('Name', $ActivityType) == 'WallPost' && $Activity['NotifyUserID'] == self::NOTIFY_PUBLIC) { $this->notifyWallPost($Activity); } return $Activity; }
/** * Check whether or not the record is spam. * @param string $RecordType By default, this should be one of the following: * - Comment: A comment. * - Discussion: A discussion. * - User: A user registration. * @param array $Data The record data. * @param array $Options Options for fine-tuning this method call. * - Log: Log the record if it is found to be spam. */ public static function isSpam($RecordType, $Data, $Options = array()) { if (self::$Disabled) { return false; } // Set some information about the user in the data. if ($RecordType == 'Registration') { touchValue('Username', $Data, $Data['Name']); } else { touchValue('InsertUserID', $Data, Gdn::session()->UserID); $User = Gdn::userModel()->getID(val('InsertUserID', $Data), DATASET_TYPE_ARRAY); if ($User) { if (val('Verified', $User)) { // The user has been verified and isn't a spammer. return false; } touchValue('Username', $Data, $User['Name']); touchValue('Email', $Data, $User['Email']); touchValue('IPAddress', $Data, $User['LastIPAddress']); } } if (!isset($Data['Body']) && isset($Data['Story'])) { $Data['Body'] = $Data['Story']; } touchValue('IPAddress', $Data, Gdn::request()->ipAddress()); $Sp = self::_Instance(); $Sp->EventArguments['RecordType'] = $RecordType; $Sp->EventArguments['Data'] =& $Data; $Sp->EventArguments['Options'] =& $Options; $Sp->EventArguments['IsSpam'] = false; $Sp->fireEvent('CheckSpam'); $Spam = $Sp->EventArguments['IsSpam']; // Log the spam entry. if ($Spam && val('Log', $Options, true)) { $LogOptions = array(); switch ($RecordType) { case 'Registration': $LogOptions['GroupBy'] = array('RecordIPAddress'); break; case 'Comment': case 'Discussion': case 'Activity': case 'ActivityComment': $LogOptions['GroupBy'] = array('RecordID'); break; } LogModel::insert('Spam', $RecordType, $Data, $LogOptions); } return $Spam; }