/** * Writes a content-changelog entry. * * @param string $action Must be one of 'INSERT', 'UPDATE', or 'DELETE'. * @param string $contenttype The contenttype setting to log. * @param int $contentid ID of the content item to log. * @param array $newContent For 'INSERT' and 'UPDATE', the new content; * null for 'DELETE'. * * For the 'UPDATE' and 'DELETE' actions, this function fetches the * previous data from the database; this means that you must call it * _before_ running the actual update/delete query; for the 'INSERT' * action, this is not necessary, and since you really want to provide * an ID, you can only really call the logging function _after_ the update. * @throws \Exception */ private function writeChangelog($action, $contenttype, $contentid, $newContent = null) { $allowed = array('INSERT', 'UPDATE', 'DELETE'); if (!in_array($action, $allowed)) { throw new \Exception("Invalid action '{$action}' specified for changelog (must be one of [ " . implode(', ', $allowed) . " ])"); } if ($this->app['config']->get('general/changelog/enabled')) { $tablename = $this->getTablename($contenttype); if ($action === 'INSERT') { $oldContent = null; } else { $oldContent = $this->app['db']->fetchAssoc("SELECT * FROM {$tablename} WHERE id = ?", array($contentid)); } if (empty($oldContent) && empty($newContent)) { throw new \Exception("Tried to log something that cannot be: both old and new content are empty"); } if (empty($oldContent) && in_array($action, array('UPDATE', 'DELETE'))) { throw new \Exception("Cannot log action {$action} when old content doesn't exist"); } if (empty($newContent) && in_array($action, array('INSERT', 'UPDATE'))) { throw new \Exception("Cannot log action {$action} when new content is empty"); } switch ($action) { case 'UPDATE': $diff = DeepDiff::deep_diff($oldContent, $newContent); foreach ($diff as $item) { list($k, $old, $new) = $item; if (isset($newContent[$k])) { $data[$k] = array($old, $new); } } break; case 'INSERT': foreach ($newContent as $k => $val) { $data[$k] = array(null, $val); } break; case 'DELETE': foreach ($oldContent as $k => $val) { $data[$k] = array($val, null); } break; } if ($newContent) { $content = new Content($this->app, $contenttype, $newContent); } else { $content = new Content($this->app, $contenttype, $oldContent); } $title = $content->getTitle(); if (empty($title)) { $content = $this->getContent("{$contenttype}/{$contentid}"); $title = $content->getTitle(); } $str = json_encode($data); $user = $this->app['users']->getCurrentUser(); $entry['title'] = $title; $entry['date'] = date('Y-m-d H:i:s'); $entry['ownerid'] = $user['id']; $entry['contenttype'] = $contenttype; $entry['contentid'] = $contentid; $entry['mutation_type'] = $action; $entry['diff'] = $str; $this->app['db']->insert($this->getTablename('content_changelog'), $entry); } }
/** * Get the context data. * * @param array $context * * @return array */ protected function getData(array $context) { $data = []; switch ($context['action']) { case 'UPDATE': $diff = DeepDiff::diff($context['old'], $context['new']); foreach ($diff as $item) { list($k, $old, $new) = $item; if (isset($context['new'][$k])) { $data[$k] = [$old, $new]; } } break; case 'INSERT': foreach ($context['new'] as $k => $val) { $data[$k] = [null, $val]; } break; case 'DELETE': foreach ($context['old'] as $k => $val) { $data[$k] = [$val, null]; } break; } return $data; }
/** * @dataProvider deepDiffProvider */ public function testDeepDiff($a, $b, $expected) { $actual = DeepDiff::diff($a, $b); $this->assertEquals($expected, $actual); }
protected function write(array $record) { // Simply exit if we're not enabled if (!$this->app['config']->get('general/changelog/enabled')) { return; } // Initialise ourselves if not already if (!$this->initialized) { $this->initialize(); } // Check for a valid call if (!in_array($record['context']['action'], $this->allowed)) { throw new \Exception("Invalid action '{$record['context']['action']}' specified for changelog (must be one of [ " . implode(', ', $this->allowed) . " ])"); } if (empty($record['context']['old']) && empty($record['context']['new'])) { throw new \Exception("Tried to log something that cannot be: both old and new content are empty"); } if (empty($record['context']['old']) && in_array($record['context']['action'], ['UPDATE', 'DELETE'])) { throw new \Exception("Cannot log action '{$record['context']['action']}' when old content doesn't exist"); } if (empty($record['context']['new']) && in_array($record['context']['action'], ['INSERT', 'UPDATE'])) { throw new \Exception("Cannot log action '{$record['context']['action']}' when new content is empty"); } $data = []; switch ($record['context']['action']) { case 'UPDATE': $diff = DeepDiff::diff($record['context']['old'], $record['context']['new']); foreach ($diff as $item) { list($k, $old, $new) = $item; if (isset($record['context']['new'][$k])) { $data[$k] = [$old, $new]; } } break; case 'INSERT': foreach ($record['context']['new'] as $k => $val) { $data[$k] = [null, $val]; } break; case 'DELETE': foreach ($record['context']['old'] as $k => $val) { $data[$k] = [$val, null]; } break; } if ($record['context']['new']) { $content = new Content($this->app, $record['context']['contenttype'], $record['context']['new']); } else { $content = new Content($this->app, $record['context']['contenttype'], $record['context']['old']); } $title = $content->getTitle(); if (empty($title)) { /** @var \Bolt\Content $content */ $content = $this->app['storage']->getContent($record['context']['contenttype'] . '/' . $record['context']['id']); $title = $content->getTitle(); } // Don't store datechanged, or records that are only datechanged unset($data['datechanged']); if (empty($data)) { return; } $str = json_encode($data); $user = $this->app['users']->getCurrentUser(); try { $this->app['db']->insert($this->tablename, ['date' => $record['datetime']->format('Y-m-d H:i:s'), 'ownerid' => $user['id'], 'title' => $title, 'contenttype' => $record['context']['contenttype'], 'contentid' => $record['context']['id'], 'mutation_type' => $record['context']['action'], 'diff' => $str, 'comment' => $record['context']['comment']]); } catch (\Exception $e) { // Nothing. } }