Merge remote-tracking branch 'origin/3.1'

This commit is contained in:
Ingo Schommer 2013-12-02 20:44:57 +01:00
commit 9ca336bea3
13 changed files with 250 additions and 167 deletions

View File

@ -10,6 +10,7 @@ class CMSPageAddController extends CMSPageEditController {
private static $allowed_actions = array(
'AddForm',
'doAdd',
'doCancel'
);
/**
@ -108,9 +109,11 @@ class CMSPageAddController extends CMSPageEditController {
}
$actions = new FieldList(
// $resetAction = new ResetFormAction('doCancel', _t('CMSMain.Cancel', 'Cancel')),
FormAction::create("doAdd", _t('CMSMain.Create',"Create"))
->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept')
->setUseButtonTag(true),
FormAction::create("doCancel", _t('CMSMain.Cancel',"Cancel"))
->addExtraClass('ss-ui-action-destructive ss-ui-action-cancel')
->setUseButtonTag(true)
);
@ -174,4 +177,8 @@ class CMSPageAddController extends CMSPageEditController {
return $this->redirect(Controller::join_links(singleton('CMSPageEditController')->Link('show'), $record->ID));
}
public function doCancel($data, $form) {
return $this->redirect(singleton('CMSMain')->Link());
}
}

View File

@ -77,7 +77,13 @@ class CMSSettingsController extends LeftAndMain {
public function save_siteconfig($data, $form) {
$siteConfig = SiteConfig::current_site_config();
$form->saveInto($siteConfig);
$siteConfig->write();
try {
$siteConfig->write();
} catch(ValidationException $ex) {
$form->sessionMessage($ex->getResult()->message(), 'bad');
return $this->getResponseNegotiator()->respond($this->request);
}
$this->response->addHeader('X-Status', rawurlencode(_t('LeftAndMain.SAVEDUP', 'Saved.')));
return $this->getResponseNegotiator()->respond($this->request);

View File

@ -42,7 +42,9 @@ class ErrorPage extends Page {
/**
* Get a {@link SS_HTTPResponse} to response to a HTTP error code if an
* {@link ErrorPage} for that code is present.
* {@link ErrorPage} for that code is present. First tries to serve it
* through the standard SilverStripe request method. Falls back to a static
* file generated when the user hit's save and publish in the CMS
*
* @param int $statusCode
*
@ -50,11 +52,17 @@ class ErrorPage extends Page {
*/
public static function response_for($statusCode) {
// first attempt to dynamically generate the error page
if($errorPage = DataObject::get_one('ErrorPage', "\"ErrorPage\".\"ErrorCode\" = $statusCode")) {
$errorPage = ErrorPage::get()->filter(array(
"ErrorCode" => $statusCode
))->first();
if($errorPage) {
Requirements::clear();
Requirements::clear_combined_files();
return ModelAsController::controller_for($errorPage)->handleRequest(new SS_HTTPRequest('GET', ''), DataModel::inst());
return ModelAsController::controller_for($errorPage)->handleRequest(
new SS_HTTPRequest('GET', ''), DataModel::inst()
);
}
// then fall back on a cached version
@ -62,13 +70,13 @@ class ErrorPage extends Page {
$statusCode,
class_exists('Translatable') ? Translatable::get_current_locale() : null
);
if(file_exists($cachedPath)) {
$response = new SS_HTTPResponse();
$response->setStatusCode($statusCode);
$response->setBody(file_get_contents($cachedPath));
return $response;
}
}
@ -129,13 +137,12 @@ class ErrorPage extends Page {
}
}
}
}
}
/**
* Returns an array of arrays, each of which defines
* properties for a new ErrorPage record.
* Returns an array of arrays, each of which defines properties for a new
* ErrorPage record.
*
* @return array
*/
@ -159,6 +166,7 @@ class ErrorPage extends Page {
)
)
);
$this->extend('getDefaultRecords', $data);
return $data;
@ -316,14 +324,20 @@ class ErrorPage extends Page {
*/
class ErrorPage_Controller extends Page_Controller {
public function init() {
parent::init();
/**
* Overload the provided {@link Controller::handleRequest()} to append the
* correct status code post request since otherwise permission related error
* pages such as 401 and 403 pages won't be rendered due to
* {@link SS_HTTPResponse::isFinished() ignoring the response body.
*
* @param SS_HTTPRequest
* @param DataModel
*/
public function handleRequest(SS_HTTPRequest $request, DataModel $model = NULL) {
$body = parent::handleRequest($request, $model);
$this->response->setStatusCode($this->ErrorCode);
$action = $this->request->param('Action');
if(!$action || $action == 'index') {
$this->getResponse()->setStatusCode($this->failover->ErrorCode ? $this->failover->ErrorCode : 404);
}
return $this->response;
}
}

View File

@ -84,7 +84,7 @@
else $(this).find('input').removeAttr('disabled');
}
});
$(".cms-page-add-button").entwine({
onclick: function(e) {
var tree = $('.cms-tree'), list = $('.cms-list'), parentId = 0;

View File

@ -1,4 +1,4 @@
<div id="settings-controller-cms-content" class="cms-content center cms-tabset $BaseCSSClasses" data-layout-type="border" data-pjax-fragment="Content" data-ignore-tab-state="true">
<div id="settings-controller-cms-content" class="cms-content center cms-tabset $BaseCSSClasses" data-layout-type="border" data-pjax-fragment="Content CurrentForm" data-ignore-tab-state="true">
<div class="cms-content-header north">
<% with $EditForm %>

View File

@ -3,12 +3,12 @@
namespace SilverStripe\Cms\Test\Behaviour;
use SilverStripe\BehatExtension\Context\SilverStripeContext,
SilverStripe\BehatExtension\Context\BasicContext,
SilverStripe\BehatExtension\Context\LoginContext,
SilverStripe\BehatExtension\Context\FixtureContext,
SilverStripe\Framework\Test\Behaviour\CmsFormsContext,
SilverStripe\Framework\Test\Behaviour\CmsUiContext,
SilverStripe\Cms\Test\Behaviour;
SilverStripe\BehatExtension\Context\BasicContext,
SilverStripe\BehatExtension\Context\LoginContext,
SilverStripe\BehatExtension\Context\FixtureContext,
SilverStripe\Framework\Test\Behaviour\CmsFormsContext,
SilverStripe\Framework\Test\Behaviour\CmsUiContext,
SilverStripe\Cms\Test\Behaviour;
// PHPUnit
require_once 'PHPUnit/Autoload.php';
@ -20,33 +20,31 @@ require_once 'PHPUnit/Framework/Assert/Functions.php';
* Context automatically loaded by Behat.
* Uses subcontexts to extend functionality.
*/
class FeatureContext extends \SilverStripe\Framework\Test\Behaviour\FeatureContext
{
/**
* Initializes context.
* Every scenario gets it's own context object.
*
* @param array $parameters context parameters (set them up through behat.yml)
*/
public function __construct(array $parameters)
{
parent::__construct($parameters);
class FeatureContext extends \SilverStripe\Framework\Test\Behaviour\FeatureContext {
// Override existing fixture context with more specific one
$fixtureContext = new \SilverStripe\Cms\Test\Behaviour\FixtureContext($parameters);
$fixtureContext->setFixtureFactory($this->getFixtureFactory());
$this->useContext('FixtureContext', $fixtureContext);
/**
* Initializes context.
* Every scenario gets it's own context object.
*
* @param array $parameters context parameters (set them up through behat.yml)
*/
public function __construct(array $parameters) {
parent::__construct($parameters);
// Use blueprints which auto-publish all subclasses of SiteTree
$factory = $fixtureContext->getFixtureFactory();
foreach(\ClassInfo::subclassesFor('SiteTree') as $id => $class) {
$blueprint = \Injector::inst()->create('FixtureBlueprint', $class);
$blueprint->addCallback('afterCreate', function($obj, $identifier, &$data, &$fixtures) {
$obj->publish('Stage', 'Live');
});
$factory->define($class, $blueprint);
}
// Override existing fixture context with more specific one
$fixtureContext = new \SilverStripe\Cms\Test\Behaviour\FixtureContext($parameters);
$fixtureContext->setFixtureFactory($this->getFixtureFactory());
$this->useContext('FixtureContext', $fixtureContext);
}
// Use blueprints which auto-publish all subclasses of SiteTree
$factory = $fixtureContext->getFixtureFactory();
foreach(\ClassInfo::subclassesFor('SiteTree') as $id => $class) {
$blueprint = \Injector::inst()->create('FixtureBlueprint', $class);
$blueprint->addCallback('afterCreate', function($obj, $identifier, &$data, &$fixtures) {
$obj->publish('Stage', 'Live');
});
$factory->define($class, $blueprint);
}
}
}

View File

@ -3,14 +3,14 @@
namespace SilverStripe\Cms\Test\Behaviour;
use Behat\Behat\Context\ClosuredContextInterface,
Behat\Behat\Context\TranslatedContextInterface,
Behat\Behat\Context\BehatContext,
Behat\Behat\Context\Step,
Behat\Behat\Event\StepEvent,
Behat\Behat\Exception\PendingException,
Behat\Mink\Driver\Selenium2Driver,
Behat\Gherkin\Node\PyStringNode,
Behat\Gherkin\Node\TableNode;
Behat\Behat\Context\TranslatedContextInterface,
Behat\Behat\Context\BehatContext,
Behat\Behat\Context\Step,
Behat\Behat\Event\StepEvent,
Behat\Behat\Exception\PendingException,
Behat\Mink\Driver\Selenium2Driver,
Behat\Gherkin\Node\PyStringNode,
Behat\Gherkin\Node\TableNode;
// PHPUnit
require_once 'PHPUnit/Autoload.php';
@ -19,32 +19,50 @@ require_once 'PHPUnit/Framework/Assert/Functions.php';
/**
* Context used to create fixtures in the SilverStripe ORM.
*/
class FixtureContext extends \SilverStripe\BehatExtension\Context\FixtureContext
{
class FixtureContext extends \SilverStripe\BehatExtension\Context\FixtureContext {
/**
* Find or create a redirector page and link to another existing page.
* Example: Given a "page" "My Redirect" which redirects to a "page" "Page 1"
*
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" (:?which )?redirects to (?:(an|a|the) )"(?<targetType>[^"]+)" "(?<targetId>[^"]+)"$/
*/
public function stepCreateRedirectorPage($type, $id, $targetType, $targetId)
{
$class = 'RedirectorPage';
$targetClass = $this->convertTypeToClass($targetType);
$targetObj = $this->fixtureFactory->get($targetClass, $targetId);
if(!$targetObj) $targetObj = $this->fixtureFactory->get($targetClass, $targetId);
$fields = array('LinkToID' => $targetObj->ID);
$obj = $this->fixtureFactory->get($class, $id);
if($obj) {
$obj->update($fields);
} else {
$obj = $this->fixtureFactory->createObject($class, $id, $fields);
}
$obj->write();
$obj->publish('Stage', 'Live');
}
/**
* Find or create a redirector page and link to another existing page.
* Example: Given a "page" "My Redirect" which redirects to a "page" "Page 1"
*
* @Given /^(?:(an|a|the) )"(?<type>[^"]+)" "(?<id>[^"]+)" (:?which )?redirects to (?:(an|a|the) )"(?<targetType>[^"]+)" "(?<targetId>[^"]+)"$/
*/
public function stepCreateRedirectorPage($type, $id, $targetType, $targetId) {
$class = 'RedirectorPage';
$targetClass = $this->convertTypeToClass($targetType);
$targetObj = $this->fixtureFactory->get($targetClass, $targetId);
if (!$targetObj) $targetObj = $this->fixtureFactory->get($targetClass, $targetId);
$fields = array('LinkToID' => $targetObj->ID);
$obj = $this->fixtureFactory->get($class, $id);
if ($obj) {
$obj->update($fields);
} else {
$obj = $this->fixtureFactory->createObject($class, $id, $fields);
}
$obj->write();
$obj->publish('Stage', 'Live');
}
/**
*
* Check if the user can edit a page
*
* Example: Then pages should be editable by "Admin"
* Then pages should not be editable by "Admin"
*
* @Then /^pages should( not? |\s*)be editable by "([^"]*)"$/
*/
public function pagesShouldBeEditableBy($negative, $member){
$page = \Page::get()->First();
return array(
new Step\Given('I am not logged in'),
new Step\Given('I am logged in with "' . $member . '" permissions'),
new Step\Given('I go to "/admin/pages/edit/show/' . $page->ID . '"'),
new Step\Given('I should' . $negative . 'see a "Page name" field'),
new Step\Then('I am on the homepage')
);
}
}

View File

@ -6,10 +6,8 @@ Feature: Insert an image into a page
Background:
Given a "page" "About Us"
#And a "file" "assets/folder1/file1.jpg"
#And a "file" "assets/folder1/file3.jpg"
#And a "file" "assets/folder1/folder1.1/file2.jpg"
#And a "folder" "assets/folder2"
And a "file" "assets/folder1/file1.jpg"
And a "file" "assets/folder1/file2.jpg"
And I am logged in with "ADMIN" permissions
And I go to "/admin/pages"
And I click on "About Us" in the tree
@ -28,17 +26,36 @@ Feature: Insert an image into a page
# Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button
@todo
@assets
Scenario: I can insert an image uploaded from my own computer
Given I press the "Insert Media" button
And I press the "From your computer" button
# TODO Figure out how to provide the file
And I attach the file "testfile.jpg" to "AssetUploadField" with HTML5
Then the upload field should have successfully uploaded "testfile.jpg"
# TODO Delay previous step until upload succeeded
And I wait for 2 seconds
Then there should be a file "assets/Uploads/testfile.jpg"
When I press the "Insert" button
Then the "Content" HTML field should contain "testfile.jpg"
# Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button
@todo
@assets
Scenario: I can overwrite an existing image with one uploaded from my own computer
Given a "file" "assets/Uploads/file1.jpg"
When I press the "Insert Media" button
And I press the "From your computer" button
And I attach the file "file1.jpg" to "AssetUploadField" with HTML5
# TODO Delay previous step until upload succeeded
And I wait for 2 seconds
Then I should see "Overwrite"
When I press the "Overwrite" button
Then there should be a file "assets/Uploads/file1.jpg"
When I press the "Insert" button
Then the "Content" HTML field should contain "file1.jpg"
# Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button
@todo
Scenario: I can insert an image from the CMS file store
Given I press the "Insert Media" button
And I press the "From the CMS" button
@ -46,6 +63,8 @@ Feature: Insert an image into a page
And I select "file1.jpg"
When I press the "Insert" button
Then the "Content" HTML field should contain "file1.jpg"
# Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button
@todo
Scenario: I can insert multiple images at once
@ -53,10 +72,12 @@ Feature: Insert an image into a page
And I press the "From the CMS" button
And I select "folder1" in the "Find in Folder" dropdown
And I select "file1.jpg"
And I select "file3.jpg"
And I select "file2.jpg"
When I press the "Insert" button
Then the "Content" HTML field should contain "file1.jpg"
And the "Content" HTML field should contain "file1.jpg"
And the "Content" HTML field should contain "file2.jpg"
# Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button
@todo
Scenario: I can edit properties of an image before inserting it
@ -69,6 +90,8 @@ Feature: Insert an image into a page
And I press the "Insert" button
Then the "Content" HTML field should contain "file1.jpg"
And the "Content" HTML field should contain "My alt"
# Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button
@todo
Scenario: I can edit dimensions of an image before inserting it
@ -81,6 +104,8 @@ Feature: Insert an image into a page
When I fill in "Height" with "20"
And I press the "Insert" button
Then the "Content" HTML field should contain "<img src=assets/folder1/file1.jpg width=10 height=20>"
# Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button
@todo
Scenario: I can edit dimensions of an existing image
@ -92,4 +117,6 @@ Feature: Insert an image into a page
When I fill in "Width" with "10"
When I fill in "Height" with "20"
And I press the "Insert" button
Then the "Content" HTML field should contain "<img src=assets/folder1/file1.jpg width=10 height=20>"
Then the "Content" HTML field should contain "<img src=assets/folder1/file1.jpg width=10 height=20>"
# Required to avoid "unsaved changed" browser dialog
Then I press the "Save draft" button

View File

@ -1,56 +0,0 @@
@todo
Feature: Manage global page permissions
As an administrator
I want to manage view and edit permission defaults on pages
In order to set good defaults and avoid repeating myself on each page
Background:
Given I have an "Administrator" user in a "Administrators" Security Group
Given I have an "Content Author" user in a "Content Authors" Security Group
And I am logged in as an "ADMIN"
And I navigate to the "Settings" CMS section
Scenario: I can open global view permissions to everyone
Given I select the 'Access' tab
And I select "Anyone" in the 'Who can view pages on this site?' field
And press the "Save" button
When I visit the homepage without being logged in
Then I can see "Welcome"
Scenario: I can limit global view permissions to logged-in users
Given I select the 'Access' tab
And I select "Logged-in users" in 'Who can view pages on this site?'
And press the 'Save' button
When I visit the homepage without being logged in
Then I am redirected to the log-in page
When I visit the homepage as "Content Author"
Then I can see "Welcome"
Scenario: I can limit global view permissions to certain groups
Given I select the 'Access' tab
And I select "Only these people (choose from list)" in 'Who can view pages on this site?'
And I select "Administrators" in the "Viewer Groups" dropdown
And press the 'Save' button
When I visit the homepage without being logged in
Then I am redirected to the log-in page
When I visit the homepage as "Content Author"
Then I am redirected to the log-in page
When I visit the homepage as "Administrator"
Then I can see "Welcome"
Scenario: I can limit global edit permissions to logged-in users
Given I select the 'Access' tab
And I select "Logged-in users" in 'Who can edit pages on this site?'
And press the 'Save' button
Then pages should be editable by "Content Authors"
And pages should be editable by "Administrators"
Scenario: I can limit global edit permissions to certain groups
Given I select the 'Access' tab
And I select "Only these people (choose from list)" in 'Who can edit pages on this site?'
And I select "Administrators" in the "Viewer Groups" dropdown
And press the 'Save' button
Then pages should not be editable by "Content Authors"
But pages should be editable by "Administrators"

View File

@ -0,0 +1,58 @@
Feature: Manage global page permissions
As an administrator
I want to manage view and edit permission defaults on pages
In order to set good defaults and avoid repeating myself on each page
Background:
Given a "page" "Home" with "Content"="Welcome"
And a "group" "AUTHOR group" has permissions "Access to 'Pages' section"
And a "group" "SECURITY group" has permissions "Access to 'Security' section"
And I am logged in with "ADMIN" permissions
And I go to "admin/settings"
And I click the "Access" CMS tab
Scenario: I can open global view permissions to everyone
Given I select "Anyone" from "Who can view pages on this site?" input group
And I press the "Save" button
When I am not logged in
And I go to the homepage
Then I should see "Welcome"
Scenario: I can limit global view permissions to logged-in users
Given I select "Logged-in users" from "Who can view pages on this site?" input group
And I press the "Save" button
When I am not logged in
And I go to the homepage
Then I should see a log-in form
When I am logged in with "AUTHOR" permissions
And I go to the homepage
Then I should see "Welcome"
Scenario: I can limit global view permissions to certain groups
Given I select "Only these people (choose from list)" from "Who can view pages on this site?" input group
And I select "AUTHOR group" from "Viewer Groups"
And I press the "Save" button
When I am not logged in
And I go to the homepage
Then I should see a log-in form
When I am logged in with "SECURITY" permissions
And I go to the homepage
Then I will see a "warning" log-in message
When I am not logged in
And I am logged in with "AUTHOR" permissions
And I go to the homepage
Then I should see "Welcome"
Scenario: I can limit global edit permissions to logged-in users
Given I select "Anyone who can log-in to the CMS" from "Who can edit pages on this site?" input group
And I press the "Save" button
Then pages should be editable by "AUTHOR"
And pages should be editable by "ADMIN"
Scenario: I can limit global edit permissions to certain groups
Given I select "Only these people (choose from list)" from "Who can edit pages on this site?" input group
And I select "ADMIN group" from "Viewer Groups"
And I press the "Save" button
Then pages should not be editable by "AUTHOR"
But pages should be editable by "ADMIN"

View File

@ -17,38 +17,35 @@ Feature: Search for a page
Then I should see "About Us" in the tree
But I should not see "Contact Us" in the tree
@todo
Scenario: I can search for a page by its type
Given a "page" "My Error Page" of type "Error Page"
And I fill in "Page Type" with "Redirector Page"
Given a "Error Page" "My Error Page"
When I select "Error Page" from "Page Type"
And I press the "Apply Filter" button
Then I should see "My Error Page" in the tree
But I should not see "Contact Us" in the tree
@todo
Scenario: I can search for a page by its oldest last edited date
Given a "page" "Recent Page"
And a "page" "Old Page" was last edited 7 days ago
When I fill in "From" with "5 days ago"
And a "page" "Old Page" was last edited "7 days ago"
When I fill in "From" with "the date of 5 days ago"
And I press the "Apply Filter" button
Then I should see "Recent Page" in the tree
But I should not see "Old Page" in the tree
@todo
Scenario: I can search for a page by its newest last edited date
Given a "page" "Recent Page"
And a "page" "Old Page" was last edited 7 days ago
When I fill in "To" with "5 days ago"
And a "page" "Old Page" was last edited "7 days ago"
When I fill in "To" with "the date of 5 days ago"
And I press the "Apply Filter" button
Then I should not see "Recent Page" in the tree
But I should see "Old Page" in the tree
@todo
Scenario: I can include deleted pages in my search
Given a "page" "Deleted Page"
And the "page" "Old Page" is deleted
And the "page" "Deleted Page" is deleted
When I press the "Apply Filter" button
Then I should not see "Deleted Page" in the tree
When I fill in "Pages" with "All pages, including deleted"
When I select "All pages, including deleted" from "Pages"
And I press the "Apply Filter" button
Then I should see "Deleted Page" in the tree

View File

@ -60,5 +60,14 @@ class ErrorPageTest extends FunctionalTest {
/* Don't show the error page in the search */
$this->assertEquals($page->ShowInSearch, 0, 'Don\'t show the error page in search');
}
}
public function testBehaviourOf403() {
$page = $this->objFromFixture('ErrorPage', '403');
$page->publish('Stage', 'Live');
$response = $this->get($page->Link());
$this->assertEquals($response->getStatusCode(), '403');
$this->assertNotNull($response->getBody(), 'We have body text from the error page');
}
}

View File

@ -3,4 +3,9 @@ ErrorPage:
Title: Page Not Found
URLSegment: page-not-found
Content: My error page body
ErrorCode: 404
ErrorCode: 404
403:
Title: Permission Failure
URLSegment: permission-denied
Content: You do not have permission to view this page
ErrorCode: 403