/** * @return array */ public function actionIndex() { Yii::$app->response->format = Response::FORMAT_JSON; $data = Json::decode(Yii::$app->request->post('data')); if (!isset($data['auditEntry'])) { $entry = Audit::getInstance()->getEntry(true); $data['auditEntry'] = $entry->id; } // Convert data into the loggable object $javascript = new models\AuditJavascript(); $map = ['auditEntry' => 'entry_id', 'message' => 'message', 'type' => 'type', 'file' => 'origin', 'line' => function ($value) use($javascript) { $javascript->origin .= ':' . $value; }, 'col' => function ($value) use($javascript) { $javascript->origin .= ':' . $value; }, 'data' => function ($value) use($javascript) { if (count($value)) { $javascript->data = $value; } }]; foreach ($map as $key => $target) { if (isset($data[$key])) { if (is_callable($target)) { $target($data[$key]); } else { $javascript->{$target} = $data[$key]; } } } if ($javascript->save()) { return ['result' => 'ok', 'entry' => $data['auditEntry']]; } return ['result' => 'error', 'errors' => $javascript->getErrors()]; }
protected function finalize() { $_panel = Audit::getInstance()->getPanel('audit/soap'); if ($_panel) { $_panel->logSoapRequest($this->_data); } }
/** * @param Event $event * @return null|static */ public static function record($event) { /* @var $message MessageInterface */ $message = $event->message; $entry = Audit::getInstance()->getEntry(true); $mail = new static(); $mail->entry_id = $entry->id; $mail->successful = $event->isSuccessful; $mail->from = self::convertParams($message->getFrom()); $mail->to = self::convertParams($message->getTo()); $mail->reply = self::convertParams($message->getReplyTo()); $mail->cc = self::convertParams($message->getCc()); $mail->bcc = self::convertParams($message->getBcc()); $mail->subject = $message->getSubject(); // add more information when message is a SwiftMailer message if ($message instanceof Message) { /* @var $swiftMessage Swift_Message */ $swiftMessage = $message->getSwiftMessage(); foreach ($swiftMessage->getChildren() as $part) { /* @var $part Swift_Mime_MimePart */ if ($part instanceof Swift_Mime_Attachment) { continue; } $contentType = $part->getContentType(); if ($contentType == 'text/plain') { $mail->text = Helper::compress($part->getBody()); } elseif ($contentType == 'text/html') { $mail->html = Helper::compress($part->getBody()); } } } $mail->data = Helper::compress($message->toString()); return $mail->save(false) ? $mail : null; }
/** * @param Exception $exception */ public function logException($exception) { try { $isMemoryError = strncmp($exception->getMessage(), 'Allowed memory size of', 22) === 0; /** @var Audit $audit */ $audit = Audit::getInstance(); if (!$audit && !$isMemoryError) { // Only attempt to load the module if this isn't an out of memory error, not enough room otherwise $audit = \Yii::$app->getModule(Audit::findModuleIdentifier()); } if (!$audit) { throw new \Exception('Audit module cannot be loaded'); } $entry = $audit->getEntry(!$isMemoryError); if ($entry) { /** @var ErrorPanel $errorPanel */ $errorPanel = $audit->getPanel($audit->findPanelIdentifier(ErrorPanel::className())); $errorPanel->log($entry->id, $exception); $entry->finalize(); } } catch (\Exception $e) { // if we catch an exception here, let it slide, we don't want recursive errors killing the script } parent::logException($exception); }
/** * Compress * @param mixed $data * @return string binary blob of data */ public static function uncompress($data) { if (Audit::getInstance()->compressData) { $data = gzuncompress($data); } return $data; }
/** * @param $userId * @param ActiveQuery $query */ protected function filterUserId($userId, $query) { if (strlen($this->user_id)) { if (!is_numeric($userId) && ($callback = Audit::getInstance()->userFilterCallback)) { $userId = call_user_func($callback, $userId); } else { $userId = intval($this->user_id) ?: 0; } } $query->andFilterWhere(['user_id' => $userId]); }
/** * @param \yii\web\AssetManager $assetManager */ public function publish($assetManager) { $module = Audit::getInstance(); // We can't be sure that the actual logger was loaded already, so we fallback on the window object // to store the associated audit url and entry id $url = Url::to(["/{$module->id}/js-log"]); $script = "window.auditUrl = '{$url}';"; if ($module->entry) { $id = $module->getEntry()->id; $script .= "window.auditEntry = {$id};"; } \Yii::$app->view->registerJs($script, View::POS_HEAD); parent::publish($assetManager); }
/** * Check if the current user has access to the audit functionality * @return bool */ public static function checkAccess() { $audit = Audit::getInstance(); if ($audit->accessIps === null && $audit->accessRoles === null && $audit->accessUsers === null) { return true; } if (self::checkAccessIps($audit->accessIps)) { return true; } if (self::checkAccessRoles($audit->accessRoles)) { return true; } if (self::checkAccessUsers($audit->accessUsers)) { return true; } return false; }
/** * Clean up the audit data according to the settings. */ public function actionCleanup() { $audit = Audit::getInstance(); if ($audit->maxAge === null) { return; } $entry = AuditEntry::tableName(); $errors = AuditError::tableName(); $data = AuditData::tableName(); $javascript = AuditJavascript::tableName(); $trail = AuditTrail::tableName(); $threshold = time() - $audit->maxAge * 86400; AuditEntry::getDb()->createCommand(<<<SQL DELETE FROM {$entry}, {$errors}, {$data}, {$javascript}, {$trail} USING {$entry} INNER JOIN {$errors} ON {$errors}.entry_id = {$entry}.id INNER JOIN {$data} ON {$data}.entry_id = {$entry}.id INNER JOIN {$javascript} ON {$javascript}.entry_id = {$entry}.id INNER JOIN {$trail} ON {$trail}.entry_id = {$entry}.id WHERE {$entry}.created < FROM_UNIXTIME({$threshold}) SQL )->execute(); }
/** * @param Exception $exception */ public function logException($exception) { try { $isMemoryError = strncmp($exception->getMessage(), 'Allowed memory size of', 22) === 0; $module = Audit::getInstance(); if (!$module && !$isMemoryError) { // Only attempt to load the module if this isn't an out of memory error, not enough room otherwise $module = \Yii::$app->getModule(Audit::findModuleIdentifier()); } if (!$module) { throw new \Exception('Audit module cannot be loaded'); } $entry = $module->getEntry(!$isMemoryError); if ($entry) { AuditError::log($entry, $exception); $entry->finalize(); } } catch (\Exception $e) { // if we catch an exception here, let it slide, we don't want recursive errors killing the script } parent::logException($exception); }
/* @var $dataProvider yii\data\ActiveDataProvider */ $this->title = Yii::t('audit', 'Trails'); $this->params['breadcrumbs'][] = ['label' => Yii::t('audit', 'Audit'), 'url' => ['default/index']]; $this->params['breadcrumbs'][] = $this->title; ?> <div class="box box-primary"> <div class="box-header" style="cursor: pointer;"> <i class="fa fa-th-list"></i> <h3 class="box-title"><?php echo Html::encode($this->title); ?> </h3> </div> <div class="box-body"> <div class="audit-trail"> <?php echo GridView::widget(['dataProvider' => $dataProvider, 'filterModel' => $searchModel, 'columns' => [['class' => 'yii\\grid\\ActionColumn', 'template' => '{view}'], 'id', ['attribute' => 'entry_id', 'class' => 'yii\\grid\\DataColumn', 'value' => function ($data) { return $data->entry_id ? Html::a($data->entry_id, ['entry/view', 'id' => $data->entry_id]) : ''; }, 'format' => 'raw'], ['attribute' => 'user_id', 'label' => Yii::t('audit', 'User ID'), 'class' => 'yii\\grid\\DataColumn', 'value' => function ($data) { return Audit::getInstance()->getUserIdentifier($data->user_id); }, 'format' => 'raw'], ['attribute' => 'action', 'filter' => AuditTrailSearch::actionFilter()], 'model', 'model_id', 'field', ['label' => Yii::t('audit', 'Diff'), 'value' => function ($model) { return $model->getDiffHtml(); }, 'format' => 'raw'], ['attribute' => 'created', 'options' => ['width' => '150px']]]]); ?> </div> </div> </div>
/** * @param string $className * @return bool|string */ public static function findPanelIdentifier($className) { $audit = Audit::getInstance(); foreach ($audit->panels as $panel) { if ($panel->className() == $className) { return $panel->id; } } return false; }
/** * @return models\AuditEntry|null|static * @throws \Exception */ protected function getAuditEntryId() { $module = Audit::getInstance(); if (!$module) { $module = \Yii::$app->getModule(Audit::findModuleIdentifier()); } if (!$module) { throw new \Exception('Audit module cannot be loaded'); } return Audit::getInstance()->getEntry(true)->id; }
/** * Cleans the Panel data * * @param $id * @param $maxAge * @return bool */ protected function cleanupPanel($id, $maxAge) { /** @var Panel $panel */ $panel = Audit::getInstance()->getPanel($id); $age = $maxAge !== null ? $maxAge : $panel->maxAge; if ($age === null) { $this->stdout("\n*** skipped {$id}\n", Console::FG_PURPLE); return true; } $this->stdout("\n*** cleaning {$id}", Console::FG_YELLOW); $start = microtime(true); $count = $panel->cleanup($maxAge); if ($count !== false) { $time = microtime(true) - $start; $this->stdout("\n*** cleaned {$id} (records: " . $count . ", time: " . sprintf("%.3f", $time) . "s)\n", Console::FG_GREEN); return true; } $time = microtime(true) - $start; $this->stdout("\n*** failed to clean {$id} (time: " . sprintf("%.3f", $time) . "s)\n", Console::FG_RED); return false; }
$this->params['breadcrumbs'][] = ['label' => Yii::t('audit', 'Entries'), 'url' => ['index']]; $this->params['breadcrumbs'][] = '#' . $model->id; ?> <?php echo Html::tag('h1', $this->title); ?> <div class="row"> <div class="col-md-6"> <?php echo Html::tag('h2', Yii::t('audit', 'Request'), ['id' => 'entry', 'class' => 'hashtag']); if ($model->request_method == 'CLI') { $attributes = ['route', 'request_method']; } else { $attributes = [['label' => $model->getAttributeLabel('user_id'), 'value' => Audit::getInstance()->getUserIdentifier($model->user_id), 'format' => 'raw'], 'ip', 'route', 'request_method', ['label' => $model->getAttributeLabel('ajax'), 'value' => $model->ajax ? Yii::t('audit', 'Yes') : Yii::t('audit', 'No')]]; } echo DetailView::widget(['model' => $model, 'attributes' => $attributes]); ?> </div> <div class="col-md-6"> <?php echo Html::tag('h2', Yii::t('audit', 'Profiling'), ['id' => 'entry', 'class' => 'hashtag']); $attributes = [['attribute' => 'duration', 'format' => 'decimal'], ['attribute' => 'memory_max', 'format' => 'shortsize'], 'created']; echo DetailView::widget(['model' => $model, 'attributes' => $attributes]); ?> </div> </div> <?php Pjax::begin(['id' => 'audit-panels', 'timeout' => 0]);
<?php $this->registerCss('body{padding-top: 60px;} .navbar-brand { background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABsElEQVRIS82UwU3DQBBF5+8lR5IO0gFQAaGD0EGoAF+yu0dy804OMRUAFUAH0AEpIXQQbsnFi75kR8Y4wREJwpJlybveN//PH0OOfOHI58v/AYzH4zNjzCyEcLmP6tYKvPeDGONLCAHOuRhjXAJ4BjBJ03SxDboBsEIA96p6Xt3snJsDSPiuBFhrXwGcicgJ3wO4TtP0oQmyAVQrrAFY7cQY81oCynXv/SjGmBEUY7xS1ec65FcAHlb05o2WqWrvG8A5t4wxjowxfJYes6p+CGFY+N2ooDzMOcf9N00qyoZ9OYAe82NVHbQBlPbSSlW9rar4E0AGgBKZkiSEkFhrh8aYLpNB+fX1us/W2gTArClNO+eA0vM8vzDGvG+LYZIk3U6n88aerdfrXpZlyy8W7ZrK4mP241REqO6uur9YfxIRDuGjqo62xnQbqIghIcz6XETKrHdFZASAT9rLFF5Op1Pu2VytfhXe+36e5w8ALmqFfHDQAAypsgnSClCZ3D69Lu7FarWa0/OqlXXIXoC2/apCDgYgvEnJQQFNkIMDKhD+vhdHAewd013N/Wnt6Ao+AURRX/2e7CxYAAAAAElFTkSuQmCC") no-repeat 10px; padding-left: 40px; }'); ?> <?php $this->head(); ?> </head> <body> <?php $this->beginBody(); ?> <?php NavBar::begin(['brandLabel' => Yii::t('audit', 'Audit'), 'brandUrl' => ['default/index'], 'options' => ['class' => 'navbar-default navbar-fixed-top navbar-fluid'], 'innerContainerOptions' => ['class' => 'container-fluid']]); $items = [['label' => Yii::t('audit', 'Entries'), 'url' => ['entry/index']]]; foreach (Audit::getInstance()->panels as $panel) { /** @var Panel $panel */ $indexUrl = $panel->getIndexUrl(); if (!$indexUrl) { continue; } $items[] = ['label' => $panel->getName(), 'url' => $indexUrl]; } echo Nav::widget(['items' => $items, 'options' => ['class' => 'navbar-nav']]); echo Nav::widget(['items' => [['label' => Yii::$app->name, 'url' => Yii::$app->getHomeUrl()]], 'options' => ['class' => 'navbar-nav navbar-right']]); NavBar::end(); ?> <div class="container-fluid"> <?php if (isset($this->params['breadcrumbs'])) {
/** * @return \yii\db\Connection */ public static function getDb() { return Audit::getInstance()->getDb(); }
<?php use bedezign\yii2\audit\Audit; use bedezign\yii2\audit\components\Access; use yii\helpers\Html; /** @var yii\web\View $this */ /** @var bedezign\yii2\audit\models\AuditEntry $entry */ if ($auditEntry = Audit::getInstance()->getEntry()) { if (!isset($style)) { $style = YII_DEBUG ? '' : 'color:transparent;'; } if (Access::checkAccess()) { echo Html::a('audit-' . $auditEntry->id, ['/audit/entry/view', 'id' => $auditEntry->id], ['style' => $style]); } else { echo Html::tag('span', 'audit-' . $auditEntry->id, ['style' => $style]); } }
<?php /** @var yii\web\View $this */ /** @var bedezign\yii2\audit\models\AuditTrail $model */ use bedezign\yii2\audit\Audit; use yii\helpers\Html; use yii\widgets\DetailView; $this->title = Yii::t('audit', 'Trail #{id}', ['id' => $model->id]); $this->params['breadcrumbs'][] = ['label' => Yii::t('audit', 'Audit'), 'url' => ['default/index']]; $this->params['breadcrumbs'][] = ['label' => Yii::t('audit', 'Trails'), 'url' => ['trail/index']]; $this->params['breadcrumbs'][] = '#' . $model->id; ?> <div class="box box-primary"> <div class="box-header" style="cursor: pointer;"> <i class="fa fa-info-circle"></i> <h3 class="box-title"><?php echo Html::encode($this->title); ?> </h3> </div> <div class="box-body"> <?php echo DetailView::widget(['model' => $model, 'attributes' => ['id', ['label' => $model->getAttributeLabel('user_id'), 'value' => Audit::getInstance()->getUserIdentifier($model->user_id), 'format' => 'raw'], ['attribute' => 'entry_id', 'value' => $model->entry_id ? Html::a($model->entry_id, ['entry/view', 'id' => $model->entry_id]) : '', 'format' => 'raw'], 'action', 'model', 'model_id', 'field', 'created']]); echo Html::tag('h2', Yii::t('audit', 'Difference')); echo $model->getDiffHtml(); ?> </div> </div>
/** * @inheritDoc */ protected function saveAuditTrail($action, $newAttributes, $oldAttributes, $entry_id, $user_id, $model, $model_id, $created) { // Build a list of fields to log $rows = array(); $fields = array(); //字段 $newVal = array(); //新值 $oldVal = array(); //旧值 foreach ($newAttributes as $field => $new) { $old = isset($oldAttributes[$field]) ? $oldAttributes[$field] : ''; // If they are not the same lets write an audit log if ($new != $old) { $fields[] = $field; $newVal[] = $new; $oldVal[] = $old; $rows[] = [$entry_id, $user_id, $old, $new, $action, $model, $model_id, $field, $created]; } } //保存一条单独的记录 $rows[] = [$entry_id, $user_id, Json::encode($oldVal), Json::encode($newVal), $action, $model, $model_id, Json::encode($fields), $created]; // Record the field changes with a batch insert if (!empty($rows)) { $columns = ['entry_id', 'user_id', 'old_value', 'new_value', 'action', 'model', 'model_id', 'field', 'created']; $audit = Audit::getInstance(); $audit->getDb()->createCommand()->batchInsert(AuditTrail::tableName(), $columns, $rows)->execute(); } }