Merge pull request #52 from wilr/package-info

FIX: WidgetControllerTest failing due to 3.1 API.
This commit is contained in:
Will Rossiter 2013-05-08 03:06:51 -07:00
commit b7b8b80c18
9 changed files with 239 additions and 211 deletions

View File

@ -241,7 +241,7 @@ sure that your controller follows the usual naming conventions, and it will be a
); );
} }
class MyWidget_Controller extends Widget_Controller { class MyWidget_Controller extends WidgetController {
public function MyFormName() { public function MyFormName() {
return new Form( return new Form(
$this, $this,

View File

@ -1,6 +1,9 @@
<?php <?php
/** /**
* Add this to ContentController to enable widgets * Add this to ContentController to enable widgets
*
* @package widgets
*/ */
class WidgetContentControllerExtension extends Extension { class WidgetContentControllerExtension extends Extension {
@ -13,9 +16,12 @@ class WidgetContentControllerExtension extends Extension {
); );
/** /**
* Handles widgets attached to a page through one or more {@link WidgetArea} elements. * Handles widgets attached to a page through one or more {@link WidgetArea}
* Iterated through each $has_one relation with a {@link WidgetArea} * elements.
* and looks for connected widgets by their database identifier. *
* Iterated through each $has_one relation with a {@link WidgetArea} and
* looks for connected widgets by their database identifier.
*
* Assumes URLs in the following format: <URLSegment>/widget/<Widget-ID>. * Assumes URLs in the following format: <URLSegment>/widget/<Widget-ID>.
* *
* @return RequestHandler * @return RequestHandler
@ -27,7 +33,11 @@ class WidgetContentControllerExtension extends Extension {
// find WidgetArea relations // find WidgetArea relations
$widgetAreaRelations = array(); $widgetAreaRelations = array();
$hasOnes = $this->owner->data()->has_one(); $hasOnes = $this->owner->data()->has_one();
if(!$hasOnes) return false;
if(!$hasOnes) {
return false;
}
foreach($hasOnes as $hasOneName => $hasOneClass) { foreach($hasOnes as $hasOneName => $hasOneClass) {
if($hasOneClass == 'WidgetArea' || is_subclass_of($hasOneClass, 'WidgetArea')) { if($hasOneClass == 'WidgetArea' || is_subclass_of($hasOneClass, 'WidgetArea')) {
$widgetAreaRelations[] = $hasOneName; $widgetAreaRelations[] = $hasOneName;
@ -36,26 +46,21 @@ class WidgetContentControllerExtension extends Extension {
// find widget // find widget
$widget = null; $widget = null;
foreach($widgetAreaRelations as $widgetAreaRelation) { foreach($widgetAreaRelations as $widgetAreaRelation) {
if($widget) break; if($widget) {
break;
}
$widget = $this->owner->data()->$widgetAreaRelation()->Widgets( $widget = $this->owner->data()->$widgetAreaRelation()->Widgets(
sprintf('"Widget"."ID" = %d', $SQL_id) sprintf('"Widget"."ID" = %d', $SQL_id)
)->First(); )->First();
} }
if(!$widget) user_error('No widget found', E_USER_ERROR);
if(!$widget) {
// find controller user_error('No widget found', E_USER_ERROR);
$controllerClass = '';
foreach(array_reverse(ClassInfo::ancestry($widget->class)) as $widgetClass) {
$controllerClass = "{$widgetClass}_Controller";
if(class_exists($controllerClass)) break;
} }
if(!$controllerClass) user_error(
sprintf('No controller available for %s', $widget->class), return $widget->getController();
E_USER_ERROR
);
return new $controllerClass($widget);
} }
} }

View File

@ -0,0 +1,121 @@
<?php
/**
* Optional controller for every widget which has its own logic, e.g. in forms.
*
* It always handles a single widget, usually passed in as a database
* identifier through the controller URL. Needs to be constructed as a nested
* controller within a {@link ContentController}.
*
* ## Forms
* You can add forms like in any other SilverStripe controller. If you need
* access to the widget from within a form, you can use
* `$this->controller->getWidget()` inside the form logic.
*
* Note: Widget controllers currently only work on {@link Page} objects,
* because the logic is implemented in {@link ContentController->handleWidget()}.
* Copy this logic and the URL rules to enable it for other controllers.
*
* @package widgets
*/
class WidgetController extends Controller {
/**
* @var Widget
*/
protected $widget;
/**
* @var array
*/
private static $allowed_actions = array(
'editablesegment'
);
/**
* @param Widget $widget
*/
public function __construct($widget = null) {
if($widget) {
$this->widget = $widget;
$this->failover = $widget;
}
parent::__construct();
}
/**
* @param string $action
* @return string
*/
public function Link($action = null) {
$id = ($this->widget) ? $this->widget->ID : null;
$segment = Controller::join_links('widget', $id, $action);
if($page = Director::get_current_page()) {
return $page->Link($segment);
}
return Controller::curr()->Link($segment);
}
/**
* @return Widget
*/
public function getWidget() {
return $this->widget;
}
/**
* Overloaded from {@link Widget->Content()} to allow for controller / form
* linking.
*
* @return string HTML
*/
public function Content() {
return $this->renderWith(array_reverse(ClassInfo::ancestry($this->widget->class)));
}
/**
* Overloaded from {@link Widget->WidgetHolder()} to allow for controller/
* form linking.
*
* @return string HTML
*/
public function WidgetHolder() {
return $this->renderWith("WidgetHolder");
}
/**
* Uses the `WidgetEditor.ss` template and {@link Widget->editablesegment()}
* to render a administrator-view of the widget. It is assumed that this
* view contains form elements which are submitted and saved through
* {@link WidgetAreaEditor} within the CMS interface.
*
* @return string HTML
*/
public function editablesegment() {
$className = $this->urlParams['ID'];
if (class_exists('Translatable') && Member::currentUserID()) {
// set current locale based on logged in user's locale
$locale = Member::currentUser()->Locale;
Translatable::set_current_locale($locale);
i18n::set_locale($locale);
}
if(class_exists($className) && is_subclass_of($className, 'Widget')) {
$obj = new $className();
return $obj->EditableSegment();
} else {
user_error("Bad widget class: $className", E_USER_WARNING);
return "Bad widget class name given";
}
}
}
/**
* @deprecated Use WidgetController
* @package widgets
*/
class Widget_Controller extends WidgetController {
}

View File

@ -1,13 +1,13 @@
<?php <?php
/** /**
* Special field type for selecting and configuring widgets on a page. * Special field type for selecting and configuring widgets on a page.
* @package cms *
* @subpackage content * @package widgets
*/ */
class WidgetAreaEditor extends FormField { class WidgetAreaEditor extends FormField {
/** /**
*
* @param string $name * @param string $name
* @param array $widgetClasses * @param array $widgetClasses
* @param int $maxWidgets * @param int $maxWidgets
@ -20,13 +20,14 @@ class WidgetAreaEditor extends FormField {
} }
/** /**
*
* @param array $properties * @param array $properties
* @return string - HTML for this formfield *
* @return string - HTML
*/ */
public function FieldHolder($properties = array()) { public function FieldHolder($properties = array()) {
Requirements::css('widgets/css/WidgetAreaEditor.css'); Requirements::css('widgets/css/WidgetAreaEditor.css');
Requirements::javascript('widgets/javascript/WidgetAreaEditor.js'); Requirements::javascript('widgets/javascript/WidgetAreaEditor.js');
return $this->renderWith("WidgetAreaEditor"); return $this->renderWith("WidgetAreaEditor");
} }
@ -40,7 +41,14 @@ class WidgetAreaEditor extends FormField {
foreach($this->widgetClasses as $widgetClass) { foreach($this->widgetClasses as $widgetClass) {
$classes = ClassInfo::subclassesFor($widgetClass); $classes = ClassInfo::subclassesFor($widgetClass);
if(count($classes) > 1) array_shift($classes);
if (isset($classes['Widget'])) {
unset($classes['Widget']);
}
else if (isset($classes[0]) && $classes[0] == 'Widget') {
unset($classes[0]);
}
foreach($classes as $class) { foreach($classes as $class) {
$widgets->push(singleton($class)); $widgets->push(singleton($class));
} }
@ -50,7 +58,6 @@ class WidgetAreaEditor extends FormField {
} }
/** /**
*
* @return HasManyList * @return HasManyList
*/ */
public function UsedWidgets() { public function UsedWidgets() {
@ -59,11 +66,11 @@ class WidgetAreaEditor extends FormField {
$relationName = $this->name; $relationName = $this->name;
$widgets = $this->form->getRecord()->getComponent($relationName)->Items(); $widgets = $this->form->getRecord()->getComponent($relationName)->Items();
return $widgets; return $widgets;
} }
/** /**
*
* @return string * @return string
*/ */
public function IdxField() { public function IdxField() {
@ -76,11 +83,11 @@ class WidgetAreaEditor extends FormField {
*/ */
public function Value() { public function Value() {
$relationName = $this->name; $relationName = $this->name;
return $this->form->getRecord()->getComponent($relationName)->ID; return $this->form->getRecord()->getComponent($relationName)->ID;
} }
/** /**
*
* @param DataObjectInterface $record * @param DataObjectInterface $record
*/ */
public function saveInto(DataObjectInterface $record) { public function saveInto(DataObjectInterface $record) {
@ -145,7 +152,7 @@ class WidgetAreaEditor extends FormField {
if($widget->ParentID == 0) { if($widget->ParentID == 0) {
$widget->ParentID = $record->$name()->ID; $widget->ParentID = $record->$name()->ID;
} }
// echo "Saving $widget->ID into $name/$widget->ParentID\n<br/>";
$widget->populateFromPostData($newWidgetData); $widget->populateFromPostData($newWidgetData);
} }
} }

View File

@ -1,19 +1,18 @@
<?php <?php
/** /**
* Widgets let CMS authors drag and drop small pieces of functionality into * Widgets let CMS authors drag and drop small pieces of functionality into
* defined areas of their websites. * defined areas of their websites.
* *
* ## Forms * You can use forms in widgets by implementing a {@link WidgetController}.
* You can use forms in widgets by implementing a {@link Widget_Controller}. *
* See {@link Widget_Controller} for more information. * See {@link Widget_Controller} for more information.
* *
* @package cms * @package widgets
* @subpackage widgets
*/ */
class Widget extends DataObject { class Widget extends DataObject {
/** /**
*
* @var array * @var array
*/ */
private static $db = array( private static $db = array(
@ -22,7 +21,6 @@ class Widget extends DataObject {
); );
/** /**
*
* @var array * @var array
*/ */
private static $defaults = array( private static $defaults = array(
@ -30,7 +28,6 @@ class Widget extends DataObject {
); );
/** /**
*
* @var array * @var array
*/ */
private static $has_one = array( private static $has_one = array(
@ -38,47 +35,45 @@ class Widget extends DataObject {
); );
/** /**
*
* @var array * @var array
*/ */
private static $has_many = array(); private static $has_many = array();
/** /**
*
* @var array * @var array
*/ */
private static $many_many = array(); private static $many_many = array();
/** /**
*
* @var array * @var array
*/ */
private static $belongs_many_many = array(); private static $belongs_many_many = array();
/** /**
*
* @var string * @var string
*/ */
private static $default_sort = "\"Sort\""; private static $default_sort = "\"Sort\"";
/** /**
*
* @var string * @var string
*/ */
private static $title = "Widget Title"; private static $title = "Widget Title";
/** /**
*
* @var string * @var string
*/ */
private static $cmsTitle = "Name of this widget"; private static $cmsTitle = "Name of this widget";
/** /**
*
* @var string * @var string
*/ */
private static $description = "Description of what this widget does."; private static $description = "Description of what this widget does.";
/**
* @var WidgetController
*/
protected $controller;
/** /**
* *
* @return FieldList * @return FieldList
@ -86,11 +81,12 @@ class Widget extends DataObject {
public function getCMSFields() { public function getCMSFields() {
$fields = new FieldList(); $fields = new FieldList();
$this->extend('updateCMSFields', $fields); $this->extend('updateCMSFields', $fields);
return $fields; return $fields;
} }
/** /**
* Note: Overloaded in {@link Widget_Controller}. * Note: Overloaded in {@link WidgetController}.
* *
* @return string HTML * @return string HTML
*/ */
@ -99,13 +95,13 @@ class Widget extends DataObject {
} }
/** /**
* Renders the widget content in a custom template with the same name as the current class. * Renders the widget content in a custom template with the same name as the
* This should be the main point of output customization. * current class. This should be the main point of output customization.
* *
* Invoked from within WidgetHolder.ss, which contains * Invoked from within WidgetHolder.ss, which contains the "framing" around
* the "framing" around the custom content, like a title. * the custom content, like a title.
* *
* Note: Overloaded in {@link Widget_Controller}. * Note: Overloaded in {@link WidgetController}.
* *
* @return string HTML * @return string HTML
*/ */
@ -114,7 +110,6 @@ class Widget extends DataObject {
} }
/** /**
*
* @return string * @return string
*/ */
public function Title() { public function Title() {
@ -122,7 +117,6 @@ class Widget extends DataObject {
} }
/** /**
*
* @return string * @return string
*/ */
public function CMSTitle() { public function CMSTitle() {
@ -130,7 +124,6 @@ class Widget extends DataObject {
} }
/** /**
*
* @return string * @return string
*/ */
public function Description() { public function Description() {
@ -138,7 +131,6 @@ class Widget extends DataObject {
} }
/** /**
*
* @return string - HTML * @return string - HTML
*/ */
public function DescriptionSegment() { public function DescriptionSegment() {
@ -146,7 +138,8 @@ class Widget extends DataObject {
} }
/** /**
* @see Widget_Controller->editablesegment() * @see WidgetController::editablesegment()
*
* @return string - HTML * @return string - HTML
*/ */
public function EditableSegment() { public function EditableSegment() {
@ -154,12 +147,12 @@ class Widget extends DataObject {
} }
/** /**
*
* @return FieldList * @return FieldList
*/ */
public function CMSEditor() { public function CMSEditor() {
$fields = $this->getCMSFields(); $fields = $this->getCMSFields();
$outputFields = new FieldList(); $outputFields = new FieldList();
foreach($fields as $field) { foreach($fields as $field) {
$name = $field->getName(); $name = $field->getName();
$value = $this->getField($name); $value = $this->getField($name);
@ -170,11 +163,11 @@ class Widget extends DataObject {
$field->setName($name); $field->setName($name);
$outputFields->push($field); $outputFields->push($field);
} }
return $outputFields; return $outputFields;
} }
/** /**
*
* @return string * @return string
*/ */
public function ClassName() { public function ClassName() {
@ -182,7 +175,6 @@ class Widget extends DataObject {
} }
/** /**
*
* @return string * @return string
*/ */
public function Name() { public function Name() {
@ -190,7 +182,39 @@ class Widget extends DataObject {
} }
/** /**
* @throws Exception
* *
* @return WidgetController
*/
public function getController() {
if($this->controller) {
return $this->controller;
}
foreach(array_reverse(ClassInfo::ancestry($this->class)) as $widgetClass) {
$controllerClass = "{$widgetClass}_Controller";
if(class_exists($controllerClass)) {
break;
}
$controllerClass = "{$widgetClass}Controller";
if(class_exists($controllerClass)) {
break;
}
}
if(!class_exists($controllerClass)) {
throw new Exception("Could not find controller class for $this->classname");
}
$this->controller = Injector::inst()->create($controllerClass, $this);
return $this->controller;
}
/**
* @param array $data * @param array $data
*/ */
public function populateFromPostData($data) { public function populateFromPostData($data) {
@ -212,132 +236,6 @@ class Widget extends DataObject {
// The field must be written to ensure a unique ID. // The field must be written to ensure a unique ID.
$this->Name = $this->class.$this->ID; $this->Name = $this->class.$this->ID;
$this->write(); $this->write();
}
}
/**
* Optional controller for every widget which has its own logic,
* e.g. in forms. It always handles a single widget, usually passed
* in as a database identifier through the controller URL.
* Needs to be constructed as a nested controller
* within a {@link ContentController}.
*
* ## Forms
* You can add forms like in any other SilverStripe controller.
* If you need access to the widget from within a form,
* you can use `$this->controller->getWidget()` inside the form logic.
* Note: Widget controllers currently only work on {@link Page} objects,
* because the logic is implemented in {@link ContentController->handleWidget()}.
* Copy this logic and the URL rules to enable it for other controllers.
*
* @package cms
* @subpackage widgets
*/
class Widget_Controller extends Controller {
/**
* @var Widget
*/
protected $widget;
/**
*
* @var array
*/
private static $allowed_actions = array(
'editablesegment'
);
/**
*
* @param Widget $widget
*/
public function __construct($widget = null) {
// TODO This shouldn't be optional, is only necessary for editablesegment()
if($widget) {
$this->widget = $widget;
$this->failover = $widget;
}
parent::__construct();
}
/**
*
* @param string $action
* @return string
*/
public function Link($action = null) {
$segment = Controller::join_links('widget', ($this->widget ? $this->widget->ID : null), $action);
if(Director::get_current_page()) {
return Director::get_current_page()->Link($segment);
} else {
return Controller::curr()->Link($segment);
}
}
/**
* @return Widget
*/
public function getWidget() {
return $this->widget;
}
/**
* Overloaded from {@link Widget->Content()}
* to allow for controller/form linking.
*
* @return string HTML
*/
public function Content() {
return $this->renderWith(array_reverse(ClassInfo::ancestry($this->widget->class)));
}
/**
* Overloaded from {@link Widget->WidgetHolder()}
* to allow for controller/form linking.
*
* @return string HTML
*/
public function WidgetHolder() {
return $this->renderWith("WidgetHolder");
}
/**
* Uses the `WidgetEditor.ss` template and {@link Widget->editablesegment()}
* to render a administrator-view of the widget. It is assumed that this
* view contains form elements which are submitted and saved through {@link WidgetAreaEditor}
* within the CMS interface.
*
* @return string HTML
*/
public function editablesegment() {
$className = $this->urlParams['ID'];
if (class_exists('Translatable') && Member::currentUserID()) {
// set current locale based on logged in user's locale
$locale = Member::currentUser()->Locale;
Translatable::set_current_locale($locale);
i18n::set_locale($locale);
}
if(class_exists($className) && is_subclass_of($className, 'Widget')) {
$obj = new $className();
return $obj->EditableSegment();
} else {
user_error("Bad widget class: $className", E_USER_WARNING);
return "Bad widget class name given";
}
} }
} }
/**
* @package cms
* @subpackage widgets
*/
class Widget_TreeDropdownField extends TreeDropdownField {
public function FieldHolder($properties = array()) {}
public function Field($properties = array()) {}
}

View File

@ -1,13 +1,13 @@
<?php <?php
/** /**
* Represents a set of widgets shown on a page. * Represents a set of widgets shown on a page.
* @package cms *
* @subpackage widgets * @package widgets
*/ */
class WidgetArea extends DataObject { class WidgetArea extends DataObject {
/** /**
*
* @var array * @var array
*/ */
private static $has_many = array( private static $has_many = array(
@ -21,24 +21,18 @@ class WidgetArea extends DataObject {
public $template = __CLASS__; public $template = __CLASS__;
/** /**
* Used in template instead of {@link Widgets()} * Used in template instead of {@link Widgets()} to wrap each widget in its
* to wrap each widget in its controller, making * controller, making it easier to access and process form logic and
* it easier to access and process form logic * actions stored in {@link WidgetController}.
* and actions stored in {@link Widget_Controller}.
* *
* @return SS_List Collection of {@link Widget_Controller} * @return SS_List - Collection of {@link WidgetController} instances.
*/ */
public function WidgetControllers() { public function WidgetControllers() {
$controllers = new ArrayList(); $controllers = new ArrayList();
foreach($this->ItemsToRender() as $widget) { foreach($this->ItemsToRender() as $widget) {
// find controller $controller = $widget->getController();
$controllerClass = '';
foreach(array_reverse(ClassInfo::ancestry($widget->class)) as $widgetClass) {
$controllerClass = "{$widgetClass}_Controller";
if(class_exists($controllerClass)) break;
}
$controller = new $controllerClass($widget);
$controller->init(); $controller->init();
$controllers->push($controller); $controllers->push($controller);
} }
@ -47,7 +41,6 @@ class WidgetArea extends DataObject {
} }
/** /**
*
* @return HasManyList * @return HasManyList
*/ */
public function Items() { public function Items() {
@ -55,7 +48,6 @@ class WidgetArea extends DataObject {
} }
/** /**
*
* @return HasManyList * @return HasManyList
*/ */
public function ItemsToRender() { public function ItemsToRender() {
@ -63,7 +55,6 @@ class WidgetArea extends DataObject {
} }
/** /**
*
* @return string - HTML * @return string - HTML
*/ */
public function forTemplate() { public function forTemplate() {

View File

@ -105,7 +105,7 @@
var parentRef=$(this); var parentRef=$(this);
$.ajax({ $.ajax({
'url': 'Widget_Controller/EditableSegment/' + className, 'url': 'WidgetController/EditableSegment/' + className,
'success' : function(response) {parentRef.insertWidgetEditor(response)} 'success' : function(response) {parentRef.insertWidgetEditor(response)}
}); });
}, },

View File

@ -1,4 +1,4 @@
<div class="WidgetAreaEditor" id="WidgetAreaEditor-$Name" name="$Name"<% if MaxWidgets %> maxwidgets="$MaxWidgets"<% end_if %>> <div class="WidgetAreaEditor field" id="WidgetAreaEditor-$Name" name="$Name"<% if MaxWidgets %> maxwidgets="$MaxWidgets"<% end_if %>>
<input type="hidden" id="$Name" name="$IdxField" value="$Value" /> <input type="hidden" id="$Name" name="$IdxField" value="$Value" />
<div class="availableWidgetsHolder"> <div class="availableWidgetsHolder">
<h2><% _t('AVAILABLE', 'Available Widgets') %></h2> <h2><% _t('AVAILABLE', 'Available Widgets') %></h2>

View File

@ -1,9 +1,10 @@
<?php <?php
/** /**
* @package cms * @package widgets
* @subpackage tests * @subpackage tests
*/ */
class WidgetControllerTest extends FunctionalTest { class WidgetControllerTest extends FunctionalTest {
protected static $fixture_file = 'WidgetControllerTest.yml'; protected static $fixture_file = 'WidgetControllerTest.yml';
protected $extraDataObjects = array( protected $extraDataObjects = array(
@ -33,9 +34,9 @@ class WidgetControllerTest extends FunctionalTest {
$widget = $this->objFromFixture('WidgetControllerTest_Widget', 'widget1'); $widget = $this->objFromFixture('WidgetControllerTest_Widget', 'widget1');
$this->get($page->URLSegment); $response = $this->get($page->URLSegment);
$response = $this->submitForm('Form_Form', null, array('TestValue'=>'Updated')); $response = $this->submitForm('Form_Form', null, array('TestValue'=>'Updated'));
$this->assertContains( $this->assertContains(
'TestValue: Updated', 'TestValue: Updated',
$response->getBody(), $response->getBody(),
@ -50,7 +51,7 @@ class WidgetControllerTest extends FunctionalTest {
} }
/** /**
* @package cms * @package widgets
* @subpackage tests * @subpackage tests
*/ */
class WidgetControllerTest_Widget extends Widget implements TestOnly { class WidgetControllerTest_Widget extends Widget implements TestOnly {
@ -60,10 +61,15 @@ class WidgetControllerTest_Widget extends Widget implements TestOnly {
} }
/** /**
* @package cms * @package widgets
* @subpackage tests * @subpackage tests
*/ */
class WidgetControllerTest_Widget_Controller extends Widget_Controller implements TestOnly { class WidgetControllerTest_WidgetController extends WidgetController implements TestOnly {
private static $allowed_actions = array(
'Form'
);
function Form() { function Form() {
$widgetform = new Form( $widgetform = new Form(
$this, $this,