diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..567609b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
new file mode 100644
index 0000000..2f24d5a
--- /dev/null
+++ b/.scrutinizer.yml
@@ -0,0 +1,7 @@
+checks:
+ php:
+ code_rating: true
+ duplication: false
+
+tools:
+ external_code_coverage: true
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..de0b3f3
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,43 @@
+# see http://about.travis-ci.org/docs/user/languages/php/ for more hints
+language: php
+
+# list any PHP version you want to test against
+php:
+ # using major version aliases
+
+ # aliased to a recent 5.4.x version
+ - 5.4
+ # aliased to a recent 5.5.x version
+ - 5.5
+ # aliased to a recent 5.6.x version
+ - 5.6
+ # aliased to a recent hhvm version
+ - hhvm
+
+# optionally set up exclutions and allowed failures in the matrix
+matrix:
+ allow_failures:
+ - php: hhvm
+
+# execute any number of scripts before the test run, custom env's are available as variables
+before_script:
+ - curl -sS https://codeload.github.com/pixelandtonic/Craft-Release/zip/master > craft.zip
+ - unzip craft.zip
+ - rm craft.zip
+ - mv Craft-Release-master craft
+ - mkdir craft/config
+ - echo " 'test');" > craft/config/db.php
+ - mkdir craft/storage
+ - mkdir -p craft/plugins/auditlog
+ - for item in *; do if [[ ! "$item" == "craft" ]]; then mv $item craft/plugins/auditlog; fi; done
+ - cd craft/app
+ - composer require mockery/mockery
+ - cd ../..
+
+# execute tests
+script: phpunit --bootstrap craft/app/tests/bootstrap.php --configuration craft/plugins/auditlog/phpunit.xml.dist --coverage-clover coverage.clover craft/plugins/auditlog/tests
+
+# upload coverage to scrutinizer
+after_script:
+ - wget https://scrutinizer-ci.com/ocular.phar
+ - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
diff --git a/AuditLogPlugin.php b/AuditLogPlugin.php
index fc2c9a5..e93a067 100644
--- a/AuditLogPlugin.php
+++ b/AuditLogPlugin.php
@@ -32,7 +32,7 @@ public function getName()
*/
public function getVersion()
{
- return '0.6.2';
+ return '0.7.0';
}
/**
diff --git a/README.md b/README.md
index 474832e..7668690 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-Audit Log plugin for Craft CMS
+Audit Log plugin for Craft CMS [![Build Status](https://scrutinizer-ci.com/g/boboldehampsink/auditlog/badges/build.png?b=develop)](https://scrutinizer-ci.com/g/boboldehampsink/auditlog/build-status/develop) [![Code Coverage](https://scrutinizer-ci.com/g/boboldehampsink/auditlog/badges/coverage.png?b=develop)](https://scrutinizer-ci.com/g/boboldehampsink/auditlog/?branch=develop) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/boboldehampsink/auditlog/badges/quality-score.png?b=develop)](https://scrutinizer-ci.com/g/boboldehampsink/auditlog/?branch=develop)
=================
Plugin that allows you to log adding/updating/deleting of categories/entries/users.
@@ -12,7 +12,7 @@ Features:
- Has hooks that you can use to extend this plugin
- registerAuditLogSources
- getAuditLogTableAttributeHtml
- - modifyAuditLogTableAttributes
+ - defineAvailableTableAttributes
- modifyAuditLogSortableAttributes
- Has events that you can listen to
- auditLog.onElementChanged
@@ -23,8 +23,20 @@ Roadmap:
Important:
The plugin's folder should be named "auditlog"
+Development
+=================
+Run this from your Craft installation to test your changes to this plugin before submitting a Pull Request
+```bash
+phpunit --bootstrap craft/app/tests/bootstrap.php --configuration craft/plugins/auditlog/phpunit.xml.dist --coverage-text craft/plugins/auditlog/tests
+```
+
Changelog
=================
+###0.7.0###
+ - Added Craft 2.5 compatibility
+ - Refactored plugin for better readability, quality and testability
+ - All service code is now fully covered by unit tests
+
###0.6.2###
- Fixed a bug where the date range didn't fully work
- Fixed criteria attributes not fully working
diff --git a/controllers/AuditLogController.php b/controllers/AuditLogController.php
index 1494bbe..490cda1 100644
--- a/controllers/AuditLogController.php
+++ b/controllers/AuditLogController.php
@@ -49,10 +49,15 @@ public function actionDownload()
$log = craft()->auditLog->log($criteria);
// Set status attribute
- $attributes['status'] = Craft::t('Status');
+ $attributes = array('status' => Craft::t('Status'));
- // Get table attributes
- $attributes += $elementType->defineTableAttributes();
+ // Get nice attributes
+ $availableAttributes = $elementType->defineAvailableTableAttributes();
+
+ // Make 'em fit
+ foreach ($availableAttributes as $key => $result) {
+ $attributes[$key] = $result['label'];
+ }
// Ditch the changes button
unset($attributes['changes']);
diff --git a/elementtypes/AuditLogElementType.php b/elementtypes/AuditLogElementType.php
index 038f73a..239a3b5 100644
--- a/elementtypes/AuditLogElementType.php
+++ b/elementtypes/AuditLogElementType.php
@@ -43,39 +43,52 @@ public function hasStatuses()
public function getStatuses()
{
return array(
- AuditLogModel::CREATED => Craft::t('Created'),
+ AuditLogModel::CREATED => Craft::t('Created'),
AuditLogModel::MODIFIED => Craft::t('Modified'),
- AuditLogModel::DELETED => Craft::t('Deleted'),
+ AuditLogModel::DELETED => Craft::t('Deleted'),
);
}
/**
- * Define table column names.
- *
- * @param string $source
+ * Define available table column names.
*
* @return array
*/
- public function defineTableAttributes($source = null)
+ public function defineAvailableTableAttributes()
{
// Define default attributes
$attributes = array(
- 'type' => Craft::t('Type'),
- 'user' => Craft::t('User'),
- 'origin' => Craft::t('Origin'),
- 'dateUpdated' => Craft::t('Modified'),
+ 'type' => array('label' => Craft::t('Type')),
+ 'user' => array('label' => Craft::t('User')),
+ 'origin' => array('label' => Craft::t('Origin')),
+ 'dateUpdated' => array('label' => Craft::t('Modified')),
);
// Allow plugins to modify the attributes
- craft()->plugins->call('modifyAuditLogTableAttributes', array(&$attributes, $source));
+ $pluginAttributes = craft()->plugins->call('defineAdditionalAuditLogTableAttributes', array(), true);
+ foreach ($pluginAttributes as $thisPluginAttributes) {
+ $attributes = array_merge($attributes, $thisPluginAttributes);
+ }
// Set changes at last
- $attributes['changes'] = Craft::t('Changes');
+ $attributes['changes'] = array('label' => Craft::t('Changes'));
// Return the attributes
return $attributes;
}
+ /**
+ * Returns the default table attributes.
+ *
+ * @param string $source
+ *
+ * @return string[]
+ */
+ public function getDefaultTableAttributes($source = null)
+ {
+ return array('type', 'user', 'origin', 'dateUpdated', 'changes');
+ }
+
/**
* Return table attribute html.
*
@@ -101,29 +114,24 @@ public function getTableAttributeHtml(BaseElementModel $element, $attribute)
case 'dateCreated':
case 'dateUpdated':
return craft()->dateFormatter->formatDateTime($element->$attribute);
- break;
// Return clickable user link
case 'user':
$user = $element->getUser();
return $user ? ''.$user.'' : Craft::t('Guest');
- break;
// Return clickable event origin
case 'origin':
return ''.$element->origin.'';
- break;
// Return view changes button
case 'changes':
return ''.Craft::t('View').'';
- break;
// Default behavior
default:
return $element->$attribute;
- break;
}
}
@@ -135,26 +143,24 @@ public function getTableAttributeHtml(BaseElementModel $element, $attribute)
public function defineCriteriaAttributes()
{
return array(
- 'type' => AttributeType::String,
- 'userId' => AttributeType::Number,
- 'origin' => AttributeType::String,
- 'modified' => AttributeType::DateTime,
- 'before' => AttributeType::String,
- 'after' => AttributeType::String,
- 'status' => AttributeType::String,
- 'from' => AttributeType::DateTime,
- 'to' => AttributeType::DateTime,
- 'order' => array(AttributeType::String, 'default' => 'auditlog.id desc'),
+ 'type' => AttributeType::String,
+ 'userId' => AttributeType::Number,
+ 'origin' => AttributeType::String,
+ 'modified' => AttributeType::DateTime,
+ 'before' => AttributeType::String,
+ 'after' => AttributeType::String,
+ 'status' => AttributeType::String,
+ 'from' => AttributeType::DateTime,
+ 'to' => AttributeType::DateTime,
+ 'order' => array(AttributeType::String, 'default' => 'auditlog.id desc'),
);
}
/**
- * Cancel the elements query.
+ * Modify the elements query.
*
* @param DbCommand $query
* @param ElementCriteriaModel $criteria
- *
- * @return bool
*/
public function modifyElementsQuery(DbCommand $query, ElementCriteriaModel $criteria)
{
@@ -190,11 +196,6 @@ public function modifyElementsQuery(DbCommand $query, ElementCriteriaModel $crit
$query->andWhere(DbHelper::parseParam('auditlog.origin', $criteria->origin, $query->params));
}
- // Check for date modified
- if (!empty($criteria->modified)) {
- $query->andWhere(DbHelper::parseDateParam('auditlog.dateUpdated', $criteria->modified, $query->params));
- }
-
// Check before
if (!empty($criteria->before)) {
$query->andWhere(DbHelper::parseParam('auditlog.before', $criteria->before, $query->params));
@@ -205,6 +206,31 @@ public function modifyElementsQuery(DbCommand $query, ElementCriteriaModel $crit
$query->andWhere(DbHelper::parseParam('auditlog.after', $criteria->after, $query->params));
}
+ // Check for status
+ if (!empty($criteria->status)) {
+ $query->andWhere(DbHelper::parseParam('auditlog.status', $criteria->status, $query->params));
+ }
+
+ // Dates
+ $this->applyDateCriteria($criteria, $query);
+
+ // Search
+ $this->applySearchCriteria($criteria, $query);
+ }
+
+ /**
+ * Apply date criteria.
+ *
+ * @param ElementCriteriaModel $criteria
+ * @param DbCommand $query
+ */
+ private function applyDateCriteria(ElementCriteriaModel $criteria, DbCommand $query)
+ {
+ // Check for date modified
+ if (!empty($criteria->modified)) {
+ $query->andWhere(DbHelper::parseDateParam('auditlog.dateUpdated', $criteria->modified, $query->params));
+ }
+
// Check for date from
if (!empty($criteria->from)) {
$query->andWhere(DbHelper::parseDateParam('auditlog.dateUpdated', '>= '.DateTimeHelper::formatTimeForDb($criteria->from), $query->params));
@@ -215,18 +241,16 @@ public function modifyElementsQuery(DbCommand $query, ElementCriteriaModel $crit
$criteria->to->add(new DateInterval('PT23H59M59S'));
$query->andWhere(DbHelper::parseDateParam('auditlog.dateUpdated', '<= '.DateTimeHelper::formatTimeForDb($criteria->to), $query->params));
}
+ }
- // Check for type
- if (!empty($criteria->type)) {
- $query->andWhere(DbHelper::parseParam('auditlog.type', $criteria->type, $query->params));
- }
-
- // Check for status
- if (!empty($criteria->status)) {
- $query->andWhere(DbHelper::parseParam('auditlog.status', $criteria->status, $query->params));
- }
-
- // Search
+ /**
+ * Apply search criteria.
+ *
+ * @param ElementCriteriaModel $criteria
+ * @param DbCommand $query
+ */
+ private function applySearchCriteria(ElementCriteriaModel $criteria, DbCommand $query)
+ {
if (!empty($criteria->search)) {
// Always perform a LIKE search
@@ -273,7 +297,7 @@ public function getSources($context = null)
// Set default sources
$sources = array(
'*' => array(
- 'label' => Craft::t('All logs'),
+ 'label' => Craft::t('All logs'),
),
array('heading' => Craft::t('Elements')),
);
@@ -281,9 +305,9 @@ public function getSources($context = null)
// Show sources for entries when enabled
if (in_array(ElementType::Entry, $settings->enabled)) {
$sources['entries'] = array(
- 'label' => Craft::t('Entries'),
- 'criteria' => array(
- 'type' => ElementType::Entry,
+ 'label' => Craft::t('Entries'),
+ 'criteria' => array(
+ 'type' => ElementType::Entry,
),
);
}
@@ -291,9 +315,9 @@ public function getSources($context = null)
// Show sources for categories when enabled
if (in_array(ElementType::Category, $settings->enabled)) {
$sources['categories'] = array(
- 'label' => Craft::t('Categories'),
- 'criteria' => array(
- 'type' => ElementType::Category,
+ 'label' => Craft::t('Categories'),
+ 'criteria' => array(
+ 'type' => ElementType::Category,
),
);
}
@@ -301,9 +325,9 @@ public function getSources($context = null)
// Show sources for users when enabled
if (in_array(ElementType::User, $settings->enabled)) {
$sources['users'] = array(
- 'label' => Craft::t('Users'),
- 'criteria' => array(
- 'type' => ElementType::User,
+ 'label' => Craft::t('Users'),
+ 'criteria' => array(
+ 'type' => ElementType::User,
),
);
}
@@ -331,10 +355,10 @@ public function getSources($context = null)
public function defineSortableAttributes()
{
// Set modified first
- $attributes['dateUpdated'] = Craft::t('Modified');
+ $attributes = array('dateUpdated' => Craft::t('Modified'));
// Get table attributes
- $attributes = array_merge($attributes, $this->defineTableAttributes());
+ $attributes = array_merge($attributes, parent::defineSortableAttributes());
// Unset unsortable attributes
unset($attributes['user'], $attributes['changes']);
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..19f5544
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,25 @@
+
+
+
+
+ tests
+
+
+
+
+ ./services
+
+
+
+
+
+
+
diff --git a/services/AuditLogService.php b/services/AuditLogService.php
index efef05f..278dc66 100644
--- a/services/AuditLogService.php
+++ b/services/AuditLogService.php
@@ -21,6 +21,8 @@ class AuditLogService extends BaseApplicationComponent
* @param object $criteria
*
* @return array
+ *
+ * @codeCoverageIgnore
*/
public function log($criteria)
{
@@ -40,7 +42,6 @@ public function log($criteria)
*/
public function view($id)
{
-
// Get log from record
$log = craft()->elements->getCriteria('AuditLog', array('id' => $id))->first();
@@ -52,10 +53,10 @@ public function view($id)
// Set parsed values
$diff[$handle] = array(
- 'label' => $item['label'],
+ 'label' => $item['label'],
'changed' => ($item['value'] != $log['before'][$handle]['value']),
- 'after' => $item['value'],
- 'before' => $log['before'][$handle]['value'],
+ 'after' => $item['value'],
+ 'before' => $log['before'][$handle]['value'],
);
}
@@ -76,7 +77,6 @@ public function view($id)
*/
public function parseFieldData($handle, $data)
{
-
// Do we have any data at all
if (!is_null($data)) {
@@ -144,13 +144,14 @@ public function parseFieldData($handle, $data)
* @param int $id
* @param array $before
* @param array $after
+ *
+ * @return array
*/
public function elementHasChanged($elementType, $id, $before, $after)
{
-
// Flatten arrays
$flatBefore = ArrayHelper::flattenArray($before);
- $flatAfter = ArrayHelper::flattenArray($after);
+ $flatAfter = ArrayHelper::flattenArray($after);
// Calculate the diffence
$flatDiff = array_diff_assoc($flatAfter, $flatBefore);
@@ -171,11 +172,13 @@ public function elementHasChanged($elementType, $id, $before, $after)
// Fire an "onElementChanged" event
$event = new Event($this, array(
'elementType' => $elementType,
- 'id' => $id,
- 'diff' => $diff,
+ 'id' => $id,
+ 'diff' => $diff,
));
$this->onElementChanged($event);
}
+
+ return $diff;
}
/**
@@ -192,6 +195,8 @@ public function onElementChanged(Event $event)
* Fires an "onFieldChanged" event.
*
* @param Event $event
+ *
+ * @codeCoverageIgnore
*/
public function onFieldChanged(Event $event)
{
diff --git a/services/AuditLog_CategoryService.php b/services/AuditLog_CategoryService.php
index 32f8169..4caf7a8 100644
--- a/services/AuditLog_CategoryService.php
+++ b/services/AuditLog_CategoryService.php
@@ -27,114 +27,133 @@ class AuditLog_CategoryService extends BaseApplicationComponent
*
* @var array
*/
- public $after = array();
+ public $after = array();
/**
* Initialize the category saving/deleting events.
+ *
+ * @codeCoverageIgnore
*/
public function log()
{
-
// Get values before saving
- craft()->on('categories.onBeforeSaveCategory', function (Event $event) {
-
- // Get category id to save
- $id = $event->params['category']->id;
-
- if (!$event->params['isNewCategory']) {
+ craft()->on('categories.onBeforeSaveCategory', array($this, 'onBeforeSaveCategory'));
- // Get old category from db
- $category = CategoryModel::populateModel(CategoryRecord::model()->findById($id));
-
- // Get fields
- craft()->auditLog_category->before = craft()->auditLog_category->fields($category);
- } else {
+ // Get values after saving
+ craft()->on('categories.onSaveCategory', array($this, 'onSaveCategory'));
- // Get fields
- craft()->auditLog_category->before = craft()->auditLog_category->fields($event->params['category'], true);
- }
+ // Get values before deleting
+ craft()->on('categories.onBeforeDeleteCategory', array($this, 'onBeforeDeleteCategory'));
+ }
- });
+ /**
+ * Handle the onBeforeSaveCategory event.
+ *
+ * @param Event $event
+ */
+ public function onBeforeSaveCategory(Event $event)
+ {
+ // Get category id to save
+ $id = $event->params['category']->id;
- // Get values after saving
- craft()->on('categories.onSaveCategory', function (Event $event) {
+ if (!$event->params['isNewCategory']) {
- // Get saved category
- $category = $event->params['category'];
+ // Get old category from db
+ $category = CategoryModel::populateModel(CategoryRecord::model()->findById($id));
// Get fields
- craft()->auditLog_category->after = craft()->auditLog_category->fields($category);
+ $this->before = $this->fields($category);
+ } else {
- // New row
- $log = new AuditLogRecord();
+ // Get fields
+ $this->before = $this->fields($event->params['category'], true);
+ }
+ }
- // Get user
- $user = craft()->userSession->getUser();
+ /**
+ * Handle the onSaveCategory event.
+ *
+ * @param Event $event
+ */
+ public function onSaveCategory(Event $event)
+ {
+ // Get saved category
+ $category = $event->params['category'];
- // Set user id
- $log->userId = $user ? $user->id : null;
+ // Get fields
+ $this->after = $this->fields($category);
- // Set element type
- $log->type = ElementType::Category;
+ // New row
+ $log = new AuditLogRecord();
- // Set origin
- $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
+ // Get user
+ $user = craft()->userSession->getUser();
- // Set before
- $log->before = craft()->auditLog_category->before;
+ // Set user id
+ $log->userId = $user ? $user->id : null;
- // Set after
- $log->after = craft()->auditLog_category->after;
+ // Set element type
+ $log->type = ElementType::Category;
- // Set status
- $log->status = ($event->params['isNewCategory'] ? AuditLogModel::CREATED : AuditLogModel::MODIFIED);
+ // Set origin
+ $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
- // Save row
- $log->save(false);
+ // Set before
+ $log->before = $this->before;
- // Callback
- craft()->auditLog->elementHasChanged(ElementType::Category, $category->id, craft()->auditLog_category->before, craft()->auditLog_category->after);
+ // Set after
+ $log->after = $this->after;
- });
+ // Set status
+ $log->status = ($event->params['isNewCategory'] ? AuditLogModel::CREATED : AuditLogModel::MODIFIED);
- // Get values before deleting
- craft()->on('categories.onBeforeDeleteCategory', function (Event $event) {
+ // Save row
+ $log->save(false);
- // Get deleted category
- $category = $event->params['category'];
+ // Callback
+ craft()->auditLog->elementHasChanged(ElementType::Category, $category->id, $this->before, $this->after);
+ }
- // Get fields
- craft()->auditLog_category->before = craft()->auditLog_category->fields($category);
- craft()->auditLog_category->after = craft()->auditLog_category->fields($category, true);
+ /**
+ * Handle the onBeforeDeleteCategory event.
+ *
+ * @param Event $event
+ */
+ public function onBeforeDeleteCategory(Event $event)
+ {
+ // Get deleted category
+ $category = $event->params['category'];
- // New row
- $log = new AuditLogRecord();
+ // Get fields
+ $this->before = $this->fields($category);
+ $this->after = $this->fields($category, true);
- // Set user id
- $log->userId = craft()->userSession->getUser()->id;
+ // New row
+ $log = new AuditLogRecord();
- // Set element type
- $log->type = ElementType::Category;
+ // Set user id
+ $log->userId = craft()->userSession->getUser()->id;
- // Set origin
- $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
+ // Set element type
+ $log->type = ElementType::Category;
- // Set before
- $log->before = craft()->auditLog_category->before;
+ // Set origin
+ $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
- // Set after
- $log->after = craft()->auditLog_category->after;
+ // Set before
+ $log->before = $this->before;
- // Set status
- $log->status = AuditLogModel::DELETED;
+ // Set after
+ $log->after = $this->after;
- // Save row
- $log->save(false);
+ // Set status
+ $log->status = AuditLogModel::DELETED;
- // Callback
- craft()->auditLog->elementHasChanged(ElementType::Category, $category->id, craft()->auditLog_category->before, craft()->auditLog_category->after);
+ // Save row
+ $log->save(false);
- });
+ // Callback
+ craft()->auditLog->elementHasChanged(ElementType::Category, $category->id, $this->before, $this->after);
}
/**
@@ -156,11 +175,11 @@ public function fields(CategoryModel $category, $empty = false)
),
'title' => array(
'label' => Craft::t('Title'),
- 'value' => $category->title,
+ 'value' => (string) $category->getTitle(),
),
'group' => array(
'label' => Craft::t('Group'),
- 'value' => $category->group->name,
+ 'value' => (string) $category->getGroup(),
),
);
@@ -168,7 +187,13 @@ public function fields(CategoryModel $category, $empty = false)
$elementType = craft()->elements->getElementType(ElementType::Category);
// Get nice attributes
- $attributes = $elementType->defineTableAttributes();
+ $availableAttributes = $elementType->defineAvailableTableAttributes();
+
+ // Make 'em fit
+ $attributes = array();
+ foreach ($availableAttributes as $key => $result) {
+ $attributes[$key] = $result['label'];
+ }
// Get static "fields"
foreach ($category->getAttributes() as $handle => $value) {
diff --git a/services/AuditLog_EntryService.php b/services/AuditLog_EntryService.php
index 20a8a42..51509ad 100644
--- a/services/AuditLog_EntryService.php
+++ b/services/AuditLog_EntryService.php
@@ -27,114 +27,133 @@ class AuditLog_EntryService extends BaseApplicationComponent
*
* @var array
*/
- public $after = array();
+ public $after = array();
/**
* Initialize the category saving/deleting events.
+ *
+ * @codeCoverageIgnore
*/
public function log()
{
-
// Get values before saving
- craft()->on('entries.onBeforeSaveEntry', function (Event $event) {
-
- // Get entry id to save
- $id = $event->params['entry']->id;
-
- if (!$event->params['isNewEntry']) {
-
- // Get old entry from db
- $entry = EntryModel::populateModel(EntryRecord::model()->findById($id));
+ craft()->on('entries.onBeforeSaveEntry', array($this, 'onBeforeSaveEntry'));
- // Get fields
- craft()->auditLog_entry->before = craft()->auditLog_entry->fields($entry);
- } else {
+ // Get values after saving
+ craft()->on('entries.onSaveEntry', array($this, 'onSaveEntry'));
- // Get fields
- craft()->auditLog_entry->before = craft()->auditLog_entry->fields($event->params['entry'], true);
- }
+ // Get values before deleting
+ craft()->on('entries.onBeforeDeleteEntry', array($this, 'onBeforeDeleteEntry'));
+ }
- });
+ /**
+ * Handle the onBeforeSaveEntry event.
+ *
+ * @param Event $event
+ */
+ public function onBeforeSaveEntry(Event $event)
+ {
+ // Get entry id to save
+ $id = $event->params['entry']->id;
- // Get values after saving
- craft()->on('entries.onSaveEntry', function (Event $event) {
+ if (!$event->params['isNewEntry']) {
- // Get saved entry
- $entry = $event->params['entry'];
+ // Get old entry from db
+ $entry = EntryModel::populateModel(EntryRecord::model()->findById($id));
// Get fields
- craft()->auditLog_entry->after = craft()->auditLog_entry->fields($entry);
+ $this->before = $this->fields($entry);
+ } else {
- // New row
- $log = new AuditLogRecord();
+ // Get fields
+ $this->before = $this->fields($event->params['entry'], true);
+ }
+ }
- // Get user
- $user = craft()->userSession->getUser();
+ /**
+ * Handle the onSaveEntry event.
+ *
+ * @param Event $event
+ */
+ public function onSaveEntry(Event $event)
+ {
+ // Get saved entry
+ $entry = $event->params['entry'];
- // Set user id
- $log->userId = $user ? $user->id : null;
+ // Get fields
+ $this->after = $this->fields($entry);
- // Set element type
- $log->type = ElementType::Entry;
+ // New row
+ $log = new AuditLogRecord();
- // Set origin
- $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
+ // Get user
+ $user = craft()->userSession->getUser();
- // Set before
- $log->before = craft()->auditLog_entry->before;
+ // Set user id
+ $log->userId = $user ? $user->id : null;
- // Set after
- $log->after = craft()->auditLog_entry->after;
+ // Set element type
+ $log->type = ElementType::Entry;
- // Set status
- $log->status = ($event->params['isNewEntry'] ? AuditLogModel::CREATED : AuditLogModel::MODIFIED);
+ // Set origin
+ $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
- // Save row
- $log->save(false);
+ // Set before
+ $log->before = $this->before;
- // Callback
- craft()->auditLog->elementHasChanged(ElementType::Entry, $entry->id, craft()->auditLog_entry->before, craft()->auditLog_entry->after);
+ // Set after
+ $log->after = $this->after;
- });
+ // Set status
+ $log->status = ($event->params['isNewEntry'] ? AuditLogModel::CREATED : AuditLogModel::MODIFIED);
- // Get values before deleting
- craft()->on('entries.onBeforeDeleteEntry', function (Event $event) {
+ // Save row
+ $log->save(false);
- // Get deleted entry
- $entry = $event->params['entry'];
+ // Callback
+ craft()->auditLog->elementHasChanged(ElementType::Entry, $entry->id, $this->before, $this->after);
+ }
- // Get fields
- craft()->auditLog_entry->before = craft()->auditLog_entry->fields($entry);
- craft()->auditLog_entry->after = craft()->auditLog_entry->fields($entry, true);
+ /**
+ * Handle the onBeforeDeleteEntry event.
+ *
+ * @param Event $event
+ */
+ public function onBeforeDeleteEntry(Event $event)
+ {
+ // Get deleted entry
+ $entry = $event->params['entry'];
- // New row
- $log = new AuditLogRecord();
+ // Get fields
+ $this->before = $this->fields($entry);
+ $this->after = $this->fields($entry, true);
- // Set user id
- $log->userId = craft()->userSession->getUser()->id;
+ // New row
+ $log = new AuditLogRecord();
- // Set element type
- $log->type = ElementType::Entry;
+ // Set user id
+ $log->userId = craft()->userSession->getUser()->id;
- // Set origin
- $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
+ // Set element type
+ $log->type = ElementType::Entry;
- // Set before
- $log->before = craft()->auditLog_entry->before;
+ // Set origin
+ $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
- // Set after
- $log->after = craft()->auditLog_entry->after;
+ // Set before
+ $log->before = $this->before;
- // Set status
- $log->status = AuditLogModel::DELETED;
+ // Set after
+ $log->after = $this->after;
- // Save row
- $log->save(false);
+ // Set status
+ $log->status = AuditLogModel::DELETED;
- // Callback
- craft()->auditLog->elementHasChanged(ElementType::Entry, $entry->id, craft()->auditLog_entry->before, craft()->auditLog_entry->after);
+ // Save row
+ $log->save(false);
- });
+ // Callback
+ craft()->auditLog->elementHasChanged(ElementType::Entry, $entry->id, $this->before, $this->after);
}
/**
@@ -156,11 +175,11 @@ public function fields(EntryModel $entry, $empty = false)
),
'title' => array(
'label' => Craft::t('Title'),
- 'value' => $entry->title,
+ 'value' => (string) $entry->getTitle(),
),
'section' => array(
'label' => Craft::t('Section'),
- 'value' => $entry->section->name,
+ 'value' => (string) $entry->getSection(),
),
);
@@ -168,7 +187,13 @@ public function fields(EntryModel $entry, $empty = false)
$elementType = craft()->elements->getElementType(ElementType::Entry);
// Get nice attributes
- $attributes = $elementType->defineTableAttributes();
+ $availableAttributes = $elementType->defineAvailableTableAttributes();
+
+ // Make 'em fit
+ $attributes = array();
+ foreach ($availableAttributes as $key => $result) {
+ $attributes[$key] = $result['label'];
+ }
// Get static "fields"
foreach ($entry->getAttributes() as $handle => $value) {
@@ -184,21 +209,23 @@ public function fields(EntryModel $entry, $empty = false)
// Get fieldlayout
$entrytype = $entry->getType();
- $tabs = craft()->fields->getLayoutById($entrytype->fieldLayoutId)->getTabs();
- foreach ($tabs as $tab) {
- foreach ($tab->getFields() as $field) {
-
- // Get field values
- $field = $field->getField();
- $handle = $field->handle;
- $label = $field->name;
- $value = $empty ? '' : craft()->auditLog->parseFieldData($handle, $entry->$handle);
-
- // Set on fields
- $fields[$handle] = array(
- 'label' => $label,
- 'value' => $value,
- );
+ if ($entrytype) {
+ $tabs = craft()->fields->getLayoutById($entrytype->fieldLayoutId)->getTabs();
+ foreach ($tabs as $tab) {
+ foreach ($tab->getFields() as $field) {
+
+ // Get field values
+ $field = $field->getField();
+ $handle = $field->handle;
+ $label = $field->name;
+ $value = $empty ? '' : craft()->auditLog->parseFieldData($handle, $entry->$handle);
+
+ // Set on fields
+ $fields[$handle] = array(
+ 'label' => $label,
+ 'value' => $value,
+ );
+ }
}
}
diff --git a/services/AuditLog_UserService.php b/services/AuditLog_UserService.php
index 4b67dd2..d03e2a8 100644
--- a/services/AuditLog_UserService.php
+++ b/services/AuditLog_UserService.php
@@ -27,111 +27,130 @@ class AuditLog_UserService extends BaseApplicationComponent
*
* @var array
*/
- public $after = array();
+ public $after = array();
/**
- * Initialize the category saving/deleting events.
+ * Initialize the user saving/deleting events.
+ *
+ * @codeCoverageIgnore
*/
public function log()
{
-
// Get values before saving
- craft()->on('users.onBeforeSaveUser', function (Event $event) {
-
- // Get user id to save
- $id = $event->params['user']->id;
-
- if (!$event->params['isNewUser']) {
+ craft()->on('users.onBeforeSaveUser', array($this, 'onBeforeSaveUser'));
- // Get old user from db
- $user = UserModel::populateModel(UserRecord::model()->findById($id));
-
- // Get fields
- craft()->auditLog_user->before = craft()->auditLog_user->fields($user);
- } else {
+ // Get values after saving
+ craft()->on('users.onSaveUser', array($this, 'onSaveUser'));
- // Get fields
- craft()->auditLog_user->before = craft()->auditLog_user->fields($event->params['user'], true);
- }
+ // Get values before deleting
+ craft()->on('users.onBeforeDeleteUser', array($this, 'onBeforeDeleteUser'));
+ }
- });
+ /**
+ * Handle the onBeforeSaveUser event.
+ *
+ * @param Event $event
+ */
+ public function onBeforeSaveUser(Event $event)
+ {
+ // Get user id to save
+ $id = $event->params['user']->id;
- // Get values after saving
- craft()->on('users.onSaveUser', function (Event $event) {
+ if (!$event->params['isNewUser']) {
- // Get saved user
- $user = $event->params['user'];
+ // Get old user from db
+ $user = UserModel::populateModel(UserRecord::model()->findById($id));
// Get fields
- craft()->auditLog_user->after = craft()->auditLog_user->fields($user);
+ $this->before = $this->fields($user);
+ } else {
- // New row
- $log = new AuditLogRecord();
+ // Get fields
+ $this->before = $this->fields($event->params['user'], true);
+ }
+ }
- // Set user id
- $log->userId = craft()->userSession->getUser() ? craft()->userSession->getUser()->id : $user->id;
+ /**
+ * Handle the onSaveUser event.
+ *
+ * @param Event $event
+ */
+ public function onSaveUser(Event $event)
+ {
+ // Get saved user
+ $user = $event->params['user'];
- // Set element type
- $log->type = ElementType::User;
+ // Get fields
+ $this->after = $this->fields($user);
- // Set origin
- $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
+ // New row
+ $log = new AuditLogRecord();
- // Set before
- $log->before = craft()->auditLog_user->before;
+ // Set user id
+ $log->userId = craft()->userSession->getUser() ? craft()->userSession->getUser()->id : $user->id;
- // Set after
- $log->after = craft()->auditLog_user->after;
+ // Set element type
+ $log->type = ElementType::User;
- // Set status
- $log->status = ($event->params['isNewUser'] ? AuditLogModel::CREATED : AuditLogModel::MODIFIED);
+ // Set origin
+ $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
- // Save row
- $log->save(false);
+ // Set before
+ $log->before = $this->before;
- // Callback
- craft()->auditLog->elementHasChanged(ElementType::User, $user->id, craft()->auditLog_user->before, craft()->auditLog_user->after);
+ // Set after
+ $log->after = $this->after;
- });
+ // Set status
+ $log->status = ($event->params['isNewUser'] ? AuditLogModel::CREATED : AuditLogModel::MODIFIED);
- // Get values before deleting
- craft()->on('users.onBeforeDeleteUser', function (Event $event) {
+ // Save row
+ $log->save(false);
- // Get deleted user
- $user = $event->params['user'];
+ // Callback
+ craft()->auditLog->elementHasChanged(ElementType::User, $user->id, $this->before, $this->after);
+ }
- // Get fields
- craft()->auditLog_user->before = craft()->auditLog_user->fields($user);
- craft()->auditLog_user->after = craft()->auditLog_user->fields($user, true);
+ /**
+ * Handle the onBeforeDeleteUser event.
+ *
+ * @param Event $event
+ */
+ public function onBeforeDeleteUser(Event $event)
+ {
+ // Get deleted user
+ $user = $event->params['user'];
- // New row
- $log = new AuditLogRecord();
+ // Get fields
+ $this->before = $this->fields($user);
+ $this->after = $this->fields($user, true);
- // Set user id
- $log->userId = craft()->userSession->getUser()->id;
+ // New row
+ $log = new AuditLogRecord();
- // Set element type
- $log->type = ElementType::User;
+ // Set user id
+ $log->userId = craft()->userSession->getUser()->id;
- // Set origin
- $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
+ // Set element type
+ $log->type = ElementType::User;
- // Set before
- $log->before = craft()->auditLog_user->before;
+ // Set origin
+ $log->origin = craft()->request->isCpRequest() ? craft()->config->get('cpTrigger').'/'.craft()->request->path : craft()->request->path;
- // Set after
- $log->after = craft()->auditLog_user->after;
+ // Set before
+ $log->before = $this->before;
- // Set status
- $log->status = AuditLogModel::DELETED;
+ // Set after
+ $log->after = $this->after;
- // Save row
- $log->save(false);
+ // Set status
+ $log->status = AuditLogModel::DELETED;
- // Callback
- craft()->auditLog->elementHasChanged(ElementType::User, $user->id, craft()->auditLog_user->before, craft()->auditLog_user->after);
+ // Save row
+ $log->save(false);
- });
+ // Callback
+ craft()->auditLog->elementHasChanged(ElementType::User, $user->id, $this->before, $this->after);
}
/**
@@ -144,7 +163,6 @@ public function log()
*/
public function fields(UserModel $user, $empty = false)
{
-
// Check if we are saving new groups
$groupIds = craft()->request->getPost('groups', false);
@@ -179,7 +197,13 @@ public function fields(UserModel $user, $empty = false)
$elementType = craft()->elements->getElementType(ElementType::User);
// Get nice attributes
- $attributes = $elementType->defineTableAttributes();
+ $availableAttributes = $elementType->defineAvailableTableAttributes();
+
+ // Make 'em fit
+ $attributes = array();
+ foreach ($availableAttributes as $key => $result) {
+ $attributes[$key] = $result['label'];
+ }
// Get static "fields"
foreach ($user->getAttributes() as $handle => $value) {
diff --git a/templates/index.twig b/templates/index.twig
index 1f8cb77..e13a148 100644
--- a/templates/index.twig
+++ b/templates/index.twig
@@ -74,7 +74,7 @@ $('#auditlog-download-csv').click(function(e) {
var form = $(this).after('
').next('form');
// Add controller data
- var params = decodeURIComponent($.param(Craft.elementIndex.getControllerData())).split('&');
+ var params = decodeURIComponent($.param(Craft.elementIndex.getViewParams())).split('&');
$.each(params, function(key, value) {
$(form).append('');
});
diff --git a/tests/AuditLogServiceTest.php b/tests/AuditLogServiceTest.php
new file mode 100644
index 0000000..b82f7c7
--- /dev/null
+++ b/tests/AuditLogServiceTest.php
@@ -0,0 +1,256 @@
+
+ * @copyright Copyright (c) 2015, Bob Olde Hampsink
+ * @license MIT
+ *
+ * @link http://github.com/boboldehampsink
+ *
+ * @coversDefaultClass Craft\AuditLogService
+ * @covers ::
+ */
+class AuditLogServiceTest extends BaseTest
+{
+ /**
+ * {@inheritdoc}
+ */
+ public static function setUpBeforeClass()
+ {
+ // Set up parent
+ parent::setUpBeforeClass();
+
+ // Require dependencies
+ require_once __DIR__.'/../services/AuditLogService.php';
+ require_once __DIR__.'/../models/AuditLogModel.php';
+ }
+
+ /**
+ * Test view.
+ *
+ * @covers ::view
+ */
+ final public function testView()
+ {
+ $this->setMockElementsService();
+
+ $service = new AuditLogService();
+ $result = $service->view(1);
+
+ $this->assertInstanceOf('Craft\AuditLogModel', $result);
+ }
+
+ /**
+ * Test parseFieldData.
+ *
+ * @param string $handle
+ * @param mixed $data
+ * @param string $expected
+ *
+ * @covers ::parseFieldData
+ * @dataProvider provideFieldData
+ */
+ final public function testParseFieldData($handle, $data, $expected)
+ {
+ $this->setMockFieldsService($handle);
+
+ $service = new AuditLogService();
+ $result = $service->parseFieldData($handle, $data);
+
+ $this->assertSame($result, $expected);
+ }
+
+ /**
+ * Test elementHasChanged.
+ *
+ * @covers ::elementHasChanged
+ * @covers ::onElementChanged
+ */
+ final public function testElementHasChanged()
+ {
+ $before = array(
+ 'test' => array(
+ 'label' => 'test1',
+ 'value' => 'test1',
+ ),
+ );
+
+ $after = array(
+ 'test' => array(
+ 'label' => 'test2',
+ 'value' => 'test2',
+ ),
+ );
+
+ $service = new AuditLogService();
+ $result = $service->elementHasChanged(ElementType::Entry, 1, $before, $after);
+
+ $this->assertInternalType('array', $result);
+ }
+
+ /**
+ * Provide field data.
+ *
+ * @return array
+ */
+ final public function provideFieldData()
+ {
+ require_once __DIR__.'/../models/AuditLogModel.php';
+
+ return array(
+ 'Parse ElementCriteriaModel' => array(
+ 'element',
+ $this->getMockElementCriteriaModel(),
+ 'test, test',
+ ),
+ 'Parse Lightswitch with "no" option' => array(
+ 'lightswitch',
+ '0',
+ Craft::t('No'),
+ ),
+ 'Parse Lightswitch with "yes" option' => array(
+ 'lightswitch',
+ '1',
+ Craft::t('Yes'),
+ ),
+ 'Parse empty data' => array(
+ 'empty',
+ null,
+ '',
+ ),
+ 'Parse data array' => array(
+ 'array',
+ array('test', 'test'),
+ 'test, test',
+ ),
+ 'Parse data object' => array(
+ 'object',
+ call_user_func(function () {
+ $class = new \stdClass();
+ $class->test = 'test';
+
+ return $class;
+ }),
+ 'test',
+ ),
+ );
+ }
+
+ /**
+ * Mock ElementsService.
+ */
+ private function setMockElementsService()
+ {
+ $mock = $this->getMockBuilder('Craft\ElementsService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getCriteria'))
+ ->getMock();
+
+ $criteria = $this->getMockElementCriteriaModel();
+
+ $mock->expects($this->any())->method('getCriteria')->willReturn($criteria);
+
+ $this->setComponent(craft(), 'elements', $mock);
+ }
+
+ /**
+ * Mock ElementCriteriaModel.
+ *
+ * @return ElementCriteriaModel
+ */
+ private function getMockElementCriteriaModel()
+ {
+ $mock = $this->getMockBuilder('Craft\ElementCriteriaModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__set', '__get', 'first', 'find'))
+ ->getMock();
+
+ $log = $this->getMockAuditLogModel();
+
+ $mock->expects($this->any())->method('__set')->willReturn(true);
+ $mock->expects($this->any())->method('__get')->willReturn(true);
+ $mock->expects($this->any())->method('first')->willReturn($log);
+ $mock->expects($this->any())->method('find')->willReturn(array($log, $log));
+
+ return $mock;
+ }
+
+ /**
+ * Mock EntryModel.
+ *
+ * @return EntryModel
+ */
+ private function getMockAuditLogModel()
+ {
+ $mock = $this->getMockBuilder('Craft\AuditLogModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__toString', '__get', 'setAttribute'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__toString')->willReturn('test');
+ $mock->expects($this->any())->method('__get')->will($this->returnCallback(function ($attribute) {
+ switch ($attribute) {
+ case 'before':
+ case 'after':
+ return array(
+ 'test' => array(
+ 'label' => 'test',
+ 'value' => 'test',
+ ),
+ );
+
+ default:
+ return 'test';
+ }
+ }));
+ $mock->expects($this->any())->method('setAttribute')->willReturn(true);
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldsService.
+ *
+ * @param string $handle
+ */
+ private function setMockFieldsService($handle)
+ {
+ $mock = $this->getMockBuilder('Craft\FieldsService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getFieldByHandle'))
+ ->getMock();
+
+ $field = $this->getMockFieldModel($handle);
+
+ $mock->expects($this->any())->method('getFieldByHandle')->willReturn($field);
+
+ $this->setComponent(craft(), 'fields', $mock);
+ }
+
+ /**
+ * Mock FieldModel.
+ *
+ * @param string $handle
+ *
+ * @return FieldModel
+ */
+ private function getMockFieldModel($handle)
+ {
+ $mock = $this->getMockBuilder('Craft\FieldsService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__get'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__get')->will($this->returnCallback(function ($attribute) use ($handle) {
+ return $handle == 'element' ? AuditLogModel::FieldTypeEntries : AuditLogModel::FieldTypeLightswitch;
+ }));
+
+ return $mock;
+ }
+}
diff --git a/tests/AuditLogTest.php b/tests/AuditLogTest.php
deleted file mode 100644
index e415690..0000000
--- a/tests/AuditLogTest.php
+++ /dev/null
@@ -1,48 +0,0 @@
-
- * @copyright Copyright (c) 2015, author
- * @license http://buildwithcraft.com/license Craft License Agreement
- *
- * @link http://github.com/boboldehampsink
- */
-class AuditLogTest extends BaseTest
-{
- /**
- * Load the plugin component.
- */
- public function setUp()
- {
-
- // Load plugins
- $pluginsService = craft()->getComponent('plugins');
- $pluginsService->loadPlugins();
- }
-
- /**
- * Test if viewing and parsing works.
- */
- public function testActionDownload()
- {
-
- // Get first log item
- $log = craft()->auditLog->view(1);
-
- // Only test if already set
- if ($log) {
-
- // $log is a model, want to break that down
- $result = craft()->auditLog->parseFieldData('title', $log);
-
- // Result is always a string
- $this->assertInternalType('string', $result);
- }
- }
-}
diff --git a/tests/AuditLog_CategoryServiceTest.php b/tests/AuditLog_CategoryServiceTest.php
new file mode 100644
index 0000000..f93c120
--- /dev/null
+++ b/tests/AuditLog_CategoryServiceTest.php
@@ -0,0 +1,390 @@
+
+ * @copyright Copyright (c) 2015, Bob Olde Hampsink
+ * @license MIT
+ *
+ * @link http://github.com/boboldehampsink
+ *
+ * @coversDefaultClass Craft\AuditLog_CategoryService
+ * @covers ::
+ */
+class AuditLog_CategoryServiceTest extends BaseTest
+{
+ /**
+ * {@inheritdoc}
+ */
+ public static function setUpBeforeClass()
+ {
+ // Set up parent
+ parent::setUpBeforeClass();
+
+ // Require dependencies
+ require_once __DIR__.'/../services/AuditLogService.php';
+ require_once __DIR__.'/../services/AuditLog_CategoryService.php';
+ require_once __DIR__.'/../records/AuditLogRecord.php';
+ }
+
+ /**
+ * Test onSaveCategory.
+ *
+ * @param CategoryModel $category
+ * @param bool $isNewCategory
+ *
+ * @covers ::onBeforeSaveCategory
+ * @covers ::onSaveCategory
+ * @covers ::fields
+ * @dataProvider provideSaveCategoryEvents
+ */
+ final public function testOnSaveCategory(CategoryModel $category, $isNewCategory)
+ {
+ AuditLogRecord::$db = $this->setMockDbConnection();
+
+ $this->setMockAuditLogService();
+ $this->setMockUserSessionService();
+ $this->setMockFieldsService();
+ $this->setMockLocalizationService();
+
+ $service = new AuditLog_CategoryService();
+ $event = new Event($service, array(
+ 'category' => $category,
+ 'isNewCategory' => $isNewCategory,
+ ));
+ $service->onBeforeSaveCategory($event);
+ $service->onSaveCategory($event);
+
+ $this->assertArrayHasKey('id', $service->after);
+ }
+
+ /**
+ * Test onBeforeDeleteCategory.
+ *
+ * @param CategoryModel $category
+ *
+ * @covers ::onBeforeDeleteCategory
+ * @covers ::fields
+ * @dataProvider provideSaveCategoryEvents
+ */
+ final public function testOnBeforeDeleteCategory(CategoryModel $category)
+ {
+ AuditLogRecord::$db = $this->setMockDbConnection();
+
+ $this->setMockAuditLogService();
+ $this->setMockUserSessionService();
+ $this->setMockFieldsService();
+
+ $service = new AuditLog_CategoryService();
+ $event = new Event($service, array(
+ 'category' => $category,
+ ));
+ $service->onBeforeDeleteCategory($event);
+
+ $this->assertArrayHasKey('id', $service->after);
+ }
+
+ /**
+ * Provide saveCategory events.
+ *
+ * @return array
+ */
+ final public function provideSaveCategoryEvents()
+ {
+ return array(
+ 'With new category' => array($this->getMockCategoryModel(), true),
+ 'Without new category' => array($this->getMockCategoryModel(), false),
+ );
+ }
+
+ /**
+ * Mock CategoryModel.
+ *
+ * @return CategoryModel
+ */
+ private function getMockCategoryModel()
+ {
+ $mock = $this->getMockBuilder('Craft\CategoryModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__get', 'getAttributes', 'getTitle', 'getGroup'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__get')->willReturn('test');
+ $mock->expects($this->any())->method('getAttributes')->willReturn(array(
+ array('id' => 'test'),
+ ));
+ $mock->expects($this->any())->method('getTitle')->willReturn('test');
+ $mock->expects($this->any())->method('getGroup')->will($this->returnSelf());
+
+ return $mock;
+ }
+
+ /**
+ * Mock AuditLogService.
+ */
+ private function setMockAuditLogService()
+ {
+ $mock = $this->getMockBuilder('Craft\AuditLogService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('elementHasChanged', 'parseFieldData'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('elementHasChanged')->willReturn(true);
+ $mock->expects($this->any())->method('parseFieldData')->willReturn('test');
+
+ $this->setComponent(craft(), 'auditLog', $mock);
+ }
+
+ /**
+ * Mock DbConnection.
+ *
+ * @return DbConnection
+ */
+ private function setMockDbConnection()
+ {
+ $mock = $this->getMockBuilder('Craft\DbConnection')
+ ->disableOriginalConstructor()
+ ->setMethods(array('createCommand', 'getSchema'))
+ ->getMock();
+ $mock->autoConnect = false; // Do not auto connect
+
+ $command = $this->getMockDbCommand($mock);
+ $schema = $this->getMockDbSchema($mock);
+
+ $mock->expects($this->any())->method('createCommand')->willReturn($command);
+ $mock->expects($this->any())->method('getSchema')->willReturn($schema);
+
+ return $mock;
+ }
+
+ /**
+ * Mock DbCommand.
+ *
+ * @param DbConnection $connection
+ *
+ * @return DbCommand
+ */
+ private function getMockDbCommand(DbConnection $connection)
+ {
+ $mock = $this->getMockBuilder('Craft\DbCommand')
+ ->setConstructorArgs(array($connection))
+ ->setMethods(array('execute', 'prepare', 'queryRow', 'queryAll'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('execute')->willReturn(true);
+ $mock->expects($this->any())->method('prepare')->willReturn(true);
+ $mock->expects($this->any())->method('queryRow')->willReturn(array('username' => 'test'));
+ $mock->expects($this->any())->method('queryAll')->willReturn(array(array('username' => 'test')));
+
+ return $mock;
+ }
+
+ /**
+ * Mock MysqlSchema.
+ *
+ * @param DbConncetion $connection
+ *
+ * @return MysqlSchema
+ */
+ private function getMockDbSchema(DbConnection $connection)
+ {
+ $mock = $this->getMockBuilder('Craft\MysqlSchema')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getTable', 'getCommandBuilder'))
+ ->getMock();
+
+ $table = new \CMysqlTableSchema();
+ $table->columns = array(
+ 'id' => new \CMysqlColumnSchema(),
+ 'userId' => new \CMysqlColumnSchema(),
+ 'type' => new \CMysqlColumnSchema(),
+ 'origin' => new \CMysqlColumnSchema(),
+ 'before' => new \CMysqlColumnSchema(),
+ 'after' => new \CMysqlColumnSchema(),
+ 'status' => new \CMysqlColumnSchema(),
+ 'type' => new \CMysqlColumnSchema(),
+ 'dateCreated' => new \CMysqlColumnSchema(),
+ 'dateUpdated' => new \CMysqlColumnSchema(),
+ 'uid' => new \CMysqlColumnSchema(),
+ );
+ $builder = $this->getMockCommandBuilder($connection, $mock);
+
+ $mock->expects($this->any())->method('getTable')->willReturn($table);
+ $mock->expects($this->any())->method('getCommandBuilder')->willReturn($builder);
+
+ return $mock;
+ }
+
+ /**
+ * Mock CdbCommandBuilder.
+ *
+ * @param DbConnection $connection
+ * @param MysqlSchema $schema
+ *
+ * @return \CdbCommandBuilder
+ */
+ private function getMockCommandBuilder(DbConnection $connection, MysqlSchema $schema)
+ {
+ $mock = $this->getMockBuilder('\CdbCommandBuilder')
+ ->disableOriginalConstructor()
+ ->setMethods(array('createInsertCommand', 'createPkCommand', 'createPkCriteria', 'createFindCommand', 'applyLimit', 'getSchema', 'getDbConnection', 'bindValues'))
+ ->getMock();
+
+ $command = $this->getMockDbCommand($connection);
+
+ $mock->expects($this->any())->method('createInsertCommand')->willReturn($command);
+ $mock->expects($this->any())->method('createPkCommand')->willReturn($command);
+ $mock->expects($this->any())->method('createPkCriteria')->willReturn($command);
+ $mock->expects($this->any())->method('createFindCommand')->willReturn($command);
+ $mock->expects($this->any())->method('getSchema')->willReturn($schema);
+ $mock->expects($this->any())->method('getDbConnection')->willReturn($connection);
+
+ return $mock;
+ }
+
+ /**
+ * Mock UserGroupModel.
+ *
+ * @return UserGroupModel
+ */
+ private function getMockUserGroupModel()
+ {
+ $mock = $this->getMockBuilder('Craft\UserGroupModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__toString'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__toString')->willReturn('test');
+
+ return $mock;
+ }
+
+ /**
+ * Mock UserSessionService.
+ */
+ private function setMockUserSessionService()
+ {
+ $mock = $this->getMockBuilder('Craft\UserSessionService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getUser'))
+ ->getMock();
+
+ $user = $this->getMockUserModel();
+
+ $mock->expects($this->any())->method('getUser')->willReturn($user);
+
+ $this->setComponent(craft(), 'userSession', $mock);
+ }
+
+ /**
+ * Mock UserModel.
+ *
+ * @return UserModel
+ */
+ private function getMockUserModel()
+ {
+ $mock = $this->getMockBuilder('Craft\UserModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__get'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__get')->willReturn('test');
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldsService.
+ */
+ private function setMockFieldsService()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldsService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getLayoutByType', 'getFieldByHandle', 'getAllFields'))
+ ->getMock();
+
+ $layout = $this->getMockFieldLayoutModel();
+ $field = $this->getMockFieldModel();
+
+ $mock->expects($this->any())->method('getLayoutByType')->willReturn($layout);
+ $mock->expects($this->any())->method('getFieldByHandle')->willReturn($field);
+ $mock->expects($this->any())->method('getAllFields')->willReturn(array($field));
+
+ $this->setComponent(craft(), 'fields', $mock);
+ }
+
+ /**
+ * Mock FieldLayoutModel.
+ *
+ * @return FieldLayoutModel
+ */
+ private function getMockFieldLayoutModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldLayoutModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getFields'))
+ ->getMock();
+
+ $fields = array($this->getMockFieldLayoutFieldModel());
+
+ $mock->expects($this->any())->method('getFields')->willReturn($fields);
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldLayoutFieldModel.
+ *
+ * @return FieldLayoutFieldModel
+ */
+ private function getMockFieldLayoutFieldModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldLayoutFieldModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getField'))
+ ->getMock();
+
+ $field = $this->getMockFieldModel();
+
+ $mock->expects($this->any())->method('getField')->willReturn($field);
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldModel.
+ *
+ * @return FieldModel
+ */
+ private function getMockFieldModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__get'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__get')->willReturn('test');
+
+ return $mock;
+ }
+
+ /**
+ * Mock LocalizationService.
+ */
+ private function setMockLocalizationService()
+ {
+ $mock = $this->getMockBuilder('Craft\LocalizationService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getPrimarySiteLocaleId'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('getPrimarySiteLocaleId')->willReturn('nl');
+
+ $this->setComponent(craft(), 'i18n', $mock);
+ }
+}
diff --git a/tests/AuditLog_EntryServiceTest.php b/tests/AuditLog_EntryServiceTest.php
new file mode 100644
index 0000000..7eaf3d1
--- /dev/null
+++ b/tests/AuditLog_EntryServiceTest.php
@@ -0,0 +1,398 @@
+
+ * @copyright Copyright (c) 2015, Bob Olde Hampsink
+ * @license MIT
+ *
+ * @link http://github.com/boboldehampsink
+ *
+ * @coversDefaultClass Craft\AuditLog_EntryService
+ * @covers ::
+ */
+class AuditLog_EntryServiceTest extends BaseTest
+{
+ /**
+ * {@inheritdoc}
+ */
+ public static function setUpBeforeClass()
+ {
+ // Set up parent
+ parent::setUpBeforeClass();
+
+ // Require dependencies
+ require_once __DIR__.'/../services/AuditLogService.php';
+ require_once __DIR__.'/../services/AuditLog_EntryService.php';
+ require_once __DIR__.'/../records/AuditLogRecord.php';
+ }
+
+ /**
+ * Test onSaveEntry.
+ *
+ * @param EntryModel $entry
+ * @param bool $isNewEntry
+ *
+ * @covers ::onBeforeSaveEntry
+ * @covers ::onSaveEntry
+ * @covers ::fields
+ * @dataProvider provideSaveEntryEvents
+ */
+ final public function testOnSaveEntry(EntryModel $entry, $isNewEntry)
+ {
+ AuditLogRecord::$db = $this->setMockDbConnection();
+
+ $this->setMockAuditLogService();
+ $this->setMockUserSessionService();
+ $this->setMockFieldsService();
+ $this->setMockLocalizationService();
+
+ $service = new AuditLog_EntryService();
+ $event = new Event($service, array(
+ 'entry' => $entry,
+ 'isNewEntry' => $isNewEntry,
+ ));
+ $service->onBeforeSaveEntry($event);
+ $service->onSaveEntry($event);
+
+ $this->assertArrayHasKey('id', $service->after);
+ }
+
+ /**
+ * Test onBeforeDeleteEntry.
+ *
+ * @param EntryModel $entry
+ *
+ * @covers ::onBeforeDeleteEntry
+ * @covers ::fields
+ * @dataProvider provideSaveEntryEvents
+ */
+ final public function testOnBeforeDeleteEntry(EntryModel $entry)
+ {
+ AuditLogRecord::$db = $this->setMockDbConnection();
+
+ $this->setMockAuditLogService();
+ $this->setMockUserSessionService();
+ $this->setMockFieldsService();
+
+ $service = new AuditLog_EntryService();
+ $event = new Event($service, array(
+ 'entry' => $entry,
+ ));
+ $service->onBeforeDeleteEntry($event);
+
+ $this->assertArrayHasKey('id', $service->after);
+ }
+
+ /**
+ * Provide saveEntry events.
+ *
+ * @return array
+ */
+ final public function provideSaveEntryEvents()
+ {
+ return array(
+ 'With new entry' => array($this->getMockEntryModel(), true),
+ 'Without new entry' => array($this->getMockEntryModel(), false),
+ );
+ }
+
+ /**
+ * Mock EntryModel.
+ *
+ * @return EntryModel
+ */
+ private function getMockEntryModel()
+ {
+ $mock = $this->getMockBuilder('Craft\EntryModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__get', 'getAttributes', 'getTitle', 'getSection', 'getType'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__get')->willReturn('test');
+ $mock->expects($this->any())->method('getAttributes')->willReturn(array(
+ array('id' => 'test'),
+ ));
+ $mock->expects($this->any())->method('getTitle')->willReturn('test');
+
+ // Doesn't really matter if we mock sections and types - returning self is enough
+ $mock->expects($this->any())->method('getSection')->will($this->returnSelf());
+ $mock->expects($this->any())->method('getType')->will($this->returnSelf());
+
+ return $mock;
+ }
+
+ /**
+ * Mock AuditLogService.
+ */
+ private function setMockAuditLogService()
+ {
+ $mock = $this->getMockBuilder('Craft\AuditLogService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('elementHasChanged', 'parseFieldData'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('elementHasChanged')->willReturn(true);
+ $mock->expects($this->any())->method('parseFieldData')->willReturn('test');
+
+ $this->setComponent(craft(), 'auditLog', $mock);
+ }
+
+ /**
+ * Mock DbConnection.
+ *
+ * @return DbConnection
+ */
+ private function setMockDbConnection()
+ {
+ $mock = $this->getMockBuilder('Craft\DbConnection')
+ ->disableOriginalConstructor()
+ ->setMethods(array('createCommand', 'getSchema'))
+ ->getMock();
+ $mock->autoConnect = false; // Do not auto connect
+
+ $command = $this->getMockDbCommand($mock);
+ $schema = $this->getMockDbSchema($mock);
+
+ $mock->expects($this->any())->method('createCommand')->willReturn($command);
+ $mock->expects($this->any())->method('getSchema')->willReturn($schema);
+
+ return $mock;
+ }
+
+ /**
+ * Mock DbCommand.
+ *
+ * @param DbConnection $connection
+ *
+ * @return DbCommand
+ */
+ private function getMockDbCommand(DbConnection $connection)
+ {
+ $mock = $this->getMockBuilder('Craft\DbCommand')
+ ->setConstructorArgs(array($connection))
+ ->setMethods(array('execute', 'prepare', 'queryRow', 'queryAll'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('execute')->willReturn(true);
+ $mock->expects($this->any())->method('prepare')->willReturn(true);
+ $mock->expects($this->any())->method('queryRow')->willReturn(array('authorId' => 1));
+ $mock->expects($this->any())->method('queryAll')->willReturn(array(array('authorId' => 1)));
+
+ return $mock;
+ }
+
+ /**
+ * Mock MysqlSchema.
+ *
+ * @param DbConncetion $connection
+ *
+ * @return MysqlSchema
+ */
+ private function getMockDbSchema(DbConnection $connection)
+ {
+ $mock = $this->getMockBuilder('Craft\MysqlSchema')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getTable', 'getCommandBuilder'))
+ ->getMock();
+
+ $table = new \CMysqlTableSchema();
+ $table->columns = array(
+ 'id' => new \CMysqlColumnSchema(),
+ 'userId' => new \CMysqlColumnSchema(),
+ 'type' => new \CMysqlColumnSchema(),
+ 'origin' => new \CMysqlColumnSchema(),
+ 'before' => new \CMysqlColumnSchema(),
+ 'after' => new \CMysqlColumnSchema(),
+ 'status' => new \CMysqlColumnSchema(),
+ 'type' => new \CMysqlColumnSchema(),
+ 'dateCreated' => new \CMysqlColumnSchema(),
+ 'dateUpdated' => new \CMysqlColumnSchema(),
+ 'uid' => new \CMysqlColumnSchema(),
+ );
+ $builder = $this->getMockCommandBuilder($connection, $mock);
+
+ $mock->expects($this->any())->method('getTable')->willReturn($table);
+ $mock->expects($this->any())->method('getCommandBuilder')->willReturn($builder);
+
+ return $mock;
+ }
+
+ /**
+ * Mock CdbCommandBuilder.
+ *
+ * @param DbConnection $connection
+ * @param MysqlSchema $schema
+ *
+ * @return \CdbCommandBuilder
+ */
+ private function getMockCommandBuilder(DbConnection $connection, MysqlSchema $schema)
+ {
+ $mock = $this->getMockBuilder('\CdbCommandBuilder')
+ ->disableOriginalConstructor()
+ ->setMethods(array('createInsertCommand', 'createPkCommand', 'createPkCriteria', 'createFindCommand', 'applyLimit', 'getSchema', 'getDbConnection', 'bindValues'))
+ ->getMock();
+
+ $command = $this->getMockDbCommand($connection);
+
+ $mock->expects($this->any())->method('createInsertCommand')->willReturn($command);
+ $mock->expects($this->any())->method('createPkCommand')->willReturn($command);
+ $mock->expects($this->any())->method('createPkCriteria')->willReturn($command);
+ $mock->expects($this->any())->method('createFindCommand')->willReturn($command);
+ $mock->expects($this->any())->method('getSchema')->willReturn($schema);
+ $mock->expects($this->any())->method('getDbConnection')->willReturn($connection);
+
+ return $mock;
+ }
+
+ /**
+ * Mock UserSessionService.
+ */
+ private function setMockUserSessionService()
+ {
+ $mock = $this->getMockBuilder('Craft\UserSessionService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getUser'))
+ ->getMock();
+
+ $user = $this->getMockUserModel();
+
+ $mock->expects($this->any())->method('getUser')->willReturn($user);
+
+ $this->setComponent(craft(), 'userSession', $mock);
+ }
+
+ /**
+ * Mock UserModel.
+ *
+ * @return UserModel
+ */
+ private function getMockUserModel()
+ {
+ $mock = $this->getMockBuilder('Craft\UserModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__get'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__get')->willReturn('test');
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldsService.
+ */
+ private function setMockFieldsService()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldsService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getLayoutByType', 'getFieldByHandle', 'getAllFields', 'getLayoutById'))
+ ->getMock();
+
+ $layout = $this->getMockFieldLayoutModel();
+ $field = $this->getMockFieldModel();
+
+ $mock->expects($this->any())->method('getLayoutByType')->willReturn($layout);
+ $mock->expects($this->any())->method('getFieldByHandle')->willReturn($field);
+ $mock->expects($this->any())->method('getAllFields')->willReturn(array($field));
+ $mock->expects($this->any())->method('getLayoutById')->willReturn($layout);
+
+ $this->setComponent(craft(), 'fields', $mock);
+ }
+
+ /**
+ * Mock FieldLayoutModel.
+ *
+ * @return FieldLayoutModel
+ */
+ private function getMockFieldLayoutModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldLayoutModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getFields', 'getTabs'))
+ ->getMock();
+
+ $fields = array($this->getMockFieldLayoutFieldModel());
+ $tabs = array($this->getMockFieldLayoutTabModel());
+
+ $mock->expects($this->any())->method('getFields')->willReturn($fields);
+ $mock->expects($this->any())->method('getTabs')->willReturn($tabs);
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldLayoutFieldModel.
+ *
+ * @return FieldLayoutFieldModel
+ */
+ private function getMockFieldLayoutFieldModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldLayoutFieldModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getField'))
+ ->getMock();
+
+ $field = $this->getMockFieldModel();
+
+ $mock->expects($this->any())->method('getField')->willReturn($field);
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldLayoutFieldModel.
+ *
+ * @return FieldLayoutFieldModel
+ */
+ private function getMockFieldLayoutTabModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldLayoutTabModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getFields'))
+ ->getMock();
+
+ $fields = array($this->getMockFieldLayoutFieldModel());
+
+ $mock->expects($this->any())->method('getFields')->willReturn($fields);
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldModel.
+ *
+ * @return FieldModel
+ */
+ private function getMockFieldModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__get'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__get')->willReturn('test');
+
+ return $mock;
+ }
+
+ /**
+ * Mock LocalizationService.
+ */
+ private function setMockLocalizationService()
+ {
+ $mock = $this->getMockBuilder('Craft\LocalizationService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getPrimarySiteLocaleId'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('getPrimarySiteLocaleId')->willReturn('nl');
+
+ $this->setComponent(craft(), 'i18n', $mock);
+ }
+}
diff --git a/tests/AuditLog_UserServiceTest.php b/tests/AuditLog_UserServiceTest.php
new file mode 100644
index 0000000..8b62178
--- /dev/null
+++ b/tests/AuditLog_UserServiceTest.php
@@ -0,0 +1,411 @@
+
+ * @copyright Copyright (c) 2015, Bob Olde Hampsink
+ * @license MIT
+ *
+ * @link http://github.com/boboldehampsink
+ *
+ * @coversDefaultClass Craft\AuditLog_UserService
+ * @covers ::
+ */
+class AuditLog_UserServiceTest extends BaseTest
+{
+ /**
+ * {@inheritdoc}
+ */
+ public static function setUpBeforeClass()
+ {
+ // Set up parent
+ parent::setUpBeforeClass();
+
+ // Require dependencies
+ require_once __DIR__.'/../services/AuditLogService.php';
+ require_once __DIR__.'/../services/AuditLog_UserService.php';
+ require_once __DIR__.'/../records/AuditLogRecord.php';
+ }
+
+ /**
+ * Test onSaveUser.
+ *
+ * @param UserModel $user
+ * @param bool $isNewUser
+ *
+ * @covers ::onBeforeSaveUser
+ * @covers ::onSaveUser
+ * @covers ::fields
+ * @dataProvider provideSaveUserEvents
+ */
+ final public function testOnSaveUser(UserModel $user, $isNewUser)
+ {
+ AuditLogRecord::$db = $this->setMockDbConnection();
+
+ $this->setMockAuditLogService();
+ $this->setMockUserGroupsService();
+ $this->setMockUserSessionService();
+ $this->setMockFieldsService();
+ $this->setMockLocalizationService();
+
+ $service = new AuditLog_UserService();
+ $event = new Event($service, array(
+ 'user' => $user,
+ 'isNewUser' => $isNewUser,
+ ));
+ $service->onBeforeSaveUser($event);
+ $service->onSaveUser($event);
+
+ $this->assertArrayHasKey('id', $service->after);
+ }
+
+ /**
+ * Test onBeforeDeleteUser.
+ *
+ * @param UserModel $user
+ *
+ * @covers ::onBeforeDeleteUser
+ * @covers ::fields
+ * @dataProvider provideSaveUserEvents
+ */
+ final public function testOnBeforeDeleteUser(UserModel $user)
+ {
+ AuditLogRecord::$db = $this->setMockDbConnection();
+
+ $this->setMockAuditLogService();
+ $this->setMockUserGroupsService();
+ $this->setMockUserSessionService();
+ $this->setMockFieldsService();
+
+ $service = new AuditLog_UserService();
+ $event = new Event($service, array(
+ 'user' => $user,
+ ));
+ $service->onBeforeDeleteUser($event);
+
+ $this->assertArrayHasKey('id', $service->after);
+ }
+
+ /**
+ * Provide saveUser events.
+ *
+ * @return array
+ */
+ final public function provideSaveUserEvents()
+ {
+ return array(
+ 'With new user' => array($this->getMockUserModel(), true),
+ 'Without new user' => array($this->getMockUserModel(), false),
+ 'With posted groups' => call_user_func(function () {
+ $this->setMockRequestService();
+
+ return array($this->getMockUserModel(), false);
+ }),
+ );
+ }
+
+ /**
+ * Mock RequestService.
+ */
+ private function setMockRequestService()
+ {
+ $mock = $this->getMockBuilder('Craft\HttpRequestService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getPost'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('getPost')->willReturn(array(1, 2));
+
+ $this->setComponent(craft(), 'request', $mock);
+ }
+
+ /**
+ * Mock UserModel.
+ *
+ * @return UserModel
+ */
+ private function getMockUserModel()
+ {
+ $mock = $this->getMockBuilder('Craft\UserModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__get', 'getAttributes'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__get')->willReturn('test');
+ $mock->expects($this->any())->method('getAttributes')->willReturn(array(
+ array('id' => 'test'),
+ ));
+
+ return $mock;
+ }
+
+ /**
+ * Mock AuditLogService.
+ */
+ private function setMockAuditLogService()
+ {
+ $mock = $this->getMockBuilder('Craft\AuditLogService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('elementHasChanged', 'parseFieldData'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('elementHasChanged')->willReturn(true);
+ $mock->expects($this->any())->method('parseFieldData')->willReturn('test');
+
+ $this->setComponent(craft(), 'auditLog', $mock);
+ }
+
+ /**
+ * Mock DbConnection.
+ *
+ * @return DbConnection
+ */
+ private function setMockDbConnection()
+ {
+ $mock = $this->getMockBuilder('Craft\DbConnection')
+ ->disableOriginalConstructor()
+ ->setMethods(array('createCommand', 'getSchema'))
+ ->getMock();
+ $mock->autoConnect = false; // Do not auto connect
+
+ $command = $this->getMockDbCommand($mock);
+ $schema = $this->getMockDbSchema($mock);
+
+ $mock->expects($this->any())->method('createCommand')->willReturn($command);
+ $mock->expects($this->any())->method('getSchema')->willReturn($schema);
+
+ return $mock;
+ }
+
+ /**
+ * Mock DbCommand.
+ *
+ * @param DbConnection $connection
+ *
+ * @return DbCommand
+ */
+ private function getMockDbCommand(DbConnection $connection)
+ {
+ $mock = $this->getMockBuilder('Craft\DbCommand')
+ ->setConstructorArgs(array($connection))
+ ->setMethods(array('execute', 'prepare', 'queryRow', 'queryAll'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('execute')->willReturn(true);
+ $mock->expects($this->any())->method('prepare')->willReturn(true);
+ $mock->expects($this->any())->method('queryRow')->willReturn(array('username' => 'test'));
+ $mock->expects($this->any())->method('queryAll')->willReturn(array(array('username' => 'test')));
+
+ return $mock;
+ }
+
+ /**
+ * Mock MysqlSchema.
+ *
+ * @param DbConncetion $connection
+ *
+ * @return MysqlSchema
+ */
+ private function getMockDbSchema(DbConnection $connection)
+ {
+ $mock = $this->getMockBuilder('Craft\MysqlSchema')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getTable', 'getCommandBuilder'))
+ ->getMock();
+
+ $table = new \CMysqlTableSchema();
+ $table->columns = array(
+ 'id' => new \CMysqlColumnSchema(),
+ 'userId' => new \CMysqlColumnSchema(),
+ 'type' => new \CMysqlColumnSchema(),
+ 'origin' => new \CMysqlColumnSchema(),
+ 'before' => new \CMysqlColumnSchema(),
+ 'after' => new \CMysqlColumnSchema(),
+ 'status' => new \CMysqlColumnSchema(),
+ 'type' => new \CMysqlColumnSchema(),
+ 'dateCreated' => new \CMysqlColumnSchema(),
+ 'dateUpdated' => new \CMysqlColumnSchema(),
+ 'uid' => new \CMysqlColumnSchema(),
+ );
+ $builder = $this->getMockCommandBuilder($connection, $mock);
+
+ $mock->expects($this->any())->method('getTable')->willReturn($table);
+ $mock->expects($this->any())->method('getCommandBuilder')->willReturn($builder);
+
+ return $mock;
+ }
+
+ /**
+ * Mock CdbCommandBuilder.
+ *
+ * @param DbConnection $connection
+ * @param MysqlSchema $schema
+ *
+ * @return \CdbCommandBuilder
+ */
+ private function getMockCommandBuilder(DbConnection $connection, MysqlSchema $schema)
+ {
+ $mock = $this->getMockBuilder('\CdbCommandBuilder')
+ ->disableOriginalConstructor()
+ ->setMethods(array('createInsertCommand', 'createPkCommand', 'createPkCriteria', 'createFindCommand', 'applyLimit', 'getSchema', 'getDbConnection', 'bindValues'))
+ ->getMock();
+
+ $command = $this->getMockDbCommand($connection);
+
+ $mock->expects($this->any())->method('createInsertCommand')->willReturn($command);
+ $mock->expects($this->any())->method('createPkCommand')->willReturn($command);
+ $mock->expects($this->any())->method('createPkCriteria')->willReturn($command);
+ $mock->expects($this->any())->method('createFindCommand')->willReturn($command);
+ $mock->expects($this->any())->method('getSchema')->willReturn($schema);
+ $mock->expects($this->any())->method('getDbConnection')->willReturn($connection);
+
+ return $mock;
+ }
+
+ /**
+ * Mock UserGroupsService.
+ */
+ private function setMockUserGroupsService()
+ {
+ $mock = $this->getMockBuilder('Craft\UserGroupsService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getGroupsByUserId', 'getGroupById'))
+ ->getMock();
+
+ $group = $this->getMockUserGroupModel();
+
+ $mock->expects($this->any())->method('getGroupsByUserId')->willReturn(array($group));
+ $mock->expects($this->any())->method('getGroupById')->willReturn($group);
+
+ $this->setComponent(craft(), 'userGroups', $mock);
+ }
+
+ /**
+ * Mock UserGroupModel.
+ *
+ * @return UserGroupModel
+ */
+ private function getMockUserGroupModel()
+ {
+ $mock = $this->getMockBuilder('Craft\UserGroupModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__toString'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__toString')->willReturn('test');
+
+ return $mock;
+ }
+
+ /**
+ * Mock UserSessionService.
+ */
+ private function setMockUserSessionService()
+ {
+ $mock = $this->getMockBuilder('Craft\UserSessionService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getUser'))
+ ->getMock();
+
+ $user = $this->getMockUserModel();
+
+ $mock->expects($this->any())->method('getUser')->willReturn($user);
+
+ $this->setComponent(craft(), 'userSession', $mock);
+ }
+
+ /**
+ * Mock FieldsService.
+ */
+ private function setMockFieldsService()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldsService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getLayoutByType', 'getFieldByHandle', 'getAllFields'))
+ ->getMock();
+
+ $layout = $this->getMockFieldLayoutModel();
+ $field = $this->getMockFieldModel();
+
+ $mock->expects($this->any())->method('getLayoutByType')->willReturn($layout);
+ $mock->expects($this->any())->method('getFieldByHandle')->willReturn($field);
+ $mock->expects($this->any())->method('getAllFields')->willReturn(array($field));
+
+ $this->setComponent(craft(), 'fields', $mock);
+ }
+
+ /**
+ * Mock FieldLayoutModel.
+ *
+ * @return FieldLayoutModel
+ */
+ private function getMockFieldLayoutModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldLayoutModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getFields'))
+ ->getMock();
+
+ $fields = array($this->getMockFieldLayoutFieldModel());
+
+ $mock->expects($this->any())->method('getFields')->willReturn($fields);
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldLayoutFieldModel.
+ *
+ * @return FieldLayoutFieldModel
+ */
+ private function getMockFieldLayoutFieldModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldLayoutFieldModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getField'))
+ ->getMock();
+
+ $field = $this->getMockFieldModel();
+
+ $mock->expects($this->any())->method('getField')->willReturn($field);
+
+ return $mock;
+ }
+
+ /**
+ * Mock FieldModel.
+ *
+ * @return FieldModel
+ */
+ private function getMockFieldModel()
+ {
+ $mock = $this->getMockBuilder('Craft\FieldModel')
+ ->disableOriginalConstructor()
+ ->setMethods(array('__get'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('__get')->willReturn('test');
+
+ return $mock;
+ }
+
+ /**
+ * Mock LocalizationService.
+ */
+ private function setMockLocalizationService()
+ {
+ $mock = $this->getMockBuilder('Craft\LocalizationService')
+ ->disableOriginalConstructor()
+ ->setMethods(array('getPrimarySiteLocaleId'))
+ ->getMock();
+
+ $mock->expects($this->any())->method('getPrimarySiteLocaleId')->willReturn('nl');
+
+ $this->setComponent(craft(), 'i18n', $mock);
+ }
+}