ManageControllerTest.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • application/
  • category/
  • tests/
  • ManageControllerTest.php
  • View
  • Commits
  • Open Download .zip Download (29 KB)
<?php
/**
 * Test the category manage controller.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class Category_Test_ManageControllerTest extends ModuleControllerTest
{
    /**
     * Test the index action.
     */
    public function testIndex()
    {
        $this->utility->impersonate('editor');

        $this->dispatch('/category/manage/index');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('index', 'Expected action');

        // verify that table and dojo data elements exist
        $this->assertXpath('//div[@dojotype="dojox.data.QueryReadStore"]', 'Expected dojo.data div');
        $this->assertXpath(
            '//table[@dojotype="p4cms.ui.grid.DataGrid" and @jsid="p4cms.category.grid.instance"]',
            'Expected dojox.grid table'
        );

        // verify add button appears
        $this->assertXpath('//button[@class="add-button"]', 'Expected category add link.');

        // create several categories.
        $items = array();
        for ($i = 1; $i <= 10; $i++) {
            $values = array(
                'id'            => "test-cat-$i",
                'title'         => "Test Category $i",
                'description'   => "a category for testing #$i",
            );
            Category_Model_Category::store($values);
            $items[] = $values;
        }

        // check JSON output
        $this->resetRequest()
             ->resetResponse();
        $this->dispatch('/category/manage/format/json');
        $body = $this->response->getBody();
        $this->assertModule('category', 'Expected module, dispatch #2. '. $body);
        $this->assertController('manage', 'Expected controller, dispatch #2 '. $body);
        $this->assertAction('index', 'Expected action, dispatch #2 '. $body);

        $data = Zend_Json::decode($body);

        // verify number of items
        $this->assertSame(
            count($items),
            $data['numRows'],
            'Expected number of items'
        );

        // verify item values
        reset($items);
        foreach ($data['items'] as $item) {
            $this->assertSame(
                current($items),
                array_intersect($item, current($items)),
                'Expected item values'
            );
            next($items);
        }
    }

    /**
     * Test add- with a conflicting title.
     */
    public function testAddConflictingTitle()
    {
        $this->utility->impersonate('author');

        // create a category to show up in the parent select
        Category_Model_Category::store(
            array(
                'id'            => 'foo',
                'title'         => 'Foobulous',
                'description'   => 'Foobulous'
            )
        );

        // form request with appropriate fields.
        $this->request->setMethod('POST');
        $this->request->setPost('title',       'Foo');
        $this->request->setPost('parent',      '/');
        $this->request->setPost(P4Cms_Form::CSRF_TOKEN_NAME, P4Cms_Form::getCsrfToken());
        $this->dispatch('/category/manage/add');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('add', 'Expected action');

        // check for form w. errors.
        $this->assertQuery('form.category-form', 'Expected add form.');
        $this->assertQueryCount('ul.errors', 1, 'Expected one error.');
    }

    /**
     * Test add with a good post w. JSON
     */
    public function testAddGoodPostJson()
    {
        $this->utility->impersonate('author');

        // form request without required fields.
        $title = 'Good Category';
        $this->request->setMethod('POST');
        $this->request->setPost('title',  $title);
        $this->request->setPost('parent', '');
        $this->request->setPost('format', 'json');
        $this->request->setPost(P4Cms_Form::CSRF_TOKEN_NAME, P4Cms_Form::getCsrfToken());
        
        $this->dispatch('/category/manage/add');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('add', 'Expected action');

        // check for saved category entry.
        $id = 'good-category';
        $this->assertTrue(Category_Model_Category::exists($id), 'Expected category to be saved.');
        $category = Category_Model_Category::fetch($id);
        $this->assertSame(
            $title,
            $category->getValue('title'),
            'Expected same title as was posted.'
        );

        $body = $this->response->getBody();
        $this->assertResponseCode(200, 'Expected success response code for post');
        $data = json_decode($body, true);

        // verify that arrays of category in json and saved category are same (items order can differ)
        $this->assertSame(
            count($data['category']),
            count($category->toArray()),
            'Expected number of items in category in json and in saved category are the same.'
        );
        $this->assertSame(
            array(),
            array_diff($category->toArray(), $data['category']),
            'Expected items in category in json response and in saved category are the same.'
        );
    }

    /**
     * Test the add action without a post.
     */
    public function testAddNoPost()
    {
        $this->utility->impersonate('author');

        // create a category to show up in the parent select
        Category_Model_Category::store(
            array(
                'id'            => 'foo',
                'title'         => 'Foo',
                'description'   => 'Foobulous'
            )
        );

        $this->dispatch('/category/manage/add');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('add', 'Expected action');

        // ensure that form inputs are presented correctly.
        $this->assertQuery('form.category-form', 'Expected add form.');
        $this->assertQuery('select[name="parent"]', 'Expected handler select input.');
        $this->assertQueryCount('select[name="parent"] option', 2, 'Expected two parents.');
        $this->assertQuery('input[name="title"]', 'Expected label input.');
        $this->assertQuery('textarea[name="description"]', 'Expected description input.');
        $this->assertQuery('input[type="submit"]', 'Expected submit button.');

        // ensure labels are present.
        $labels = array(
            'title'       => 'Title',
            'parent'      => 'Parent',
            'description' => 'Description',
        );
        foreach ($labels as $field => $label) {
            $this->assertQueryContentContains("label[for='$field']", $label, 'Expected $field label.');
        }
    }

    /**
     * Test add with a post missing several required fields.
     */
    public function testAddBadPost()
    {
        $this->utility->impersonate('author');

        // form request without required fields.
        $this->request->setMethod('POST');
        $this->request->setPost('description', 'test description');
        $this->request->setPost(P4Cms_Form::CSRF_TOKEN_NAME, P4Cms_Form::getCsrfToken());
        $this->dispatch('/category/manage/add');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('add', 'Expected action');

        // check for form w. errors.
        $this->assertQuery('form.category-form', 'Expected add form.');
        $this->assertQueryCount('ul.errors', 1, 'Expected matching number of errors.');

        // ensure description value was preserved.
        $this->assertQueryContentContains('textarea', 'test description');
    }

    /**
     * Test add with a bad parent.
     */
    public function testAddBadParentPost()
    {
        $this->utility->impersonate('author');

        // form request without required fields.
        $this->request->setMethod('POST');
        $this->request->setPost('title',       'Test Category');
        $this->request->setPost('parent',      'lasdjkfasdklf');
        $this->request->setPost('description', 'a description');
        $this->request->setPost(P4Cms_Form::CSRF_TOKEN_NAME, P4Cms_Form::getCsrfToken());
        $this->dispatch('/category/manage/add');

        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('add', 'Expected action');

        // check for form w. errors.
        $this->assertQuery('form.category-form', 'Expected add form.');
        $this->assertQueryCount('ul.errors', 1, 'Expected one error.');

        $this->assertQueryContentContains('li', "'lasdjkfasdklf' is not a valid parent category.");

        // ensure elements value was preserved.
        $this->assertQueryContentContains('textarea', 'a description');
    }

    /**
     * Test add with a good post.
     */
    public function testGoodAddPost()
    {
        $this->utility->impersonate('author');

        // form request without required fields.
        $title       = 'Good Category';
        $id          = 'good-category';
        $description = 'a description';
        $this->request->setMethod('POST');
        $this->request->setPost('title',       $title);
        $this->request->setPost('parent',      '');
        $this->request->setPost('description', $description);
        $this->request->setPost(P4Cms_Form::CSRF_TOKEN_NAME, P4Cms_Form::getCsrfToken());

        $this->dispatch('/category/manage/add');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('add', 'Expected action');

        // check for saved category entry.
        $this->assertTrue(Category_Model_Category::exists($id), 'Expected category to be saved.');
        $category = Category_Model_Category::fetch($id);
        $this->assertSame(
            $title,
            $category->getValue('title'),
            'Expected same title as was posted.'
        );
        $this->assertSame(
            $description,
            $category->getValue('description'),
            'Expected same description as was posted.'
        );

        $this->resetRequest()
             ->resetResponse();

         // test that id must be unique (can't add same category twice).
        $this->request->setMethod('POST');
        $this->request->setPost('title',       $title);
        $this->request->setPost('parent',      '');
        $this->request->setPost('description', $description);
        $this->request->setPost(P4Cms_Form::CSRF_TOKEN_NAME, P4Cms_Form::getCsrfToken());
        $this->dispatch('/category/manage/add');
        $this->assertQueryCount('ul.errors', 1, 'Expected id error.');

        $this->resetRequest()
             ->resetResponse();

        // test that the added category appears as a parent
        $this->dispatch('/category/manage/add');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('add', 'Expected action');

        $this->assertQueryContentContains('option', $title);
    }

    /**
     * Test edit with no post.
     */
    public function testEditNoPost()
    {
        $this->utility->impersonate('editor');

        // test editing an id that does not exist
        $id = 'editme/subcat';
        $this->request->setParam('category', $id);
        $this->dispatch('/category/manage/edit');
        $this->assertModule('error', 'Expected module.');
        $this->assertController('index', 'Expected controller');
        $this->assertAction('error', 'Expected action');

        $this->resetRequest()->resetResponse();

        // test editing an id that does exist.
        Category_Model_Category::store(array('id' => 'editme', 'title' => 'Edit Me'));
        Category_Model_Category::store(array('id' => 'editme/subcat', 'title' => 'subCat', 'description' => 'docs'));
        $this->request->setParam('category', $id);
        $this->dispatch('/category/manage/edit');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('edit', 'Expected action');

        $this->assertQueryContentContains('select[name="parent"] option', 'Edit Me', 'Expected parent input.');
        $this->assertXpath('//input[@name="title"][@value="subCat"]', 'Expected title input.');
        $this->assertQueryContentContains('textarea[name="description"]', 'docs', 'Expected description input.');
    }

    /**
     * Test edit with bad post.
     */
    public function testEditBadPost()
    {
        $this->utility->impersonate('editor');

        // create category to be edited.
        Category_Model_Category::store(array('id' => 'edit-me', 'title' => 'Edit Me'));
        Category_Model_Category::store(array('id' => 'edit-me/subcat', 'title' => 'subCat', 'description' => 'docs'));

        // form request without required field (elements).
        $this->request->setParam('category', 'edit-me/subcat');
        $this->request->setMethod('POST');
        $this->request->setPost('title',       '');
        $this->request->setPost('description', 'changed');
        $this->request->setPost(P4Cms_Form::CSRF_TOKEN_NAME, P4Cms_Form::getCsrfToken());

        $this->dispatch('/category/manage/edit');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('edit', 'Expected action');

        // check for form w. errors.
        $this->assertQuery('form', 'Expected edit form.');
        $this->assertQueryCount('ul.errors', 1, 'Expected one error.');

        // ensure label value was preserved.
        $this->assertQuery('//input[@name="title"][@value=""]', 'Expect title to be changed.');
    }


    /**
     * Test good post to edit.
     */
    public function testGoodEditPost()
    {
        $this->utility->impersonate('editor');

        // create category to be edited.
        Category_Model_Category::store(array('id' => 'edit-me', 'title' => 'Edit Me'));
        Category_Model_Category::store(array('id' => 'edit-me/subcat', 'title' => 'subCat', 'description' => 'docs'));

        // form request without required fields.
        $this->request->setParam('category', 'edit-me/subcat');
        $this->request->setMethod('POST');
        $this->request->setPost('title',       'Test Category');
        $this->request->setPost('parent',      'edit-me');
        $this->request->setPost('description', 'new docs');
        $this->request->setPost(P4Cms_Form::CSRF_TOKEN_NAME, P4Cms_Form::getCsrfToken());

        $this->dispatch('/category/manage/edit');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('edit', 'Expected action');

        // check for saved category entry.
        $this->assertTrue(
            Category_Model_Category::exists('edit-me/test-category'),
            'Expected edited category to be saved.'
        );
        $this->assertFalse(
            Category_Model_Category::exists('edit-me/subcat'),
            'Expected original category to be gone.'
        );
        $category = Category_Model_Category::fetch('edit-me/test-category');
        $this->assertSame(
            'Test Category',
            $category->getValue('title'),
            "Expected same title as was posted."
        );
        $this->assertSame(
            'new docs',
            $category->getValue('description'),
            "Expected same description as was posted."
        );
    }

    /**
     * Test deleting.
     */
    public function testDelete()
    {
        $this->utility->impersonate('editor');

        // create category to be edited.
        Category_Model_Category::store(array('id' => 'editme', 'title' => 'Edit Me'));
        Category_Model_Category::store(array('id' => 'editme/subcat', 'title' => 'subCat', 'description' => 'docs'));

        $this->request->setMethod('POST');
        $this->request->setParam('category', 'editme');
        $this->request->setParam('format',   'json');
        $this->request->setPost(P4Cms_Form::CSRF_TOKEN_NAME, P4Cms_Form::getCsrfToken());
        $this->dispatch('/category/manage/delete');
        $this->assertModule('category', 'Expected module.');
        $this->assertController('manage', 'Expected controller');
        $this->assertAction('delete', 'Expected action');

        // ensure categories gone.
        $this->assertFalse(
            Category_Model_Category::exists('editme/subcat'),
            "Expected category editme/subcat not to exist post delete."
        );
        $this->assertFalse(
            Category_Model_Category::exists('editme'),
            "Expected category editme not to exist post delete."
        );
    }

    /**
     * Test filtering grid by search query.
     */
    public function testFilterBySearch()
    {
        $this->utility->impersonate('editor');

        // setup environment for testing
        $this->_setupFiltering();

        $this->dispatch('/category/manage/format/json?search[query]=last');
        $body = $this->response->getBody();
        $this->assertModule('category', 'Expected module, dispatch #1. '. $body);
        $this->assertController('manage', 'Expected controller, dispatch #1 '. $body);
        $this->assertAction('index', 'Expected action, dispatch #1 '. $body);

        // decode json output
        $data = Zend_Json::decode($body);

        // expected items (* denotes obligatory): B(*), B/subB, C(*), C/subC(*), C/subC/subsubC-last
        $obligatoryLogicMap = array(
            'B'                     => 1,
            'B/subB-last'           => 0,
            'C'                     => 1,
            'C/subC'                => 1,
            'C/subC/subsubC-last'   => 0
        );

        // verify number of items after filtering
        $this->assertSame(
            count($obligatoryLogicMap),
            count($data['items']),
            'Expected number of items after filtering by search #1.'
        );

        // verify obligatory items are marked
        foreach ($obligatoryLogicMap as $id => $isObligatory) {
            $assertFunction = $isObligatory ? 'assertTrue' : 'assertFalse';
            $this->$assertFunction(
                $this->_getItemById($id, $data, 'obligatory'),
                "Expected category $id " . ($isObligatory ? 'is' : 'is not') 
                    . " marked as obligatory after filtering by search #1"
            );
        }

        // test with another search query
        $this->resetRequest()
             ->resetResponse();

        $this->dispatch('/category/manage/format/json?search[query]=subc');
        $body = $this->response->getBody();
        $this->assertModule('category', 'Expected module, dispatch #2. '. $body);
        $this->assertController('manage', 'Expected controller, dispatch #2 '. $body);
        $this->assertAction('index', 'Expected action, dispatch #2 '. $body);

        // decode json output
        $data = Zend_Json::decode($body);

        // expected items (* denotes obligatory): C(*), C/subC, C/subC/subsubC-last
        $obligatoryLogicMap = array(
            'C'                     => 1,
            'C/subC'                => 0,
            'C/subC/subsubC-last'   => 0
        );

        // verify number of items after filtering
        $this->assertSame(
            count($obligatoryLogicMap),
            count($data['items']),
            'Expected number of items after filtering by search #2.'
        );

        // verify obligatory items are marked
        foreach ($obligatoryLogicMap as $id => $isObligatory) {
            $assertFunction = $isObligatory ? 'assertTrue' : 'assertFalse';
            $this->$assertFunction(
                $this->_getItemById($id, $data, 'obligatory'),
                "Expected category $id " . ($isObligatory ? 'is' : 'is not')
                    . " marked as obligatory after filtering by search #2"
            );
        }
    }

    /**
     * Test filtering grid by number of category entries.
     */
    public function testFilterByCategoryEntries()
    {
        $this->utility->impersonate('editor');

        // setup environment for testing
        $this->_setupFiltering();

        // show only non-empty categories
        $this->dispatch('/category/manage/format/json?entriesCount[display]=more');
        $body = $this->response->getBody();
        $this->assertModule('category', 'Expected module, dispatch #1. '. $body);
        $this->assertController('manage', 'Expected controller, dispatch #1 '. $body);
        $this->assertAction('index', 'Expected action, dispatch #1 '. $body);

        // decode json output
        $data = Zend_Json::decode($body);

        // expected items (* denotes obligatory): B, C(*), C/subC
        $obligatoryLogicMap = array(
            'B'                     => 0,
            'C'                     => 1,
            'C/subC'                => 0,
        );

        // verify number of items after filtering
        $this->assertSame(
            count($obligatoryLogicMap),
            count($data['items']),
            'Expected number of items after filtering by category entries #1.'
        );

        // verify obligatory items are marked
        foreach ($obligatoryLogicMap as $id => $isObligatory) {
            $assertFunction = $isObligatory ? 'assertTrue' : 'assertFalse';
            $this->$assertFunction(
                $this->_getItemById($id, $data, 'obligatory'),
                "Expected category $id " . ($isObligatory ? 'is' : 'is not')
                    . " marked as obligatory after filtering by category entries #1"
            );
        }

        // test with another filter
        $this->resetRequest()
             ->resetResponse();

        // show only empty categories
        $this->dispatch('/category/manage/format/json?entriesCount[display]=none');
        $body = $this->response->getBody();
        $this->assertModule('category', 'Expected module, dispatch #2. '. $body);
        $this->assertController('manage', 'Expected controller, dispatch #2 '. $body);
        $this->assertAction('index', 'Expected action, dispatch #2 '. $body);

        // decode json output
        $data = Zend_Json::decode($body);

        // expected items (* denotes obligatory): A, B(*), B/subB-last, C, C/subC(*), C/subC/subsubC-last
        $obligatoryLogicMap = array(
            'A'                     => 0,
            'B'                     => 1,
            'B/subB-last'           => 0,
            'C'                     => 0,
            'C/subC'                => 1,
            'C/subC/subsubC-last'   => 0
        );

        // verify number of items after filtering
        $this->assertSame(
            count($obligatoryLogicMap),
            count($data['items']),
            'Expected number of items after filtering by category entries #2.'
        );

        // verify obligatory items are marked
        foreach ($obligatoryLogicMap as $id => $isObligatory) {
            $assertFunction = $isObligatory ? 'assertTrue' : 'assertFalse';
            $this->$assertFunction(
                $this->_getItemById($id, $data, 'obligatory'),
                "Expected category $id " . ($isObligatory ? 'is' : 'is not')
                    . " marked as obligatory after filtering by category entries #2"
            );
        }
    }

    /**
     * Test multi-filtering.
     */
    public function testComposedFilter()
    {
        $this->utility->impersonate('editor');

        // setup environment for testing
        $this->_setupFiltering();

        $this->dispatch('/category/manage/format/json?search[query]=sub&entriesCount[display]=more');
        $body = $this->response->getBody();
        $this->assertModule('category', 'Expected module, dispatch #1. '. $body);
        $this->assertController('manage', 'Expected controller, dispatch #1 '. $body);
        $this->assertAction('index', 'Expected action, dispatch #1 '. $body);

        // decode json output
        $data = Zend_Json::decode($body);

        // expected items (* denotes obligatory): C(*), C/subC
        $obligatoryLogicMap = array(
            'C'                     => 1,
            'C/subC'                => 0,
        );

        // verify number of items after filtering
        $this->assertSame(
            count($obligatoryLogicMap),
            count($data['items']),
            'Expected number of items after multi-filtering #1.'
        );

        // verify obligatory items are marked
        foreach ($obligatoryLogicMap as $id => $isObligatory) {
            $assertFunction = $isObligatory ? 'assertTrue' : 'assertFalse';
            $this->$assertFunction(
                $this->_getItemById($id, $data, 'obligatory'),
                "Expected category $id " . ($isObligatory ? 'is' : 'is not')
                    . " marked as obligatory after multi-filtering #1"
            );
        }

        // test with another search query
        $this->resetRequest()
             ->resetResponse();

        $this->dispatch('/category/manage/format/json?search[query]=last&entriesCount[display]=more');
        $body = $this->response->getBody();
        $this->assertModule('category', 'Expected module, dispatch #2. '. $body);
        $this->assertController('manage', 'Expected controller, dispatch #2 '. $body);
        $this->assertAction('index', 'Expected action, dispatch #2 '. $body);

        // decode json output
        $data = Zend_Json::decode($body);

        // expected items (* denotes obligatory): empty
        $obligatoryLogicMap = array();

        // verify number of items after filtering
        $this->assertSame(
            count($obligatoryLogicMap),
            count($data['items']),
            'Expected number of items after multi-filtering #2.'
        );

        // verify obligatory items are marked
        foreach ($obligatoryLogicMap as $id => $isObligatory) {
            $assertFunction = $isObligatory ? 'assertTrue' : 'assertFalse';
            $this->$assertFunction(
                $this->_getItemById($id, $data, 'obligatory'),
                "Expected category $id " . ($isObligatory ? 'is' : 'is not')
                    . " marked as obligatory after multi-filtering #2"
            );
        }
    }

    /**
     * Helper function for getting item with given id from json decoded data.
     *
     * @param   string          $id     id to search for between items in the data
     * @param   array           $data   array to search item with given id for
     * @param   string          $key    optional - if set then item[key] will be
     *                                  returned, where item is one of the items in
     *                                  data with item['id'] matches the given id,
     *                                  otherwise the whole item will be returned
     * @return  array|string            data item with id matching the passed value
     *                                  or value of item[key]
     */
    protected function _getItemById($id, array $data, $key = null)
    {
        $callback = function($item) use ($id)
        {
            return isset($item['id']) && $item['id'] === $id;
        };
        $found = current(array_filter($data['items'], $callback));
        return $key ? $found[$key] : $found;
    }

    /**
     * Creates categories and assigned content for testing filtering options.
     */
    protected function _setupFiltering()
    {
        // create folowing categories structure:
        // A
        // B
        //   subB-last
        // C
        //   subC
        //     subsubC-last
        Category_Model_Category::store(
            array(
                'title' => 'A',
                'id'    => 'A'
            )
        );
        Category_Model_Category::store(
            array(
                'title' => 'B',
                'id'    => 'B'
            )
        );
        Category_Model_Category::store(
            array(
                'title' => 'subB-last',
                'id'    => 'B/subB-last'
            )
        );
        Category_Model_Category::store(
            array(
                'title' => 'C',
                'id'    => 'C'
            )
        );
        Category_Model_Category::store(
            array(
                'title' => 'subC',
                'id'    => 'C/subC'
            )
        );
        Category_Model_Category::store(
            array(
                'title' => 'subsubC-last',
                'id'    => 'C/subC/subsubC-last'
            )
        );

        // create content entry and assign the B and subC category to it
        P4Cms_Content::store(
            array(
                'id'    => 'test'
            )
        );
        Category_Model_Category::setEntryCategories('test', array('B', 'C/subC'));
    }

    /**
     * Test helper method to create specified content records.
     *
     * @param   array  $entries  An array of id => title for the content records to create.
     * @return  array  An array of the created content entries.
     */
    protected function _makeContent(array $entries)
    {
        $created = array();
        foreach ($entries as $id => $title) {
            $entry = new P4Cms_Content;
            $entry->setId($id)
                  ->setValue('contentType', 'basic-page')
                  ->setValue('title', $title)
                  ->save();
            $created[] = $entry;
        }

        return $created;
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/application/category/tests/ManageControllerTest.php
#1 8972 Matt Attaway Initial add of the Chronicle source code