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('
{{ getCsrfInput() }}
').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); + } +}