/ */ class Workflow_Test_ContentControllerTest extends ModuleControllerTest { /** * Test successful changing workflow state on multiple entries. */ public function testChangeStateSuccess() { $this->utility->impersonate('administrator'); // create workflows, content types and entries $this->_createWorkflowsAndTypes(3); // change workflow state to review on some entries $ids = array('simple1', 'simple3'); // dispatch $this->request->setMethod('POST'); $this->request->setPost( array( 'workflows' => array('simple'), 'ids' => $ids, 'state' => 'review', 'scheduled' => 'false' ) ); $this->dispatch('/workflow/content/change-state/format/json'); $this->assertModule('workflow', __LINE__ .': Last module run should be workflow module.'); $this->assertController('content', __LINE__ .': Expected controller'); $this->assertAction('change-state', __LINE__ .': Expected action'); // verify json response $this->_verifyChangeStateJsonResponse($ids); // verify all entries have given workflow state and they have been changes in one chagelist $this->_verifyEntriesWorkflow($ids, 'review'); // change workflow state to published for contents of simple and basic type $this->resetRequest()->resetResponse(); $ids = $this->_getContentsByType(array('simple', 'basic'))->invoke('getId'); // dispatch $this->request->setMethod('POST'); $this->request->setPost( array( 'workflows' => array('simple', 'basic'), 'ids' => $ids, 'state' => 'published', 'scheduled' => 'false' ) ); $this->dispatch('/workflow/content/change-state/format/json'); $this->assertModule('workflow', __LINE__ .': Last module run should be workflow module.'); $this->assertController('content', __LINE__ .': Expected controller'); $this->assertAction('change-state', __LINE__ .': Expected action'); // verify json response $this->_verifyChangeStateJsonResponse($ids); // verify all entries have given workflow state and they have been changes in one chagelist $this->_verifyEntriesWorkflow($ids, 'published'); } /** * Test changing workflow state on multiple entries with some entries not under workflow. */ public function testChangeStateNoWorkflow() { $this->utility->impersonate('administrator'); // create workflows, content types and entries $this->_createWorkflowsAndTypes(1); // change workflow state to review for contents of simple and basic type $ids = array('simple1', 'no-workflow1'); // dispatch $this->request->setMethod('POST'); $this->request->setPost( array( 'workflows' => array('simple'), 'ids' => $ids, 'state' => 'review', 'scheduled' => 'false' ) ); $this->dispatch('/workflow/content/change-state/format/json'); $this->assertModule('workflow', __LINE__ .': Last module run should be workflow module.'); $this->assertController('content', __LINE__ .': Expected controller'); $this->assertAction('change-state', __LINE__ .': Expected action'); $response = Zend_Json::decode($this->response->getBody()); // verify json response $this->_verifyChangeStateJsonResponse(array(), array('no-workflow1')); // ensure simple1 has not been changed $entry = P4Cms_Content::fetch('simple1'); $simpleWorkflow = Workflow_Model_Workflow::fetchByContent($entry); $this->assertSame( 'draft', $simpleWorkflow->getStateOf($entry)->getId(), "Expected state of the simple1 entry." ); } /** * Test changing workflow state on multiple entries where not all entries can be changed. */ public function testChangeStateFail() { $this->utility->impersonate('administrator'); // create workflows, content types and entries $this->_createWorkflowsAndTypes(3); // change workflow state to published on entries of simple and private type // this should not work as draft->published transition in private workflow is // not allowed due to a 'False' condition $ids = $this->_getContentsByType(array('simple', 'private'))->invoke('getId'); // dispatch $this->request->setMethod('POST'); $this->request->setPost( array( 'workflows' => array('simple', 'private'), 'ids' => $ids, 'state' => 'published', 'scheduled' => 'false' ) ); $this->dispatch('/workflow/content/change-state/format/json'); $this->assertModule('workflow', __LINE__ .': Last module run should be workflow module.'); $this->assertController('content', __LINE__ .': Expected controller'); $this->assertAction('change-state', __LINE__ .': Expected action'); $response = Zend_Json::decode($this->response->getBody()); // verify json response $this->_verifyChangeStateJsonResponse( array(), $this->_getContentsByType('private')->invoke('getId') ); // ensure simple entries were not changed $simpleWorkflow = Workflow_Model_Workflow::fetch('simple'); foreach ($this->_getContentsByType('simple') as $entry) { $this->assertSame( 'draft', $simpleWorkflow->getStateOf($entry)->getId(), "Expected simple entries were not changed." ); } } /** * Test changing workflow state on multiple entries with using 'forced' parameter. */ public function testChangeStateForce() { $this->utility->impersonate('administrator'); // create workflows, content types and entries $this->_createWorkflowsAndTypes(3); // change workflow state to published on entries of simple and private type by force // it should change only entries of simple type and leave untouched entries of private // type $ids = $this->_getContentsByType(array('simple', 'private'))->invoke('getId'); // dispatch $this->request->setMethod('POST'); $this->request->setPost( array( 'workflows' => array('simple', 'private'), 'ids' => $ids, 'state' => 'published', 'scheduled' => 'false' ) ); $this->dispatch('/workflow/content/change-state/format/json/forced/true'); $this->assertModule('workflow', __LINE__ .': Last module run should be workflow module.'); $this->assertController('content', __LINE__ .': Expected controller'); $this->assertAction('change-state', __LINE__ .': Expected action'); $response = Zend_Json::decode($this->response->getBody()); $simpleEntries = $this->_getContentsByType('simple'); $privateEntries = $this->_getContentsByType('private'); // verify json response $this->_verifyChangeStateJsonResponse( $simpleEntries->invoke('getId'), array() ); // ensure simple entries were changed (in a single change) $this->_verifyEntriesWorkflow($simpleEntries->invoke('getId'), 'published'); // ensure private entries were not changed $privateWorkflow = Workflow_Model_Workflow::fetch('private'); foreach ($privateEntries as $entry) { $this->assertSame( 'draft', $privateWorkflow->getStateOf($entry)->getId(), "Expected private entries were not changed." ); } } /** * Helper function to return list of entries having given content type(s). * * @param string|array $types list of content types * @return P4Cms_Model_Iterator list of entries having given content type(s) */ public function _getContentsByType($types) { $types = array ($types); // create record filter to keep only entries with given types $filter = new P4Cms_Record_Filter; foreach ($types as $type) { $filter->add( 'contentType', $type, P4Cms_Record_Filter::COMPARE_EQUAL, P4Cms_Record_Filter::CONNECTIVE_OR ); } $query = P4Cms_Record_Query::create()->addFilter($filter); return P4Cms_Content::fetchAll($query); } /** * Helper function to verify json response of changed-state action. * * @param array $changedEntries list of expected entry ids that have been changed * @param array $failedEntries list of expected entry ids that have failed * @param string|null $errors expected errors */ public function _verifyChangeStateJsonResponse( array $changedEntries, array $failedEntries = array(), $errors = null ) { $response = Zend_Json::decode($this->response->getBody()); $this->assertSame( $changedEntries, $response['changedEntries'], "Expected list of changed entries." ); $this->assertSame( $failedEntries, $response['failedEntries'], "Expected list of failed entries." ); $this->assertSame( $errors, $response['errors'], "Expected errors message." ); } /** * Helper function, verify that all given entries (specified by their ids) * have given workflow state. Also verifies, that they have been submitted * in one change list. * * @param array $entryIds list with entry ids to verify * @param string $state workflow state assumed to be the current * entries state of workflow * @param string|null $scheduledState optional, workflow state assumed to be * scheduled for the entries, if null then * it won't be checked * @param string|null $scheduledTime optional, timestamp of scheduled transition * for given entries */ public function _verifyEntriesWorkflow( array $entryIds, $state, $scheduledState = null, $scheduledTime = null ) { $workflowsByType = Workflow_Model_Workflow::fetchTypeMap(); // ensure all entries have given state and they have been submitted in the same changelist $entryChanges = array(); $entries = P4Cms_Content::fetchAll(array('ids' => $entryIds)); foreach ($entries as $entry) { $type = $entry->getContentTypeId(); if (!isset($workflowsByType[$type])) { $this->fail("Unexpected entry with no workflow."); } $workflow = $workflowsByType[$type]; $this->assertSame( $state, $workflow->getStateOf($entry)->getId(), "Expected entry has '$state' workflow state." ); if ($scheduledState) { $this->assertSame( $scheduledState, $workflow->getScheduledStateOf($entry)->getId(), "Expected entry has '$scheduledState' as scheduled workflow state." ); } if ($scheduledTime) { $this->assertSame( $scheduledTime, $workflow->getScheduledTimeOf($entry), "Expected scheduled time of the scheduled workflow transition." ); } $entryChanges[] = $entry->toP4File()->getChange()->getId(); } // ensure that entryChangelist contains just one unique value $this->assertSame( 1, count(array_unique($entryChanges)), "Expected all entries have been submitted in the same changelist." ); } /** * Helper function, creates workflows, content types (one for each workflow + * one with no workflow) and optionally specified number of entries for each * content type, ids are derived from content type ids with appended number 1 * to . * * @param int $entriesPerType optional, number entries to create for each * content type */ protected function _createWorkflowsAndTypes($entriesPerType = 0) { // create workflows with following states (and labels): // simple : draft (Draft), review (Review), published (Published) // basic : draft (Preliminary), published (Live) // private : draft (Private), published (Public), unable to transit from draft to published Workflow_Model_Workflow::store( array( 'id' => 'simple', 'states' => " [draft] label = 'Draft' transitions.review.label = 'Promote to reviw' transitions.published.label = 'Publish' [review] label = 'Review' transitions.draft.label = 'Demote to Draft' transitions.published.label = 'Publish' [published] label = 'Published' transitions.review.label = 'Demote to Review' transitions.draft.label = 'Demote to Draft'" ) ); Workflow_Model_Workflow::store( array( 'id' => 'basic', 'states' => " [draft] label = 'Preliminary' transitions.published.label = 'Go Live' [published] label = 'Live' transitions.draft.label = 'Demote to Preliminary'" ) ); Workflow_Model_Workflow::store( array( 'id' => 'private', 'states' => " [draft] label = 'Private' transitions.published.label = 'Publish' transitions.published.conditions[] = 'False' [published] label = 'Public' transitions.draft.label = 'Demote to Private'" ) ); // create content types, one for each workflow + one with no workflow foreach (Workflow_Model_Workflow::fetchAll() as $workflow) { P4Cms_Content_Type::store( array( 'id' => $workflow->getId(), 'elements' => array( 'title' => array( 'type' => 'text' ) ), 'workflow' => $workflow->getId() ) ); } P4Cms_Content_Type::store( array( 'id' => 'no-workflow', 'elements' => array( 'title' => array( 'type' => 'text' ) ) ) ); // create content if ($entriesPerType) { $entry = P4Cms_Content::create( array('title' => 'test') ); foreach (P4Cms_Content_Type::fetchAll() as $type) { $typeId = $type->getId(); $entry->setContentType($typeId); for ($i = 1; $i <= $entriesPerType; $i++) { $entry->setId($typeId . $i); $entry->save(); } } } } }