diff --git a/admin/code/LeftAndMain.php b/admin/code/LeftAndMain.php index ee9fed2a5..0e488c48f 100644 --- a/admin/code/LeftAndMain.php +++ b/admin/code/LeftAndMain.php @@ -1170,7 +1170,7 @@ class LeftAndMain extends Controller implements PermissionProvider { * @return int */ public function currentPageID() { - if($this->request->requestVar('ID')) { + if($this->request->requestVar('ID') && is_numeric($this->request->requestVar('ID'))) { return $this->request->requestVar('ID'); } elseif (isset($this->urlParams['ID']) && is_numeric($this->urlParams['ID'])) { return $this->urlParams['ID']; diff --git a/admin/code/ModelAdmin.php b/admin/code/ModelAdmin.php index 0696324f7..2d3fee1e8 100644 --- a/admin/code/ModelAdmin.php +++ b/admin/code/ModelAdmin.php @@ -103,7 +103,7 @@ abstract class ModelAdmin extends LeftAndMain { $models = $this->getManagedModels(); if($this->request->param('ModelClass')) { - $this->modelClass = $this->request->param('ModelClass'); + $this->modelClass = $this->unsanitiseClassName($this->request->param('ModelClass')); } else { reset($models); $this->modelClass = key($models); @@ -118,7 +118,7 @@ abstract class ModelAdmin extends LeftAndMain { } public function Link($action = null) { - if(!$action) $action = $this->modelClass; + if(!$action) $action = $this->sanitiseClassName($this->modelClass); return parent::Link($action); } @@ -127,7 +127,7 @@ abstract class ModelAdmin extends LeftAndMain { $exportButton = new GridFieldExportButton('before'); $exportButton->setExportColumns($this->getExportFields()); $listField = GridField::create( - $this->modelClass, + $this->sanitiseClassName($this->modelClass), false, $list, $fieldConfig = GridFieldConfig_RecordEditor::create($this->stat('page_length')) @@ -150,7 +150,7 @@ abstract class ModelAdmin extends LeftAndMain { ); $form->addExtraClass('cms-edit-form cms-panel-padded center'); $form->setTemplate($this->getTemplatesWithSuffix('_EditForm')); - $form->setFormAction(Controller::join_links($this->Link($this->modelClass), 'EditForm')); + $form->setFormAction(Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'EditForm')); $form->setAttribute('data-pjax-fragment', 'CurrentForm'); $this->extend('updateEditForm', $form); @@ -199,7 +199,7 @@ abstract class ModelAdmin extends LeftAndMain { new RequiredFields() ); $form->setFormMethod('get'); - $form->setFormAction($this->Link($this->modelClass)); + $form->setFormAction($this->Link($this->sanitiseClassName($this->modelClass))); $form->addExtraClass('cms-search-form'); $form->disableSecurityToken(); $form->loadDataFrom($this->request->getVars()); @@ -234,13 +234,29 @@ abstract class ModelAdmin extends LeftAndMain { $forms->push(new ArrayData(array ( 'Title' => $options['title'], 'ClassName' => $class, - 'Link' => $this->Link($class), + 'Link' => $this->Link($this->sanitiseClassName($class)), 'LinkOrCurrent' => ($class == $this->modelClass) ? 'current' : 'link' ))); } return $forms; } + + /** + * Sanitise a model class' name for inclusion in a link + * @return string + */ + protected function sanitiseClassName($class) { + return str_replace('\\', '-', $class); + } + + /** + * Unsanitise a model class' name from a URL param + * @return string + */ + protected function unsanitiseClassName($class) { + return str_replace('-', '\\', $class); + } /** * @return array Map of class name to an array of 'title' (see {@link $managed_models}) @@ -350,7 +366,7 @@ abstract class ModelAdmin extends LeftAndMain { $fields, $actions ); - $form->setFormAction(Controller::join_links($this->Link($this->modelClass), 'ImportForm')); + $form->setFormAction(Controller::join_links($this->Link($this->sanitiseClassName($this->modelClass)), 'ImportForm')); $this->extend('updateImportForm', $form); @@ -419,7 +435,7 @@ abstract class ModelAdmin extends LeftAndMain { // Show the class name rather than ModelAdmin title as root node $models = $this->getManagedModels(); $items[0]->Title = $models[$this->modelClass]['title']; - $items[0]->Link = $this->Link($this->modelClass); + $items[0]->Link = $this->Link($this->sanitiseClassName($this->modelClass)); return $items; } diff --git a/admin/css/screen.css b/admin/css/screen.css index 456b06702..3b9953899 100644 --- a/admin/css/screen.css +++ b/admin/css/screen.css @@ -181,6 +181,7 @@ form.small .field input.text, form.small .field textarea, form.small .field sele .field .chzn-container-single .chzn-single div { width: 24px; } .field .chzn-container-single .chzn-single div b { background-position: 4px 0px; } .field input.hasDatepicker { width: 50%; max-width: 96px; } +.field input.month, .field input.day, .field input.year { width: 56px; } .field input.time { width: 64px; } .field.remove-splitter { border-bottom: none; box-shadow: none; } @@ -517,6 +518,8 @@ form.member-profile-form .ui-tabs-nav .ui-corner-all, form.member-profile-form . .cms .ui-widget-overlay { background-color: #000; background-image: none; } +.cms .ui-dialog { min-width: 570px; } +.cms .ui-dialog .htmleditorfield-dialog { min-width: 570px; } .cms .ui-dialog .ss-ui-dialog.ui-dialog-content { padding-top: 0px; } .ui-dialog { background: url("../images/textures/bg_cms_main_content.png") repeat left top #f0f3f4; border: 3px solid #000 !important; border-radius: 8px; overflow: visible; padding: 0; } @@ -564,7 +567,7 @@ body.cms-dialog { overflow: auto; background: url("../images/textures/bg_cms_mai .htmleditorfield-dialog .ui-tabs ul.ui-tabs-nav li.ui-state-active { background: #f0f3f4 url("../admin/images/textures/bg_cms_main_content.png") repeat left top; } .htmleditorfield-dialog .ui-tabs ul.ui-tabs-nav li.ui-state-active a { color: #444444; text-shadow: none; } .htmleditorfield-dialog .ui-tabs .ui-tabs-panel { padding: 0; } -.htmleditorfield-dialog .ss-insert-media, .htmleditorfield-dialog .Actions { padding: 8px 16px; } +.htmleditorfield-dialog .ss-insert-media, .htmleditorfield-dialog .Actions, .htmleditorfield-dialog .ss-insert-link { padding: 8px 16px; } .htmleditorfield-dialog .details .file-url { display: block; width: 450px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; } .htmleditorfield-dialog .details .cms-file-info .field { border: none; -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, 0); -moz-box-shadow: 0 0 0 rgba(0, 0, 0, 0); box-shadow: 0 0 0 rgba(0, 0, 0, 0); } .htmleditorfield-dialog .details .field { border-bottom: 1px solid rgba(201, 205, 206, 0.8); -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); box-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); } @@ -758,14 +761,14 @@ li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; } .cms-tree a.jstree-loading .jstree-pageicon { background: url(../images/throbber.gif) top left no-repeat; } /** Styles for the left hand side menu and header for the admin panels. Take into consideration CSS selector performance. @package framework @subpackage admin */ -.cms-logo-header { background-color: #00111d; position: relative; padding: 9px 8px 0 4px; line-height: 24px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMTExZCIvPjxzdG9wIG9mZnNldD0iNTAlIiBzdG9wLWNvbG9yPSIjMDAzMDUwIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAxMTFkIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #00111d), color-stop(50%, #003050), color-stop(100%, #00111d)); background-image: -webkit-linear-gradient(#00111d, #003050, #00111d); background-image: -moz-linear-gradient(#00111d, #003050, #00111d); background-image: -o-linear-gradient(#00111d, #003050, #00111d); background-image: -ms-linear-gradient(#00111d, #003050, #00111d); background-image: linear-gradient(#00111d, #003050, #00111d); } -.cms-logo-header span { color: white; white-space: nowrap; text-overflow: ellipsis; display: block; } +.cms-logo-header { background-color: #00111d; position: relative; padding: 0 8px 0 4px; line-height: 24px; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjUwJSIgeTE9IjAlIiB4Mj0iNTAlIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzAwMTExZCIvPjxzdG9wIG9mZnNldD0iNTAlIiBzdG9wLWNvbG9yPSIjMDAzMDUwIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjMDAxMTFkIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g'); background-size: 100%; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #00111d), color-stop(50%, #003050), color-stop(100%, #00111d)); background-image: -webkit-linear-gradient(#00111d, #003050, #00111d); background-image: -moz-linear-gradient(#00111d, #003050, #00111d); background-image: -o-linear-gradient(#00111d, #003050, #00111d); background-image: -ms-linear-gradient(#00111d, #003050, #00111d); background-image: linear-gradient(#00111d, #003050, #00111d); } +.cms-logo-header span { color: white; display: block; } .cms-logo-header span a { color: #3ebae0; display: inline; } -.cms-logo { border-bottom: 1px solid #03090c; height: 31px; overflow: hidden; padding: 0 0 0 4px; vertical-align: middle; font-size: 12px; } +.cms-logo { border-bottom: 1px solid #03090c; overflow: hidden; padding: 8px 0; position: relative; vertical-align: middle; font-size: 12px; } .cms-logo .version { display: none; } -.cms-logo a { display: inline-block; height: 24px; width: 24px; float: left; margin-right: 8px; background: url("../images/logo_small.png") no-repeat; text-indent: -9999em; padding-right: 7px; border-right: 1px solid #19435c; } -.cms-logo span { font-weight: bold; font-size: 14px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; padding-top: 1px; } +.cms-logo a { position: absolute; top: 8px; bottom: 8px; left: 4px; display: block; width: 24px; background: url("../images/logo_small.png") no-repeat left center; text-indent: -9999em; padding-right: 7px; border-right: 1px solid #19435c; } +.cms-logo span { font-weight: bold; font-size: 14px; line-height: 20px; padding: 2px 0; margin-left: 44px; } .cms-login-status { border-top: 1px solid #19435c; height: 24px; padding: 7px 4px 0 4px; overflow: hidden; line-height: 16px; font-size: 11px; } .cms-login-status .logout-link { display: inline-block; height: 16px; width: 16px; float: left; margin: 0 8px 0 3px; background: url('../images/sprites-32x32-sb47394f892.png') 0 -76px no-repeat; text-indent: -9999em; } @@ -777,6 +780,7 @@ li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; } .cms-menu.collapsed .cms-panel-header { width: 30px; } .cms-menu.collapsed .cms-panel-header span { display: none; } .cms-menu.collapsed .cms-menu-list { overflow-x: hidden; overflow-y: auto; } +.cms-menu.collapsed .cms-menu-list li { width: 100%; float: left; } .cms-menu.collapsed .cms-menu-list li span.text { display: none; } .cms-menu.collapsed .cms-menu-list li ul { display: none; } .cms-menu.collapsed.cms-panel .cms-panel-content { display: block; } diff --git a/admin/javascript/LeftAndMain.js b/admin/javascript/LeftAndMain.js index 1134714c0..387ece8f9 100644 --- a/admin/javascript/LeftAndMain.js +++ b/admin/javascript/LeftAndMain.js @@ -670,7 +670,7 @@ jQuery.noConflict(); showDetailView: function(url) { // Include any GET parameters from the current URL, as the view state might depend on it. // For example, a list prefiltered through external search criteria might be passed to GridField. - if(window.location.search) url += window.location.search; + url = $.path.addSearchParams(url, window.location.search.replace(/^\?/, '')); $('.cms-container').loadPanel(url); } }); diff --git a/admin/scss/_forms.scss b/admin/scss/_forms.scss index 706236f1d..3505438c8 100644 --- a/admin/scss/_forms.scss +++ b/admin/scss/_forms.scss @@ -241,6 +241,10 @@ form.small .field, .field.small { max-width: ($grid-x * 12); } + input.month, input.day, input.year { + width: ($grid-x * 7); + } + input.time { width: ($grid-x * 8); // smaller time field, since input is restricted } diff --git a/admin/scss/_menu.scss b/admin/scss/_menu.scss index e06165cac..bebd6798f 100644 --- a/admin/scss/_menu.scss +++ b/admin/scss/_menu.scss @@ -10,7 +10,7 @@ .cms-logo-header { background-color: darken($color-dark-bg, 10%); position: relative; - padding: $grid-y + 1 8px 0 4px; + padding: 0 8px 0 4px; line-height: 24px; @include background-image( @@ -19,8 +19,6 @@ span { color: $color-text-light; - white-space: nowrap; - text-overflow: ellipsis; display: block; a { @@ -32,9 +30,9 @@ .cms-logo { border-bottom: 1px solid darken($color-dark-separator, 20%); - height: 31px; overflow: hidden; - padding: 0 0 0 4px; + padding: $grid-y 0; + position: relative; vertical-align: middle; font-size: $font-base-size; @@ -43,12 +41,13 @@ } a { - display: inline-block; - height: 24px; + position: absolute; + top: $grid-y; + bottom: $grid-y; + left: 4px; + display: block; width: 24px; - float: left; - margin-right: 8px; - background: $application-logo-small no-repeat; + background: $application-logo-small no-repeat left center; text-indent: -9999em; padding-right: 7px; border-right: 1px solid $color-dark-separator; @@ -57,8 +56,9 @@ span { font-weight: bold; font-size: 14px; - @include hide-text-overflow(); - padding-top:1px; + line-height: 20px; + padding: 2px 0; + margin-left: 44px; } } @@ -112,12 +112,15 @@ .cms-menu-list { overflow-x: hidden; overflow-y: auto; - - li span.text { - display: none; - } - li ul { + li{ + width: 100%; + float: left; + span.text { + display: none; + } + ul { display: none; + } } } diff --git a/admin/scss/_style.scss b/admin/scss/_style.scss index d0498374b..14b3feba4 100644 --- a/admin/scss/_style.scss +++ b/admin/scss/_style.scss @@ -1280,8 +1280,14 @@ form.member-profile-form { background-image: none; } -.cms .ui-dialog .ss-ui-dialog.ui-dialog-content { - padding-top: 0px; //removes padding so that tabs are flush with header +.cms .ui-dialog{ + min-width:570px; + .htmleditorfield-dialog{ + min-width:570px; + } + .ss-ui-dialog.ui-dialog-content { + padding-top: 0px; //removes padding so that tabs are flush with header + } } // Elements with this class can either frame inline markup or an iframe, @@ -1544,7 +1550,7 @@ body.cms-dialog { padding:0; } } - .ss-insert-media, .Actions{ + .ss-insert-media, .Actions, .ss-insert-link{ padding:$grid-y $grid-x*2 ; } .details{ diff --git a/admin/scss/ie7.scss b/admin/scss/ie7.scss index fb678a98e..73a35d75e 100644 --- a/admin/scss/ie7.scss +++ b/admin/scss/ie7.scss @@ -211,7 +211,7 @@ table.ss-gridfield-table { right: 0; } } - + .ss-ui-button{ &.ss-gridfield-button-filter{ diff --git a/api/RSSFeed.php b/api/RSSFeed.php index 5f8f7ed6c..08bcbbe07 100644 --- a/api/RSSFeed.php +++ b/api/RSSFeed.php @@ -8,6 +8,7 @@ * @subpackage integration */ class RSSFeed extends ViewableData { + /** * Casting information for this object's methods. * Let's us use $Title.XML in templates @@ -80,6 +81,11 @@ class RSSFeed extends ViewableData { */ protected $etag; + /** + * @var string + */ + protected $template = 'RSSFeed'; + /** * Constructor * @@ -203,10 +209,29 @@ class RSSFeed extends ViewableData { function feedContent() { $prevState = SSViewer::get_source_file_comments(); SSViewer::set_source_file_comments(false); - $content = str_replace(' ', ' ', $this->renderWith('RSSFeed')); + $content = str_replace(' ', ' ', $this->renderWith($this->getTemplate())); SSViewer::set_source_file_comments($prevState); return $content; } + + /** + * Set the name of the template to use. Actual template will be resolved + * via the standard template inclusion process. + * + * @param string + */ + public function setTemplate($template) { + $this->template = $template; + } + + /** + * Returns the name of the template to use. + * + * @return string + */ + public function getTemplate() { + return $this->template; + } } /** diff --git a/control/HTTPRequest.php b/control/HTTPRequest.php index c26baf6cf..59c8a5875 100644 --- a/control/HTTPRequest.php +++ b/control/HTTPRequest.php @@ -228,10 +228,30 @@ class SS_HTTPRequest implements ArrayAccess { } /** + * Returns the URL used to generate the page + * + * @param bool $includeGetVars whether or not to include the get parameters\ + * * @return string */ - function getURL() { - return ($this->getExtension()) ? $this->url . '.' . $this->getExtension() : $this->url; + function getURL($includeGetVars = false) { + $url = ($this->getExtension()) ? $this->url . '.' . $this->getExtension() : $this->url; + + if ($includeGetVars) { + // if we don't unset $vars['url'] we end up with /my/url?url=my/url&foo=bar etc + + $vars = $this->getVars(); + unset($vars['url']); + + if (count($vars)) { + $url .= '?' . http_build_query($vars); + } + } + else if(strpos($url, "?") !== false) { + $url = substr($url, 0, strpos($url, "?")); + } + + return $url; } /** diff --git a/core/PaginatedList.php b/core/PaginatedList.php index 9addc8e48..5886182eb 100644 --- a/core/PaginatedList.php +++ b/core/PaginatedList.php @@ -13,6 +13,7 @@ class PaginatedList extends SS_ListDecorator { protected $pageLength = 10; protected $pageStart; protected $totalItems; + protected $limitItems = true; /** * Constructs a new paginated list instance around a list. @@ -144,13 +145,39 @@ class PaginatedList extends SS_ListDecorator { } } + /** + * Returns whether or not the underlying list is limited to the current + * pagination range when iterating. + * + * By default the limit method will be called on the underlying list to + * extract the subset for the current page. In some situations, if the list + * is custom generated and already paginated you don't want to additionally + * limit the list. You can use {@link setLimitItems} to control this. + * + * @return bool + */ + public function getLimitItems() { + return $this->limitItems; + } + + /** + * @param bool $limit + */ + public function setLimitItems($limit) { + $this->limitItems = (bool) $limit; + } + /** * @return IteratorIterator */ public function getIterator() { - return new IteratorIterator( - $this->list->limit($this->pageLength, $this->getPageStart()) - ); + if($this->limitItems) { + return new IteratorIterator( + $this->list->limit($this->pageLength, $this->getPageStart()) + ); + } else { + return new IteratorIterator($this->list); + } } /** diff --git a/css/GridField.css b/css/GridField.css index 13d4cf6e9..8f965bbff 100644 --- a/css/GridField.css +++ b/css/GridField.css @@ -71,7 +71,7 @@ .cms table.ss-gridfield-table tr th div.fieldgroup.filter-buttons { min-width: 0; } .cms table.ss-gridfield-table tr th div.fieldgroup.filter-buttons div { width: auto; display: inline; } .cms table.ss-gridfield-table tr th.main { white-space: nowrap; border-top: 1px solid #a4b4bf; border-left: 1px solid #a4b4bf; color: #fff; background: #98aab6; border-bottom: 1px solid rgba(0, 0, 0, 0.1); } -.cms table.ss-gridfield-table tr th.main span { text-shadow: rgba(0, 0, 0, 0.2) 0px -1px 0; padding-left: 8px; padding-right: 8px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; o-text-overflow: ellipsis; margin-right: 8px; } +.cms table.ss-gridfield-table tr th.main span { text-shadow: rgba(0, 0, 0, 0.2) 0px -1px 0; padding-left: 8px; padding-right: 8px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; -o-text-overflow: ellipsis; margin-right: 8px; } .cms table.ss-gridfield-table tr th.main.col-listChildrenLink { border-right: none; } .cms table.ss-gridfield-table tr th.extra, .cms table.ss-gridfield-table tr th.action { padding: 0; cursor: default; } .cms table.ss-gridfield-table tr th.extra { position: relative; background: #637276; background: rgba(0, 0, 0, 0.7); padding: 5px; border-top: rgba(0, 0, 0, 0.2); } diff --git a/css/debug.css b/css/debug.css new file mode 100644 index 000000000..f4be9277d --- /dev/null +++ b/css/debug.css @@ -0,0 +1,33 @@ +body { background-color: #eee; margin: 0; overflow-x: hidden; padding: 0; font-family: Helvetica,Arial,sans-serif; } + +.info { margin: 0 0 6px 0; padding: 18px; background-color: #003050; position: relative; line-height: 24px; color: #fff; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #002137), color-stop(10%, #003050), color-stop(90%, #003050), color-stop(100%, #002137)); background-image: -webkit-linear-gradient(#002137, #003050 10%, #003050 90%, #002137); background-image: -moz-linear-gradient(#002137, #003050 10%, #003050 90%, #002137); background-image: -o-linear-gradient(#002137, #003050 10%, #003050 90%, #002137); background-image: -ms-linear-gradient(#002137, #003050 10%, #003050 90%, #002137); background-image: linear-gradient(#002137, #003050 10%, #003050 90%, #002137); } +.info h1 { margin: 0 0 6px 0; padding: 0 32px 0 0; color: #fff; font-size: 24px; text-shadow: 0 1px #002137; line-height: 30px; background: url(../admin/images/logo_small.png) no-repeat right 3px; } +.info h3 { color: #7da4be; font-size: 16px; line-height: 18px; font-weight: normal; } +.info p { margin: 0; font-size: 14px; color: #fff; } +.info a { color: #fff; font-weight: bold; text-decoration: none; } +.info a:hover, .info a:active { color: #fff; text-decoration: underline; } + +.header { margin: 0; border-bottom: 6px solid #ccdef3; height: 23px; background-color: #666673; padding: 4px 0 2px 6px; } + +.trace, .build, .options { padding: 6px 12px; } +.trace li, .build li, .options li { font-size: 14px; margin: 6px 0; } + +a { color: #666; } +a:hover { color: #222; } +a:active { color: #111; } + +p { margin-bottom: 6px; } + +pre { margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #eee; border: 1px solid rgba(0, 0, 0, 0.08); color: #333; padding: 11px; overflow: auto; -webkit-border-radius: 4px; -moz-border-radius: 4px; -ms-border-radius: 4px; -o-border-radius: 4px; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } +pre span { color: #999; } +pre .error { color: #f00; } + +h2 { margin: 0 0 12px 0; } + +h3 { margin: 0 0 6px 0; color: #333; font-size: 18px; line-height: 24px; } + +ul { margin: 0 0 18px 0; padding: 0 0 0 18px; } + +.pass { margin-top: 18px; padding: 2px 20px 2px 40px; color: #006600; background: #E2F9E3; border: 1px solid #8DD38D; border-radius: 4px; } + +.fail { margin-top: 18px; padding: 2px 20px 2px 40px; color: #C80700; background: #FFE9E9; border: 1px solid #C80700; border-radius: 4px; } diff --git a/dev/Debug.php b/dev/Debug.php index e9a39ab38..194a0d90b 100644 --- a/dev/Debug.php +++ b/dev/Debug.php @@ -380,12 +380,15 @@ class Debug { */ static function showError($errno, $errstr, $errfile, $errline, $errcontext, $errtype) { if(!headers_sent()) { - $errText = "$errtype: \"$errstr\" at line $errline of $errfile"; + $errText = "$errtype at line $errline of $errfile"; $errText = str_replace(array("\n","\r")," ",$errText); + if(!headers_sent()) header($_SERVER['SERVER_PROTOCOL'] . " 500 $errText"); // if error is displayed through ajax with CliDebugView, use plaintext output - if(Director::is_ajax()) header('Content-Type: text/plain'); + if(Director::is_ajax()) { + header('Content-Type: text/plain'); + } } // Legacy error handling for customized prototype.js Ajax.Base.responseIsSuccess() diff --git a/dev/DebugView.php b/dev/DebugView.php index 404fb7e8e..7358bb870 100644 --- a/dev/DebugView.php +++ b/dev/DebugView.php @@ -90,21 +90,16 @@ class DebugView extends Object { ENT_COMPAT, 'UTF-8' ); + + $debugCSS = Controller::join_links( + Director::absoluteBaseURL(), + FRAMEWORK_DIR, + 'css/debug.css' + ); + echo '' . $url . ''; - echo ''; + echo ''; + echo ''; echo ''; } diff --git a/dev/DevelopmentAdmin.php b/dev/DevelopmentAdmin.php index 19a656778..d9beb471c 100644 --- a/dev/DevelopmentAdmin.php +++ b/dev/DevelopmentAdmin.php @@ -97,8 +97,10 @@ class DevelopmentAdmin extends Controller { $base = Director::baseURL(); echo '
"; $renderer->writeFooter(); // CLI mode diff --git a/dev/YamlFixture.php b/dev/YamlFixture.php index d0e2a9e53..f7e13b74d 100644 --- a/dev/YamlFixture.php +++ b/dev/YamlFixture.php @@ -84,19 +84,30 @@ class YamlFixture extends Object { * @var array */ protected $fixtureDictionary; - + + /** + * String containing fixture + * + * @var String + */ + protected $fixtureString; + /** * @param String Absolute file path, or relative path to {@link Director::baseFolder()} */ - function __construct($fixtureFile) { - if(!Director::is_absolute($fixtureFile)) $fixtureFile = Director::baseFolder().'/'. $fixtureFile; - - if(!file_exists($fixtureFile)) { - throw new InvalidArgumentException('YamlFixture::__construct(): Fixture path "' . $fixtureFile . '" not found'); + function __construct($fixture) { + if(false !== strpos($fixture, "\n")) { + $this->fixtureString = $fixture; + } else { + if(!Director::is_absolute($fixture)) $fixture = Director::baseFolder().'/'. $fixture; + + if(!file_exists($fixture)) { + throw new InvalidArgumentException('YamlFixture::__construct(): Fixture path "' . $fixture . '" not found'); + } + + $this->fixtureFile = $fixture; } - $this->fixtureFile = $fixtureFile; - parent::__construct(); } @@ -106,6 +117,13 @@ class YamlFixture extends Object { function getFixtureFile() { return $this->fixtureFile; } + + /** + * @return String Fixture string + */ + function getFixtureString() { + return $this->fixtureString; + } /** * Get the ID of an object from the fixture. @@ -162,7 +180,11 @@ class YamlFixture extends Object { DataObject::set_validation_enabled(false); $parser = new Spyc(); - $fixtureContent = $parser->loadFile($this->fixtureFile); + if (isset($this->fixtureString)) { + $fixtureContent = $parser->load($this->fixtureString); + } else { + $fixtureContent = $parser->loadFile($this->fixtureFile); + } $this->fixtureDictionary = array(); foreach($fixtureContent as $dataClass => $items) { @@ -228,12 +250,12 @@ class YamlFixture extends Object { } } $obj->write(); - //If LastEdited was set in the fixture, set it here - if (array_key_exists('LastEdited', $fields)) { - $manip = array($dataClass => array("command" => "update", "id" => $obj->id, - "fields" => array("LastEdited" => "'".$this->parseFixtureVal($fields['LastEdited'])."'"))); - DB::manipulate($manip); - } + //If LastEdited was set in the fixture, set it here + if (array_key_exists('LastEdited', $fields)) { + $manip = array($dataClass => array("command" => "update", "id" => $obj->id, + "fields" => array("LastEdited" => "'".$this->parseFixtureVal($fields['LastEdited'])."'"))); + DB::manipulate($manip); + } } } diff --git a/docs/en/changelogs/3.0.0.md b/docs/en/changelogs/3.0.0.md index 0f3237478..edc290af0 100644 --- a/docs/en/changelogs/3.0.0.md +++ b/docs/en/changelogs/3.0.0.md @@ -1,4 +1,4 @@ -# 3.0.0 (unreleased) # +# 3.0.0 (Released 28 June 2012) # ## Overview ## @@ -22,6 +22,20 @@ * FormField classes now have their own HTML templates * Moved functionality to modules: Widget, RestfulServer, SapphireSoapServer, Translatable, IPRestrictions, PageComment, HomepageForDomain +## Detailed change logs ## + +The detailed change logs are broken down by pre-release: + + * [3.0.0-rc3](/changelogs/rc/3.0.0-rc3) - 27 June 2012 + * [3.0.0-rc2](changelogs/rc/3.0.0-rc2) - 26 June 2012 + * [3.0.0-rc1](/changelogs/rc/3.0.0-rc1) - 18 June 2012 + * [3.0.0-beta3](/changelogs)/beta/3.0.0-beta3) - 28 May 2012 + * [3.0.0-beta2](/changelogs/beta/3.0.0-beta2) - 20 April 2012 + * [3.0.0-beta1](/changelogs/beta/3.0.0-beta1) - 12 March 2012 + * [3.0.0-alpha2](/changelogs/alpha/3.0.0-alpha2) - 12 January 2012 + * [3.0.0-alpha1](/changelogs/alpha/3.0.0-alpha1) - 1 November 2011 + * [3.0.0-pr1](/changelogs/pr/3.0.0-pr1) - 2 May 2011 + ## Upgrading ## ### Common Upgrade Tasks @@ -283,7 +297,7 @@ unless the `FullTextSearch` feature is enabled. In order to disable this behavio you have to add the following code to your `_config.php` BEFORE running a `dev/build`: :::php - DataObject::$create_table_options['MySQLDatabase] = 'ENGINE=MyISAM'; + DataObject::$create_table_options['MySQLDatabase'] = 'ENGINE=MyISAM'; As with any SilverStripe upgrade, we recommend database backups before calling `dev/build`. See [mysql.com](http://dev.mysql.com/doc/refman/5.5/en/converting-tables-to-innodb.html) for details on the conversion. @@ -614,7 +628,7 @@ Note that its just necessary if SilverStripe is used in a language other than th The SS_Report::register() method is deprecated. You no longer need to explicitly register reports. The CMS now automatically picks up and adds all Report classes to the list of reports in ReportAdmin. You can choose to exclude certain reports by using the SS_Report::add_excluded_reports() method. -fe + ### Removed the ability use a SQLQuery object in a SS_Report You can no longer create reports that using the deprecated DataObject->buildSQL and DataObject->extendedSQL diff --git a/docs/en/changelogs/index.md b/docs/en/changelogs/index.md index d0dc521ba..485bf6b4c 100644 --- a/docs/en/changelogs/index.md +++ b/docs/en/changelogs/index.md @@ -9,7 +9,10 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins ## Stable Releases - * [3.0.0](3.0.0) - unreleased + * [3.0.0](3.0.0) - 28 June 2012 + + + * [2.4.7](2.4.7) - 1 February 2012 * [2.4.6](2.4.6) - 18 October 2011 * [2.4.5](2.4.5) - 2 February 2011 @@ -18,10 +21,16 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins * [2.4.2](2.4.2) - 22 September 2010 * [2.4.1](2.4.1) - 23 July 2010 * [2.4.0](2.4.0) + + + * [2.3.13](2.3.13) - 1 February 2012 * [2.3.12](2.3.12) - 17 October 2011 * [2.3.11](2.3.11) - 2 February 2011 * [2.3.10](2.3.10) - 21 December 2010 + + + * [2.3.9](2.3.9) - 11 November 2010 * [2.3.8](2.3.8) - 23 July 2010 * [2.3.7](2.3.7) - 18 March 2010 @@ -32,26 +41,40 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins * [2.3.2](2.3.2) - 18 June 2009 * [2.3.1](2.3.1) - 19 March 2009 * [2.3.0](2.3.0) - 23 February 2009 + + + * [2.2.4](2.2.4) - 20 March 2009 * [2.2.3](2.2.3) - ~31 October 2008 * [2.2.2](2.2.2) - 22 May 2008 * [2.2.1](2.2.1) - 21 December 2007 * [2.2.0](2.2.0) - 28 November 2007 + + + * [2.1.1](2.1.1) - 2 November 2007 * [2.1.0](2.1.0) - 2 October 2007 + + + * [2.0.2](2.0.2) - 14 July 2007 * [2.0.1](2.0.1) - 17 April 2007 * 2.0.0 - 3 February 2007 (initial release) ## Alpha/beta/release candidate ## - * [3.0.0-rc1](beta/3.0.0-rc1) +* [3.0.0-rc2](rc/3.0.0-rc3) - 27 June 2012 + * [3.0.0-rc2](rc/3.0.0-rc2) - 26 June 2012 + * [3.0.0-rc1](rc/3.0.0-rc1) - 18 June 2012 * [3.0.0-beta3](beta/3.0.0-beta3) - 28 May 2012 * [3.0.0-beta2](beta/3.0.0-beta2) - 20 April 2012 * [3.0.0-beta1](beta/3.0.0-beta1) - 12 March 2012 * [3.0.0-alpha2](alpha/3.0.0-alpha2) - 12 January 2012 * [3.0.0-alpha1](alpha/3.0.0-alpha1) - 1 November 2011 - * [3.0.0-pr1](pr/3.0.0-pr1) - unreleased + * [3.0.0-pr1](pr/3.0.0-pr1) - 2 May 2011 + + + * [2.4.5-rc1](rc/2.4.5-rc1) - 31 January 2011 * [2.4.4-rc2](rc/2.4.4-rc2) - 20 December 2010 * [2.4.4-rc1](rc/2.4.4-rc1) - 10 December 2010 @@ -67,6 +90,9 @@ For information on how to upgrade to newer versions consult the [upgrading](/ins * [2.4.0-beta2](beta/2.4.0-beta2) - 17 March 2010 * [2.4.0-beta1](beta/2.4.0-beta1) - 29 January 2010 * [2.4.0-alpha1](alpha/2.4.0-alpha1) - 11 November 2009 + + + * [2.3.11-rc1](rc/2.3.11-rc1) - 31 January 2011 * [2.3.10-rc2](rc/2.3.10-rc2) - 20 December 2010 * [2.3.10-rc1](rc/2.3.10-rc1) - 10 December 2010 diff --git a/docs/en/changelogs/rc/3.0.0-rc1.md b/docs/en/changelogs/rc/3.0.0-rc1.md index bb16a24d1..3eb96660e 100644 --- a/docs/en/changelogs/rc/3.0.0-rc1.md +++ b/docs/en/changelogs/rc/3.0.0-rc1.md @@ -1,4 +1,4 @@ -# 3.0.0-rc1 # +# 3.0.0-rc1 # ## Overview ## diff --git a/docs/en/changelogs/rc/3.0.0-rc2.md b/docs/en/changelogs/rc/3.0.0-rc2.md new file mode 100644 index 000000000..e2b7bd2a3 --- /dev/null +++ b/docs/en/changelogs/rc/3.0.0-rc2.md @@ -0,0 +1,64 @@ +# 3.0.0-rc2 # + +## Overview ## + +### CMS ### + + * Updated translations (from new getlocalization.com source, in new YML format). Big thanks to all the translators! + * Fixed UI inconsistencies around tab display and media insertion + +### Framework ### + + * Fixed dependency regressions to cms module, allow running module standalone again + * Fixed nested field controller usage, e.g. UploadField inside a GridField + +## Upgrading ## + +See [3.0.0](/changelogs/3.0.0) for previous details. + +## Changelog ## + +### Features and Enhancements + + * 2012-06-22 [00f66e2](https://github.com/silverstripe/silverstripe-installer/commit/00f66e2) getlocalization build support (Ingo Schommer) + * 2012-06-19 [d82b67c](https://github.com/silverstripe/sapphire/commit/d82b67c) remove dependencies between framework tests and cms module. (Will Rossiter) + +### Bugfixes + + * 2012-06-24 [119da09](https://github.com/silverstripe/sapphire/commit/119da09) ed DataList filtering and excluding by ID. (Andrew Short) + * 2012-06-22 [682a6a0](https://github.com/silverstripe/sapphire/commit/682a6a0) "Insert media" loading indicator (fixes #7542) (Ingo Schommer) + * 2012-06-22 [5713a37](https://github.com/silverstripe/sapphire/commit/5713a37) Alignment of http label in insert media (Naomi Guyer) + * 2012-06-22 [cffb952](https://github.com/silverstripe/sapphire/commit/cffb952) Tab colour htmleditor in IE7 (Naomi Guyer) + * 2012-06-22 [f3933aa](https://github.com/silverstripe/sapphire/commit/f3933aa) Make entire tab clickable in htmleditor (fixes #7407) (Naomi Guyer) + * 2012-06-22 [8c05f35](https://github.com/silverstripe/silverstripe-cms/commit/8c05f35) Add batch handler status messages (fixes #7427) (Hamish Friedlander) + * 2012-06-22 [0346923](https://github.com/silverstripe/sapphire/commit/0346923) Add batch handler status messages (fixes #7427) (Hamish Friedlander) + * 2012-06-22 [0bea697](https://github.com/silverstripe/sapphire/commit/0bea697) Make themedCSS use {theme}_{module}/css/{name}.css files if they exist (Hamish Friedlander) + * 2012-06-22 [daa226a](https://github.com/silverstripe/sapphire/commit/daa226a) Fix trac ticket #7476 (Hamish Friedlander) + * 2012-06-21 [6503090](https://github.com/silverstripe/sapphire/commit/6503090) Add validation to fix open.silverstripe.org ticket #7494 (Hamish Friedlander) + * 2012-06-20 [c6039ae](https://github.com/silverstripe/sapphire/commit/c6039ae) When updating the tree from EditForm, ensure we only change the text of the tree node for the first .text element, instead of the nested ones. (Sean Harvey) + * 2012-06-20 [d55eb13](https://github.com/silverstripe/silverstripe-cms/commit/d55eb13) Ensure that we only select the first item when updating tree nodes, otherwise we change the title for all nested node's text as well. (Sean Harvey) + * 2012-06-20 [ef11a0d](https://github.com/silverstripe/sapphire/commit/ef11a0d) Fix trac ticket 7081 (Hamish Friedlander) + * 2012-06-18 [42d40a7](https://github.com/silverstripe/sapphire/commit/42d40a7) Turn off filters on IE Nav icons (fixes #7471) (Naomi Guyer) + * 2012-06-18 [50ed4f5](https://github.com/silverstripe/sapphire/commit/50ed4f5) Show text for collapsed side panel in IE8 and 9 (fixes #7469) (Naomi Guyer) + +### Minor changes + + * 2012-06-25 [fd881d6](https://github.com/silverstripe/sapphire/commit/fd881d6) Add test using a namespaced class for DataList::filter() and DataList::exclude() (Sam Minnee) + * 2012-06-25 [9f7ec96](https://github.com/silverstripe/silverstripe-cms/commit/9f7ec96) Updated translations (Ingo Schommer) + * 2012-06-25 [2a3d387](https://github.com/silverstripe/sapphire/commit/2a3d387) Updated translations (Ingo Schommer) + * 2012-06-22 [17303c6](https://github.com/silverstripe/silverstripe-installer/commit/17303c6) getlocalization API URL (Ingo Schommer) + * 2012-06-22 [acb0e94](https://github.com/silverstripe/silverstripe-cms/commit/acb0e94) Updated translations master (Ingo Schommer) + * 2012-06-22 [86a2ff5](https://github.com/silverstripe/sapphire/commit/86a2ff5) Updated translations master (Ingo Schommer) + * 2012-06-20 [3a7128d](https://github.com/silverstripe/sapphire/commit/3a7128d) Fixed phpdocs (Ingo Schommer) + * 2012-06-20 [211ce61](https://github.com/silverstripe/silverstripe-cms/commit/211ce61) move route to cms module (Will Rossiter) + * 2012-06-19 [facc8ba](https://github.com/silverstripe/silverstripe-cms/commit/facc8ba) restore backlinkcount into cms module. (removed from framework in https://github.com/willrossi/sapphire/commit/266a61221cbf3d128f8aa0248726352dba91c19b) (Will Rossiter) + * 2012-06-19 [8b43780](https://github.com/silverstripe/sapphire/commit/8b43780) remove dependency on RootURLController and show a default Controller template as a failback. (Will Rossiter) + * 2012-06-19 [eb2a042](https://github.com/silverstripe/sapphire/commit/eb2a042) exclude functional tests when running just framework module tests (Will Rossiter) + * 2012-06-18 [0352a91](https://github.com/silverstripe/sapphire/commit/0352a91) Fixed filename glitch in changelog (Sam Minnee) + +### Other + + * 2012-06-22 [1b57689](https://github.com/silverstripe/sapphire/commit/1b57689) BUG: exclude() clears previously selected filters() (Trac #7529) (Sam Minnee) + * 2012-06-22 [3fbf572](https://github.com/silverstripe/silverstripe-cms/commit/3fbf572) REMOVE: Remove action handler for widgets. (Trac #7174) (Sam Minnee) + * 2012-06-22 [6be8602](https://github.com/silverstripe/silverstripe-cms/commit/6be8602) BUG: Update MenuTitle whenever Title is changed and the value of Title used to be. Bubble the change to update LHS tree. (Trac #7507) (Sam Minnee) + * 2012-06-19 [3eff92a](https://github.com/silverstripe/sapphire/commit/3eff92a) ChangedBUG FIX: IE filter buttons (fixes #7501) (Naomi Guyer) diff --git a/docs/en/changelogs/rc/3.0.0-rc3.md b/docs/en/changelogs/rc/3.0.0-rc3.md new file mode 100644 index 000000000..f2e831c0e --- /dev/null +++ b/docs/en/changelogs/rc/3.0.0-rc3.md @@ -0,0 +1,25 @@ +# 3.0.0-rc3 # + +## Overview ## + +RC3 introduces a small fixrd to the installer and to some language packs + +## Upgrading ## + +See [3.0.0](/changelogs/3.0.0) for previous details. + +## Changelog ## + +### Features and Enhancements + + * 2012-06-27 [2598f65](https://github.com/silverstripe/sapphire/commit/2598f65) Enable display_errors = on in the installer to assist with diagnosis. (Sam Minnee) + +### Bugfixes + + * 2012-06-26 [6ef4f9a](https://github.com/silverstripe/sapphire/commit/6ef4f9a) Fix increase_memory_limit_to() to reduce installation errors. (Sam Minnee) + +### Other + + * 2012-06-27 [1ca61a5](https://github.com/silverstripe/silverstripe-installer/commit/1ca61a5) Updated 'phing changelog' to work with new commit tags (Ingo Schommer) + * 2012-06-25 [bfa436b](https://github.com/silverstripe/sapphire/commit/bfa436b) Updated translations (Ingo Schommer) + * 2012-06-25 [25ee305](https://github.com/silverstripe/silverstripe-cms/commit/25ee305) Updated translations (Ingo Schommer) diff --git a/docs/en/howto/csv-import.md b/docs/en/howto/csv-import.md index a0aa50ba4..5c8253397 100644 --- a/docs/en/howto/csv-import.md +++ b/docs/en/howto/csv-import.md @@ -184,9 +184,7 @@ Sample implementation of a custom loader. Assumes a CSV-file in a certain format $obj->LastName = $parts[1]; } public static function getTeamByTitle(&$obj, $val, $record) { - $SQL_val = Convert::raw2sql($val); - return DataObject::get_one( - 'FootballTeam', "Title = '{$SQL_val}'" + return FootballTeam::get()->filter('Title', $val)->First(); ); } } diff --git a/docs/en/howto/dynamic-default-fields.md b/docs/en/howto/dynamic-default-fields.md index 66ee9af54..5e03d7e8e 100644 --- a/docs/en/howto/dynamic-default-fields.md +++ b/docs/en/howto/dynamic-default-fields.md @@ -2,7 +2,7 @@ The [api:DataObject::$defaults] array allows you to specify simple static values to be the default value for when a record is created, but in many situations default values needs to be dynamically calculated. In order to do this, the -[api:DataObjectSet->populateDefaults()] method will need to be overloaded. +`[api:DataObject->populateDefaults()]` method will need to be overloaded. This method is called whenever a new record is instantiated, and you must be sure to call the method on the parent object! @@ -13,7 +13,6 @@ A simple example is to set a field to the current date and time: /** * Sets the Date field to the current date. */ - public function populateDefaults() { $this->Date = date('Y-m-d'); parent::populateDefaults(); @@ -27,7 +26,6 @@ methods. For example: * This method combines the Title of the parent object with the Title of this * object in the FullTitle field. */ - public function populateDefaults() { if($parent = $this->Parent()) { $this->FullTitle = $parent->Title . ': ' . $this->Title; diff --git a/docs/en/howto/extend-cms-interface.md b/docs/en/howto/extend-cms-interface.md index 51a3bf8ed..173035868 100644 --- a/docs/en/howto/extend-cms-interface.md +++ b/docs/en/howto/extend-cms-interface.md @@ -85,9 +85,8 @@ Create a new file called `zzz_admin/code/BookmarkedPageExtension.php` and insert :::php array('IsBookmarked' => 'Boolean')); - } + static $db = array('IsBookmarked' => 'Boolean'); + public function updateCMSFields(&$fields) { $fields->addFieldToTab('Root.Main', new CheckboxField('IsBookmarked', "Show in CMS bookmarks?") @@ -115,7 +114,7 @@ Add the following code to a new file `zzz_admin/code/BookmarkedLeftAndMainExtens where('"IsBookmarked" = 1'); + return Page::get()->filter("IsBookmarked", 1); } } @@ -130,9 +129,9 @@ and replace it with the following: :::ss ## Summary diff --git a/docs/en/howto/grouping-dataobjectsets.md b/docs/en/howto/grouping-dataobjectsets.md index 4be1c31eb..5a37101ae 100644 --- a/docs/en/howto/grouping-dataobjectsets.md +++ b/docs/en/howto/grouping-dataobjectsets.md @@ -1,15 +1,22 @@ -# Grouping Data Object Sets +# Grouping lists of records -The [api:DataObjectSet] class has a number of methods useful for grouping objects by fields. Together with sorting this -can be used to break up long lists of data into more manageable sub-sections. +The [api:SS_List] class is designed to return a flat list of records. +These lists can get quite long, and hard to present on a single list. +[Pagination](/howto/pagination) is one way to solve this problem, +by splitting up the list into multiple pages. -The [api:DataObjectSet->groupBy()] method takes a field name as the single argument, and breaks the set up into a number -of arrays, where each array contains only objects with the same value of that field. The [api:DataObjectSet->GroupedBy()] -method builds on this and returns the same data in a template-friendly format. +In this howto, we present an alternative to pagination: +Grouping a list by various criteria, through the `[api:GroupedList]` class. +This class is a `[api:SS_ListDecorator]`, which means it wraps around a list, +adding new functionality. + +It provides a `groupBy()` method, which takes a field name, and breaks up the managed list +into a number of arrays, where each array contains only objects with the same value of that field. +Similarly, the `GroupedBy()` method builds on this and returns the same data in a template-friendly format. ## Grouping Sets By First Letter -This example deals with breaking up a [api:DataObjectSet] into sub-headings by the first letter. +This example deals with breaking up a [api:SS_List] into sub-headings by the first letter. Let's say you have a set of Module objects, each representing a SilverStripe module, and you want to output a list of these in alphabetical order, with each letter as a heading; something like the following list: @@ -23,31 +30,27 @@ these in alphabetical order, with each letter as a heading; something like the f * Database Plumber * ... -The first step is to set up the basic data model, along with a method that returns the first letter of the title. This +The first step is to set up the basic data model, +along with a method that returns the first letter of the title. This will be used both for grouping and for the title in the template. :::php class Module extends DataObject { - public static $db = array( - 'Title' => 'Varchar(255)' + 'Title' => 'Text' ); - // ... - /** * Returns the first letter of the module title, used for grouping. - * * @return string */ public function getTitleFirstLetter() { return $this->Title[0]; } - } -The next step is to create a method or variable that will contain/return all the Module objects, sorted by title. For -this example this will be a method on the Page class. +The next step is to create a method or variable that will contain/return all the objects, +sorted by title. For this example this will be a method on the `Page` class. :::php class Page extends SiteTree { @@ -56,88 +59,89 @@ this example this will be a method on the Page class. /** * Returns all modules, sorted by their title. - * - * @return DataObjectSet + * @return GroupedList */ - public function getModules() { - return DataObject::get('Module', null, '"Title"'); + public function getGroupedModules() { + return GroupedList::create(Module::get()->sort('Title')); } } -The final step is to render this into a template. The [api:DataObjectSet->GroupedBy()] method breaks up the set into -a number of sets, grouped by the field that is passed as the parameter. In this case, the getTitleFirstLetter method -defined earlier is used to break them up. +The final step is to render this into a template. The `GroupedBy()` method breaks up the set into +a number of sets, grouped by the field that is passed as the parameter. +In this case, the `getTitleFirstLetter()` method defined earlier is used to break them up. :::ss - // Modules list grouped by TitleFirstLetter + <%-- Modules list grouped by TitleFirstLetter --%>

Modules

- <% control Modules.GroupedBy(TitleFirstLetter) %> + <% loop GroupedModules.GroupedBy(TitleFirstLetter) %>

$TitleFirstLetter

- <% end_control %> + <% end_loop %> ## Grouping Sets By Month -Grouping a set by month is a very similar process. The only difference would be to sort the records by month name, and -then create a method on the DataObject that returns the month name, and pass that to the [api:DataObjectSet->GroupedBy()] -call. +Grouping a set by month is a very similar process. +The only difference would be to sort the records by month name, and +then create a method on the DataObject that returns the month name, +and pass that to the [api:GroupedList->GroupedBy()] call. -Again, the first step is to create a method on the class in question that will be displayed in a list. For this example, -a [api:DataObject] called NewsItem will be used. This will have a method which returns the month it was posted in: +We're reusing our example `Module` object, +but grouping by its built-in `Created` property instead, +which is automatically set when the record is first written to the database. +This will have a method which returns the month it was posted in: :::php - class NewsItem extends DataObject { - - public static $db = array( - 'Title' => 'Varchar(255)', - 'Date' => 'Date' - ); + class Module extends DataObject { // ... /** * Returns the month name this news item was posted in. - * * @return string */ - public function getMonthPosted() { - return date('F', strtotime($this->Date)); + public function getMonthCreated() { + return date('F', strtotime($this->Created)); } } -The next step is to create a method that will return all the News records that exist, sorted by month name from -January to December. This can be accomplshed by sorting by the Date field: +The next step is to create a method that will return all records that exist, +sorted by month name from January to December. This can be accomplshed by sorting by the `Created` field: :::php class Page extends SiteTree { - + + // ... + /** * Returns all news items, sorted by the month they were posted - * - * @return DataObjectSet + * @return GroupedList */ - public function getNewsItems() { - return DataObject::get('NewsItem', null, '"Date"'); + public function getGroupedModulesByDate() { + return GroupedList::create(Module::get()->sort('Created')); } } -The final step is the render this into the template using the [api:DataObjectSet->GroupedBy()] method. +The final step is the render this into the template using the [api:GroupedList->GroupedBy()] method. :::ss // Modules list grouped by the Month Posted

Modules

- <% control NewsItems.GroupedBy(MonthPosted) %> -

$MonthPosted

+ <% loop GroupedModulesByDate.GroupedBy(MonthCreated) %> +

$MonthCreated

- <% end_control %> \ No newline at end of file + <% end_loop %> + +## Related + + * [Howto: "Pagination"](/howto/pagination) \ No newline at end of file diff --git a/docs/en/howto/index.md b/docs/en/howto/index.md index 6c5e979a1..f57f0ab75 100644 --- a/docs/en/howto/index.md +++ b/docs/en/howto/index.md @@ -8,7 +8,7 @@ the language and functions which are used in the guides. * [Import CSV Data](csv-import). Build a simple CSV importer using either [api:ModelAdmin] or a custom controller * [Dynamic Default Fields](dynamic-default-fields). Pre populate a [api:DataObject] with data. -* [Grouping DataObjectSets](grouping-dataobjectsets). Group results in a [api:DataObjectSet] to create sub sections. +* [Grouping Lists](grouping-dataobjectsets). Group results in a [api:SS_List] to create sub sections. * [PHPUnit Configuration](phpunit-configuration). How to setup your testing environment with PHPUnit * [Extend the CMS Interface](extend-cms-interface). * [How to customize CMS Tree](customize-cms-tree). diff --git a/docs/en/howto/pagination.md b/docs/en/howto/pagination.md index 195b6adee..61345de58 100644 --- a/docs/en/howto/pagination.md +++ b/docs/en/howto/pagination.md @@ -1,12 +1,12 @@ # Paginating A List -Adding pagination to a `[api:DataList]` or `[DataObjectSet]` is quite simple. All +Adding pagination to a `[api:SS_List]` is quite simple. All you need to do is wrap the object in a `[api:PaginatedList]` decorator, which takes care of fetching a sub-set of the total list and presenting it to the template. In order to create a paginated list, you can create a method on your controller -that first creates a `DataList` that will return all pages, and then wraps it -in a `[api:PaginatedSet]` object. The `PaginatedList` object is also passed the +that first creates a `SS_List` that will return all pages, and then wraps it +in a `[api:PaginatedList]` object. The `PaginatedList` object is also passed the HTTP request object so it can read the current page information from the "?start=" GET var. @@ -18,24 +18,27 @@ information. * Returns a paginated list of all pages in the site. */ public function PaginatedPages() { - $pages = DataList::create('Page'); - return new PaginatedList($pages, $this->request); + return new PaginatedList(Page::get(), $this->request); } +Note that the concept of "pages" used in pagination does not necessarily +mean that we're dealing with `Page` classes, its just a term to describe +a sub-collection of the list. + ## Setting Up The Template Now all that remains is to render this list into a template, along with pagination controls. There are two ways to generate pagination controls: -`[api:PaginatedSet->Pages()]` and `[api:PaginatedSet->PaginationSummary()]`. In +`[api:PaginatedList->Pages()]` and `[api:PaginatedList->PaginationSummary()]`. In this example we will use `PaginationSummary()`. The first step is to simply list the objects in the template: :::ss By default this will display 10 pages at a time. The next step is to add pagination @@ -46,7 +49,7 @@ controls below this so the user can switch between pages: <% if PaginatedPages.NotFirstPage %> <% end_if %> - <% control PaginatedPages.Pages %> + <% loop PaginatedPages.Pages %> <% if CurrentBool %> $PageNum <% else %> @@ -56,11 +59,23 @@ controls below this so the user can switch between pages: ... <% end_if %> <% end_if %> - <% end_control %> + <% end_loop %> <% if PaginatedPages.NotLastPage %> <% end_if %> <% end_if %> If there is more than one page, this block will render a set of pagination -controls in the form `[1] ... [3] [4] [[5]] [6] [7] ... [10]`. \ No newline at end of file +controls in the form `[1] ... [3] [4] [[5]] [6] [7] ... [10]`. + +## Paginating Custom Lists + +In some situations where you are generating the list yourself, the underlying +list will already contain only the items that you wish to display on the current +page. In this situation the automatic limiting done by `[api:PaginatedList]` +will break the pagination. You can disable automatic limiting using the +`[api:PaginatedList->setLimitItems()]` method when using custom lists. + +## Related + + * [Howto: "Grouping Lists"](/howto/grouping-dataobjectsets) \ No newline at end of file diff --git a/docs/en/installation/nginx.md b/docs/en/installation/nginx.md index 7f70ac27c..833989bd4 100644 --- a/docs/en/installation/nginx.md +++ b/docs/en/installation/nginx.md @@ -21,10 +21,13 @@ Now you need to setup a virtual host in Nginx with the following configuration s error_page 404 /framework/main.php; location ~ \.php$ { + include fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /home/yoursite.com/httpdocs$fastcgi_script_name; - include fastcgi_params; + fastcgi_buffer_size 32k; + fastcgi_buffers 4 32k; + fastcgi_busy_buffers_size 64k; } } diff --git a/docs/en/installation/upgrading.md b/docs/en/installation/upgrading.md index f77f29c7a..ff0ad5b16 100644 --- a/docs/en/installation/upgrading.md +++ b/docs/en/installation/upgrading.md @@ -1,40 +1,41 @@ # Upgrading -Usually an update or upgrade your SilverStripe installation just means overwriting files and updating your -database-schema. Please see your [upgrade notes and changelogs](/changelogs). +Usually an update or upgrade your SilverStripe installation just means +overwriting files and updating your database-schema. + +See our [upgrade notes and changelogs](/changelogs) for release-specific information. ## Process -Never update a website on the live server without trying it on a development copy first. - * Check if any modules (e.g. blog or forum) in your installation are compatible and need to be upgraded as well -* Backup your database -* Backup your website +* Backup your database content +* Backup your webroot files * Download the new release and uncompress it to a temporary folder * Leave custom folders like *mysite* or *themes* in place. -* Identify system folders in your webroot (`cms`, `framework` and any additional modules). -* Delete existing system folders (or move them outside of your webroot) -* Extract and replace system folders from your download (Deleting instead of "copying over" existing folders -ensures that files removed from the new SilverStripe release are not persisting in your installation) +* Identify system folders in your webroot (`cms`, `framework`, `sapphire` and any additional modules). +* Delete existing system folders (or move them outside of your webroot) +* Extract and replace system folders from your download (Deleting instead of "copying over" existing folders ensures that files removed from the new SilverStripe release are not persisting in your installation) +* Visit http://yoursite.com/dev/build/?flush=1 to rebuild the website database +* Check if you need to adapt your code to changed PHP APIs +* Check if you have overwritten any core templates or styles which might need an update +* See [common-problems](common-problems) for a list of likely mistakes that could happen during an upgrade. -* Visit http://yoursite.com/dev/build/?flush=1 to rebuild the website Database -* Check if you need to adapt your code to changed APIs -* Check if you need to adapt your code to changed CSS/HTML/JS +
+ Never update a website on the live server without trying it on a development copy first. +
-* See [common-problems](common-problems) for a list of likely mistakes that could happen during an upgrade. ## Decision Helpers -How easy will it be to update my project? It's a fair question, and sometimes a difficult one to answer. This page is -intended to help you work out how hard it will be to upgrade your site. +How easy will it be to update my project? It's a fair question, and sometimes a difficult one to answer. -* If you've made custom branches of the core, or of a module, it's going to be harder to upgrade. -* The more custom features you have, the harder it will be to upgrade. You will have to re-test all of those features -and some of them may have broken. -* Customisations of a well defined type - such as custom page types or custom blog widgets - are going to be easier to -upgrade than customisations that use sneaky tricks, such as the subsites module. +* "Micro" releases (x.y.z) are explicitly backwards compatible, "minor" and "major" releases can deprecate features and change APIs (see our [/misc/release-process](release process) for details) +* If you've made custom branches of SilverStripe core, or any thirdparty module, it's going to be harder to upgrade. +* The more custom features you have, the harder it will be to upgrade. You will have to re-test all of those features, and adapt to API changes in core. +* Customisations of a well defined type - such as custom page types or custom blog widgets - are going to be easier to upgrade than customisations that modify deep system internals like rewriting SQL queries. ## Related * [Release Announcements](http://groups.google.com/group/silverstripe-announce/) * [Blog posts about releases on silverstripe.org](http://silverstripe.org/blog/tag/release) +* [/misc/release-process](Release Process) \ No newline at end of file diff --git a/docs/en/misc/coding-conventions.md b/docs/en/misc/coding-conventions.md index 5b2aa3cfd..666e69473 100644 --- a/docs/en/misc/coding-conventions.md +++ b/docs/en/misc/coding-conventions.md @@ -403,7 +403,7 @@ Example: * This method returns something cool. {@link MyParentMethod} has other cool stuff in it. * * @param string $colour The colour of cool things that you want - * @return DataObjectSet A list of everything cool + * @return DataList A list of everything cool */ public function myMethod($foo) {} @@ -420,7 +420,7 @@ Put code into the classes in the following order (where applicable). * Commonly used methods like `getCMSFields()` * Accessor methods (`getMyField()` and `setMyField()`) * Controller action methods - * Template data-access methods (methods that will be called by a `$MethodName` or `<% control MethodName %>` construct in a template somewhere) + * Template data-access methods (methods that will be called by a `$MethodName` or `<% loop MethodName %>` construct in a template somewhere) * Object methods ### SQL Format @@ -429,7 +429,7 @@ If you have to use raw SQL, make sure your code works across databases make sure with the column or table name escaped with double quotes and values with single quotes. :::php - DataObject::get("MyClass", "\"Title\" = 'my title'"); + MyClass::get()->where("\"Title\" = 'my title'"); Use [ANSI SQL](http://en.wikipedia.org/wiki/SQL#Standardization) format where possible. diff --git a/docs/en/misc/contributing.md b/docs/en/misc/contributing.md index 51c8877b2..4567ccdc1 100644 --- a/docs/en/misc/contributing.md +++ b/docs/en/misc/contributing.md @@ -69,25 +69,30 @@ If you want to learn more about git, please have a look at the [free online git ### Commit Messages -We try to maintain a consistent record of descriptive commit messages. As we automatically generate changelogs from them, we need a way to categorize and filter. Please prefix **all** commit messages with one of the following tags: +We try to maintain a consistent record of descriptive commit messages. +Most importantly: Keep the first line short, and add more detail below. +This ensures commits are easy to browse, and look nice on github.com +(more info about [proper git commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)). -* `API CHANGE`: You've added or modified the functions available to developers writing custom PHP. -* `ENHANCEMENT`: You've added something to the user-visible aspects of SilverStripe. -* `BUGFIX`: You've fixed something that was broken. -* `MINOR` Mark things that are so trivial they're not even worth telling users about; specifically, to prevent adding -clutter to our automatically generated changelogs. MINOR is not used to mark a minor bugfix or feature, see above. -Some examples: - * a subsequent commit to a bugfix/feature that you committed earlier that day - * adding unit tests (that are more interesting to developers of SilverStripe than users of it) - * subversion/codebase plumbing (changing externals, blocking revisions, moving files around, etc) - * In summary: if it's worth including in the changelog, it's not `MINOR`. +As we automatically generate [changelogs](http://doc.silverstripe.org/sapphire/en/trunk/changelogs/) from them, we need a way to categorize and filter. +Please prefix **noteworthy** commit messages with one of the following tags: + +* `NEW`: New feature or major enhancement (both for users and developers) +* `API`: Addition of a new API, or modification/removal/deprecation of an existing API. + Includes any change developers should be aware of when upgrading. +* `BUG`: Bugfix or minor enhancement on something developers or users are likely to encounter. + +All other commits should not be tagged if they are so trivial that most developers +can ignore them during upgrades or when reviewing changes to the codebase. +For example, adding unit tests or documentation would not be considered "noteworthy". +Same goes for version control plumbing like merges, file renames or reverts. Further guidelines: * Each commit should form a logical unit - if you fix two unrelated bugs, commit each one separately * If you are fixing a ticket from our [bugtracker](http://open.silverstripe.com), please append `(fixes #)` -* If your change is related to another changeset, reference it with `r`. -* Please mention the changed classes and methods in your comment - the message should be understandable on its own without browsing any sourcecode +* If your change is related to another commit, reference it with its abbreviated commit hash. +* Mention important changed classes and methods in the commit summary. Example: Bad commit message @@ -96,8 +101,11 @@ Example: Bad commit message Example: Good commit message - ENHANCEMENT Added prepValueForDB() which is called on DBField->writeToManipulation() to ensure formatting of value before insertion to DB on a per-DBField type basis (see #1234) - MINOR Added documentation for DBField->writeToManipulation() (see r55555) + ENHANCEMENT Formatting through prepValueForDB() + + Added prepValueForDB() which is called on DBField->writeToManipulation() + to ensure formatting of value before insertion to DB on a per-DBField type basis (fixes #1234). + Added documentation for DBField->writeToManipulation() (related to a4bd42fd).
Note: By supplying code in patches, tickets and pull requests, diff --git a/docs/en/misc/index.md b/docs/en/misc/index.md index 5979f7635..cbf365dc5 100644 --- a/docs/en/misc/index.md +++ b/docs/en/misc/index.md @@ -8,7 +8,6 @@ sections. * [Module release process](module-release-process): Creating and looking after a module * [Release process](release-process): Describes the Framework and CMS release process * [SS markdown](ss-markdown): Markdown syntax for our technical documentation -* [Subversion](subversion): Describes SilverStripe-specific information on how to handle subversion ## Feedback diff --git a/docs/en/misc/subversion.md b/docs/en/misc/subversion.md deleted file mode 100644 index eb51ce60f..000000000 --- a/docs/en/misc/subversion.md +++ /dev/null @@ -1,175 +0,0 @@ -## Subversion - -Subversion [website](http://subversion.tigris.org) is a **version control system**. -You can browse our Subversion "code repository" [here](http://open.silverstripe.com/browser), however we now use GIT to -manage our modules (see [Contributing](contributing)). - -This page only describes SilverStripe-specific information on how to handle subversion. For a general introduction, -please read the [Official Subversion Book](http://svnbook.red-bean.com/) (available free online). - -Start with the ["Basic Usage" chapter](http://svnbook.red-bean.com/en/1.5/svn.tour.html). - -### Clients -Make sure you have an updated [subversion client](http://subversion.tigris.org/links.html#clients) installed. -Subversion 1.5 or higher is required for features such as relative externals and merge tracking. - -### Checkout / Download -See [Download SilverStripe](http://silverstripe.org/download) and the -["Update your working copy" chapter](http://svnbook.red-bean.com/en/1.5/svn.tour.cycle.html#svn.tour.cycle.update). - -### Committing -The SilverStripe core and modules require permission to commit code. -Please have a look at our [contributors guidelines](contributing) to find out how you can gain access. - -### Configuring subversion for correct newline handling -You should configure your subversion client to set the ''svn:eol-style'' property to ''native'' on all text files checked into the system. This will ensure that we don't run into troubles, getting non-unix newlines inside our repository. - -To do this, edit your ''~/.subversion/config'' file on your development machine, and add the following settings. Note that if you already have ''[miscellany]'' and ''[auto-props]'' sections, you should combine these settings with the existing sections, rather than doing a blind copy & paste to the end of the file. - - :::php - [miscellany] - enable-auto-props = yes - - // Section for configuring automatic properties. - [auto-props] - *.js = svn:eol-style=native - *.html = svn:eol-style=native - *.inc = svn:eol-style=native - *.css = svn:eol-style=native - *.php = svn:eol-style=native - *.xml = svn:eol-style=native - *.csv = svn:eol-style=native - *.htm = svn:eol-style=native - *.ini = svn:eol-style=native - *.json = svn:eol-style=native - *.php5 = svn:eol-style=native - *.py = svn:eol-style=native - *.ss = svn:eol-style=native - *.yml = svn:eol-style=native - *.yaml = svn:eol-style=native - *.xhtml = svn:eol-style=native - -Note that if the repository gets out of whack, the following commands run on a linux box will correct things in the current working copy: - - find | grep -v \._ | \ - grep "\.\(js\|php\|css\|inc\|html\|html\|php5\|py\|json\|ini\|xml\|csv\)"\$ | \ - xargs svn propset svn:eol-style native - - find | grep -v \._ | \ - grep "\.\(js\|php\|css\|inc\|html\|html\|php5\|py\|json\|ini\|xml\|csv\)"\$ | \ - xargs dos2unix - -### Feature Branches -For more complicated bugfixes or longer-term development, you may want to create a Feature Branch. For example, you might want -to add support for other rich-text-editors within the CMS - a complex task which can't be contained in a single patch. -Feature branches are a copy of trunk, and usually have a short lifetime in which active development happens. -**The feature branch maintainer is responsible for keeping his branch in sync with trunk and reintegrate when development -is complete.** - -More information about ["Feature Branches"](http://svnbook.red-bean.com/en/1.5/svn.branchmerge.commonpatterns.html#svn.branchmerge.commonpatterns.feature), -[merging changes](http://svnbook.red-bean.com/en/1.5/svn.branchmerge.html) and [resolving conflicts](http://svnbook.red-bean.com/en/1.5/svn.tour.cycle.html#svn.tour.cycle.resolve). - -#### Example: The 'nestedurls' branch -Example for a feature branch for the ''sapphire'' module called ''nestedurls''. - -Creating the branch is a simple matter of running the ''svn cp'' command (see [SVN Book: "Creating a Branch"](http://svnbook.red-bean.com/en/1.5/svn.branchmerge.using.html#svn.branchmerge.using.create)). - - svn cp http://svn.silverstripe.com/open/modules/sapphire/trunk http://svn.silverstripe.com/open/modules/sapphire/branches/nestedurls - - -After creating a feature branch, you commit your changes as usual, but also merge in any new changes from trunk -(see [SVN Book: "Keeping your Branch in Sync"](http://svnbook.red-bean.com/en/1.5/svn.branchmerge.basicmerging.html#svn.branchemerge.basicmerging.stayinsync)). - - cd /your/nestedurls/working/copy - svn merge http://svn.silverstripe.com/open/modules/sapphire/trunk - -Once you've finished your feature development (usually a timeframe around 4-8 weeks), you "reintegrate" your branch with the -trunk repository. This activity happens only once, and the feature branch has to be removed afterwards. - - cd /your/trunk/working/copy - svn merge --reintegrate http://svn.silverstripe.com/open/modules/sapphire/branches/nestedurls - -You can get information about the merge process (see -[SVN Book: "Mergeinfo and Previews"](http://svnbook.red-bean.com/en/1.5/svn.branchmerge.basicmerging.html#svn.branchmerge.basicmerging.mergeinfo)). - - cd /your/nestedurls/working/copy - # revisions which are you still have to merge from trunk to your branch - svn mergeinfo http://svn.silverstripe.com/open/modules/sapphire/trunk --show-revs eligible - # revisions which are already merged from trunk to your branch - svn mergeinfo http://svn.silverstripe.com/open/modules/sapphire/trunk - -### Troubleshooting - -#### SVN for your own websites - -Here is a step-by-step guide on how to work with SVN on your own site. It oversimplifies some aspects, but it is a good -introduction. NOTE: you will need SSH and SVN installed and available on your server. - -* Install LAMP / WAMP and an SVN application (Tortoise SVN on Windows) -* Buy an SVN repository: http://www.SVNrepository.com/ OR set one up on your own server or look at freeby alternatives. -* Go to your SVN repository server and create a new repository - I would recommend to name it after the site you are about to setup, e.g. myfirstsite.com. -* Create / go to web folder on local LAMP/WAMP -* SVN "checkout" your empty repository (e.g. http://sunny.SVNrepository.com/SVN/myaccountname/myfirstsite.com). -* SVN "propedit" SVN:externals, add the following SVN property to the root directory: - -SVN:externals (also add your own general modules here, etc... etc... this should be a long list): - - cms http://SVN.silverstripe.com/open/modules/cms/branches/2.4 - sapphire http://SVN.silverstripe.com/open/modules/sapphire/branches/2.4 - -* SVN "update" your project. All the various files should be imported now. -* SVN "commit" your externals. -* In the root directory, create the following files: _ss_environment.php.sample, .htacess.sample and "commit" these to your repository. -* Copy in local LAMP / WAMP environment: _ss_environment.php.sample to _ss_environment.php; and .htacess.sample to .htacess -* Make sure that .htaccess and _ss_environment.php files are excluded from any commits. This is another SVN property attached to the root directory: - - SVN:ignore: - .htaccess - _ss_environment.php - assets - -* Create assets folder in root directory -* Your site should now be ready on your local WAMP / LAMP. You can now create a mysite and themes folder, add all your files, edit, bla bla until your site is ready. Then SVN "add" and SVN "commit" all your files to your repository (usually, you do many commits) -* Once you have created the site locally and committed it to your repository you should SSH to your web server (recommended application for windows is PUTTY) -* Once logged in to your web server, browse to the root web folder -* Make sure SVN is installed on the server (just type SVN help or something to check). -* SSH prompt, type: - - SVN checkout http://sunny.SVNrepository.com/SVN/myaccountname/myfirstsite.com . - -
-Add a DOT at the end to check out files to current directory -
- -* You should now have all your files on your webserver -* Copy on the server (using SSH) - - cp _ss_environment.php.sample _ss_environment.php - cp .htacess.sample .htacess - -* Edit these files: _ss_environment.php.sample , .htacess.sample, using the following SSH commands (if the nano application is not available then try pico): - - nano _ss_environment.php.sample - nano .htacess.sample - -* Create a folder called assets and make sure permissions are set correctly -* Website should now be up and running. (run dev/build to start). - -#### A few point additional points: -* The whole concept of tags and branches have been left out, just to simplify. -* We have also left out the idea of a test and live site. The instructions here can be used to setup a test site. Once you are happy with a test site you can move the code to a live site using the SVN "merge" command. In principle, this is how it works: open SSH and go to the root directory of the live site. Then type: - - svn merge http://mysvnrepository/branches/live/ http://mysvnrepository/branches/test/ - -* If you want to update a repository, but you want the repository on the webserver to be locked to a version then you need set the svn:externals as follows: - - cms -r1234567 http://svn.silverstripe.com/open/modules/cms/branches/2.4 - -where 1234567 is the revision of your repository that you want to use - -* If you want to get a better understanding of what is going on in a repository then use the following SVN commands: SVN "status" and SVN "info". -* You can not and should not make any changes to any of the core modules and other modules added with svn:externals - -### Related -* [contributing](contributing) -* [release process](release-process) \ No newline at end of file diff --git a/docs/en/misc/translation-process.md b/docs/en/misc/translation-process.md index a075698a1..0ef5e16e2 100644 --- a/docs/en/misc/translation-process.md +++ b/docs/en/misc/translation-process.md @@ -14,21 +14,34 @@ This format is now deprecated, and we don't provide tools for editing the files. Please see below for information on how to convert these legacy files and existing translations to YML. +## Download Translations + +We are managing our translations through a tool called [getlocalization.com](http://getlocalization.com). +Most modules are managed under the "silverstripe" user there, +see [list of translatable modules](http://www.getlocalization.com/profile/?username=silverstripe). + +Translations are exported from there into YML files, generated every hour, +and committed to a special "translation-staging" branch on github. +You can download individual files by opening them on github.com (inside the `lang/` folder), and using the "Raw" view. +Place those files in the appropriate directories on a local silverstripe installation. + + * ["translation-staging" branch for framework module](https://github.com/silverstripe/sapphire/tree/translation-staging) + * ["translation-staging" branch for cms module](https://github.com/silverstripe/silverstripe-cms/tree/translation-staging) + ## Help as a translator ### The online translation tool -We are managing our translations through a tool called -[getlocalization.com](http://getlocalization.com). -Most modules are managed under the "silverstripe" user there, -see [list of translatable modules](http://www.getlocalization.com/profile/?username=silverstripe). +We provide a GUI for translations through [getlocalization.com](http://getlocalization.com). If you don't have an account yet, please follow the links there to sign up. +Select a project from the [list of translatable modules](http://www.getlocalization.com/profile/?username=silverstripe) +and start translating online! For all modules listed there, we automatically import new master strings as they get committed to the various codebases, so you're always translating -on the latest and greatest version (through github service hooks). +on the latest and greatest version. -## Set up your module for localization +## Set up your own module for localization ### Collecting translatable text @@ -61,9 +74,19 @@ change the first line in this file from "en_GB" to "en-GB". ### Export existing translations -You can simply download the whole language pack as a ZIP archive +As a project maintainer, you have the permission can simply download the whole language pack as a ZIP archive and add it to your project. But for composite locales (e.g. "en-GB"), -you have to change the keys in the first line of the file (see note above). +you have to change the keys in the first line of the file. + +We encourage you to use the SilverStripe build tools for this instead, +as they run some additional sanity checks. They require the "phing" tool. +Create a 'translation-staging' branch in your module before starting, +and merge it back manually to your 'master' as required. + + pear install phing/phing + cp build.properties.default + cp build.properties # Add your own getlocalization config to 'build.properties' + phing -Dmodule= -propertyfile build.properties translations-sync ### Converting your language files from 2.4 PHP format @@ -83,16 +106,10 @@ Special characters (such as german umlauts) need to be entered in their native f ### How can I check out my translation in the interface? Currently translated entities are not directly factored into code (for security reasons and release/review-control), so you can't see them straight away. -You can download automatically generated files for your language for each module (e.g. cms, sapphire, forum, ...) - -and place those files in the appropriate directories on a local silverstripe installation. -Example for downloading french files: -downloaded fr.yml for cms => /cms/lang/fr.yml -downloaded fr.yml for sapphire => /sapphire/lang/fr.yml -(repeat for all modules) It is strongly encouraged that you check your translation this way, as its a good way to doublecheck your translation works in the right context. -Please use our daily-builds for your local installation, to ensure you're looking at the most up to date interface. +Please use our [daily-builds](http://www.silverstripe.org/daily-builds/) for your local installation, to ensure you're looking at the most up to date interface. ### Can I change a translation just for one SilverStripe version? @@ -131,7 +148,8 @@ We are currently investigating the available options, and are eager to get feedb ### Can I translate/edit the language files in my favourite text editor (on my local installation) -No, as it causes us a lot of work in merging these files back. +Not for modules managed by getlocalization.com, including "framework" and "cms. +It causes us a lot of work in merging these files back. Please use the online translation tool for all new and existing translations. ### How does my translation get into a SilverStripe release? diff --git a/docs/en/reference/built-in-page-controls.md b/docs/en/reference/built-in-page-controls.md deleted file mode 100644 index e4a3c4b08..000000000 --- a/docs/en/reference/built-in-page-controls.md +++ /dev/null @@ -1,347 +0,0 @@ -# Built-in Page Controls - - -Ever wonder when you use `$Title` and `<% Control Children %>` what else you can call in the templates?. This page is -here to help with a guide on what template controls you can call. - -**Note for advanced users:** These built-in page controls are defined in the [api:SiteTree] classes, which are the -'root' data-object and controller classes for all the sites. So if you're dealing with something that isn't a sub-class -of one of these, our handy reference to 'built-in page controls' won't be so relevant. - - -## Page controls that can't be nested - -These page controls are defined on the **controller** which means they can only be used at a top level, not nested -within another page control. - -### Controlling Menus Datafeeds - -#### <% control Menu(1) %>, <% control Menu(2) %>, ... - -Returns a fixed level menu. Because this only works in the top level, you can't use it for nested menus. Use -`<% control Children %>` instead. You can nest `<% control Children %>`. - -#### <% control ChildrenOf(page-url) %> - -This will create a datafeed of the children of the given page. Handy if you want a list of the subpages under staff (eg -the staff) on the homepage etc - -### Controlling Certain Pages - -#### <% control Level(1) %>, <% control Level(2) %>, $Level(1).Title, $Level(2).Content, etc -Returns the current section of the site that we're in, at the level specified by the numbers. For example, imagine -you're on the page __about us > staff > bob marley__: - -* `<% control Level(1) %>` would return the about us page -* `<% control Level(2) %>` would return the staff page -* `<% control Level(3) %>` would return the bob marley page - -#### <% control Page(my-page) %>$Title<% end_control %> - -"Page" will return a single page from the site tree, looking it up by URL. You can use it in the `<% control %>` format. -Can't be called using `$Page(my-page).Title`. - -## Page controls that can be used anywhere - -These are defined in the data-object and so can be used as nested page controls. Lucky us! we can control Children of -Children of Children for example. - -### Conditional Logic - -SilverStripe supports a simple set of conditional logic - - :::ss - <% if Foo %> - // if Foo is true or an object do this - <% else_if Bar %> - // if Bar is true or an object do this - <% else %> - // then do this by default - <% end_if %> - - -See more information on conditional logic on [templates](/topics/templates). - -### Site wide settings - -Since 2.4.0, SilverStripe provides a generic interface for accessing global properties such as *Site name* or *Site tag -line*. This interface is implemented by the [api:SiteConfig] class. - -### Controlling Parents and Children - -#### <% control Children %> - -This will return the children of the current page as a nested datafeed. Useful for nested navigations such as pop-out -menus. - -#### <% control AllChildren %> - -This will show all children of a page even if the option 'show in menus?' is unchecked in the tab panel behaviour. - -#### <% control Parent %> or $Parent.Title, $Parent.Content, etc - -This will return the parent page. The $ variable format lets us reference an attribute of the parent page directly. - -### Site Navigation - Breadcrumbs - -#### $Breadcrumbs - -This will return a breadcrumbs widget for the current page. You can call this on any SiteTree descendant, so, for -example, you could display the breadcrumbs of every search result if you wanted. The Breadcrumbs method returns a string -of text, so this can't be used as a control block (that is, you can't usefully say "<% control Breadcrumbs %>"). You can -limit the number of items in the breadcrumbs, as well as whether the breadcrumb items are links. - -#### $Breadcrumbs(3) - -This returns a maximum of 3 pages in the breadcrumb list, which can be handy if you want to limit the size of your -breadcrumbs to conform to your page design. - -#### <% control Breadcrumbs(3, true) %> - -This returns the same, but without any links. This is handy if you want to put the breadcrumb list into another link -tag. - - -### Links and Classes - -#### $LinkingMode, $LinkOrCurrent and $LinkOrSection - -These return different linking modes. $LinkingMode provides the greatest control, outputting 3 different strings: - -* link: Neither this page nor any of its children are current open. -* section: A child of this page is currently open, which means that we're currently in this section of the site. -* current: This page is currently open. - -A useful way of using this is in your menus. You can use the following code below to generate class="current" or -class="section" on your links. Take the following code - - :::ss -
  • $Title
  • - - -When viewed on the Home page it will render like this - - :::ss -
  • Home
  • - - -`$LinkOrCurrent` ignores the section status, returning link instead. `$LinkOrSection` ignores the current status, returning section instead. Both of these options can simplify your CSS when you only have 2 different cases to consider. - -#### <% if LinkOrCurrent = current %> - -This is an alternative way to set up your menus - if you want different HTML for the current menu item, you can do -something like this: - - :::ss - <% if LinkOrCurrent = current %> - $Title - <% else %> - $Title - <% end_if %> - - -#### <% if LinkOrSection = section %> - -Will return true if you are on the current page OR a child page of the page. Useful for menus which you only want to -show a second level menu when you are on that page or a child of it - -#### <% if InSection(page-url) %> - -This if block will pass if we're currently on the page-url page or one of its children. - -### Titles and CMS Defined Options - -#### $MetaTags - -This returns a segment of HTML appropriate for putting into the `` tag. It will set up title, keywords and -description meta-tags, based on the CMS content. If you don't want to include the title-tag (for custom templating), use -**$MetaTags(false)**. - -#### $MenuTitle - -This is the title that you should put into navigation menus. CMS authors can choose to put a different menu title from -the main page title. - -#### $Title - -This is the title of the page which displays in the browser window and usually is the title of the page. - - :::ss -

    $Title

    - -#### $URLSegment - -This returns the part of the URL of the page you're currently on. Could be handy to use as an id on your body-tag. ( -when doing this, watch out that it doesn't create invalid id-attributes though.). This is useful for adding a class to -the body so you can target certain pages. Watch out for pages named clear or anything you might have used in your CSS -file - - :::ss - - - -#### $ClassName - -Returns the ClassName of the PHP object. Eg if you have a custom HomePage page type with `$ClassName` in the template, it -will return "HomePage" - -#### $BaseHref - -Returns the base URL for the current site. This is used to populate the `` tag by default, so if you want to -override `<% base_tag %>` with a specific piece of HTML, you can do something like `` - -### Controlling Members and Visitors Data - -#### <% control CurrentMember %>, <% if CurrentMember %> or $CurrentMember.FirstName - -CurrentMember returns the currently logged in member, if there is one. All of their details or any special Member page -controls can be called on this. Alternately, you can use `<% if CurrentMember %>` to detect whether someone has logged -in. To Display a welcome message you can do - - :::ss - <% if CurrentMember %> - Welcome Back, $CurrentMember.FirstName - <% end_if %> - - -If the user is logged in this will print out - - :::ss - Welcome Back, Admin - - -#### <% if IsRepeatMember %> - -Detect the visitor's previous experience with the site. `$IsRepeatMember` will return true if the visitor has signed up or logged in on the site before. - -Note that as of version 2.4 `$PastVisitor` is deprecated. If you wish to check if a visitor has been to the site before, set a cookie with `Cookie::set()` and test for it with `Cookie::get()`. - -Note that in 2.4 this variable was called `$PastMember`. This still works in 3.0 but is deprecated. - -### Date and Time - -#### $Now.Nice, $Now.Year - -`$Now` returns the current date. You can call any of the methods from the [api:Date] class on -it. - -#### $Created.Nice, $Created.Ago - -`$Created` returns the time the page was created, `$Created.Ago` returns how long ago the page was created. You can also -call any of methods of the [api:Date] class on it. - -#### $LastEdited.Nice, $LastEdited.Ago - -`$LastEdited `returns the time the page was modified, `$LastEdited.Ago` returns how long ago the page was modified. You -can also call any of methods of the [api:Date] class on it. - -### DataObjectSet Options - -If you are using a DataObjectSet you have a wide range of methods you can call on it from the templates - -#### <% if Even %>, <% if Odd %>, $EvenOdd - -These controls can be used to do zebra-striping. `$EvenOdd` will return 'even' or 'odd' as appropriate. - -#### <% if First %>, <% if Last %>, <% if Middle %>, $FirstLast - -These controls can be used to set up special behaviour for the first and last records of a datafeed. `<% if Middle %>` is -set when neither first not last are set. `$FirstLast` will be 'first', 'last', or ''. - -#### $Pos, $TotalItems - -`$TotalItems` will return the number of items on this page of the datafeed, and `$Pos` will return a counter starting at 1. - -#### $Top - -When you're inside a control loop in your template, and want to reference methods on the current controller you're on, -breaking out of the loop to get it, you can use `$Top` to do so. For example: - - :::ss - $URLSegment - <% control News %> - $URLSegment - $Top.URLSegment - <% end_control %> - - -## Properties of a datafeed itself, rather than one of its items - -If we have a control such as `<% control SearchResults %>`, there are some properties, such as `$SearchResults.NextLink`, -that aren't accessible within `<% control SearchResults %>`. These can be used on any datafeed. - -### Search Results - -#### <% if SearchResults.MoreThanOnePage %> - -Returns true when we have a multi-page datafeed, restricted with a limit. - -#### $SearchResults.NextLink, $SearchResults.PrevLink - -This returns links to the next and previous page in a multi-page datafeed. They will return blank if there's no -appropriate page to go to, so `$PrevLink` will return blank when you're on the first page. You can therefore use -`<% if PrevLink %>` to keep your template tidy. - -#### $SearchResults.CurrentPage, $SearchResults.TotalPages - -CurrentPage returns the number of the page you're currently on, and TotalPages returns the total number of pages. - -#### $SearchResults.TotalItems - -This returns the total number of items across all pages. - -#### <% control SearchResults.First %>, <% control SearchResults.Last %> - -These controls return the first and last item on the current page of the datafeed. - -#### <% control SearchResults.Pages %> - -This will return another datafeed, listing all of the pages in this datafeed. It will have the following data -available: - -* **$PageNum:** page number, starting at 1 -* **$Link:** a link straight to that page -* `<% if CurrentBool %>`:** returns true if you're currently on that page - -`<% control SearchResults.Pages(30) %>` will show a maximum of 30 pages, useful in situations where you could get 100s of -pages returned. - -#### $SearchResults.UL - -This is a quick way of generating a `
      ` containing an `
    • ` and `` for each item in the datafeed. Usually too -restricted to use in a final application, but handy for debugging stuff. - - -## Quick Reference - -Below is a list of fields and methods that are typically available for templates (grouped by their source) - use this as -a quick reference (not all of them are described above): -### All methods available in Page_Controller - -$NexPageLink, $Link, $RelativeLink, $ChildrenOf, $Page, $Level, $Menu, $Section2, $LoginForm, $SilverStripeNavigator, -$PageComments, $Now, $LinkTo, $AbsoluteLink, $CurrentMember, $PastVisitor, $PastMember, $XML_val, $RAW_val, $SQL_val, -$JS_val, $ATT_val, $First, $Last, $FirstLast, $MiddleString, $Middle, $Even, $Odd, $EvenOdd, $Pos, $TotalItems, -$BaseHref, $Debug, $Top - -### All fields available in Page_Controller - -$ID, $ClassName, $Created, $LastEdited, $URLSegment, $Title, $MenuTitle, $Content, $MetaTitle, $MetaDescription, -$MetaKeywords, $ShowInMenus, $ShowInSearch, $HomepageForDomain, $ProvideComments, $Sort, $LegacyURL, $HasBrokenFile, -$HasBrokenLink, $Status, $ReportClass, $ParentID, $Version, $EmailTo, $EmailOnSubmit, $SubmitButtonText, -$OnCompleteMessage, $Subscribe, $AllNewsletters, $Subject, $ErrorCode, $LinkedPageID, $RedirectionType, $ExternalURL, -$LinkToID, $VersionID, $CopyContentFromID, $RecordClassName - -### All methods available in Page - -$Link, $LinkOrCurrent, $LinkOrSection, $LinkingMode, $ElementName, $InSection, $Comments, $Breadcrumbs, $NestedTitle, -$MetaTags, $ContentSource, $MultipleParents, $TreeTitle, $CMSTreeClasses, $Now, $LinkTo, $AbsoluteLink, $CurrentMember, -$PastVisitor, $PastMember, $XML_val, $RAW_val, $SQL_val, $JS_val, $ATT_val, $First, $Last, $FirstLast, $MiddleString, -$Middle, $Even, $Odd, $EvenOdd, $Pos, $TotalItems, $BaseHref, $Top - -### All fields available in Page - -$ID, $ClassName, $Created, $LastEdited, $URLSegment, $Title, $MenuTitle, $Content, $MetaTitle, $MetaDescription, -$MetaKeywords, $ShowInMenus, $ShowInSearch, $HomepageForDomain, $ProvideComments, $Sort, $LegacyURL, $HasBrokenFile, -$HasBrokenLink, $Status, $ReportClass, $ParentID, $Version, $EmailTo, $EmailOnSubmit, $SubmitButtonText, -$OnCompleteMessage, $Subscribe, $AllNewsletters, $Subject, $ErrorCode, $LinkedPageID, $RedirectionType, $ExternalURL, -$LinkToID, $VersionID, $CopyContentFromID, $RecordClassName diff --git a/docs/en/reference/complextablefield.md b/docs/en/reference/complextablefield.md index fdf98d93b..3ed5900c9 100644 --- a/docs/en/reference/complextablefield.md +++ b/docs/en/reference/complextablefield.md @@ -2,6 +2,10 @@ ## Introduction +
      + This field is deprecated in favour of the new [GridField](/topics/grid-field) API. +
      + Shows a group of DataObjects as a (readonly) tabular list (similiar to `[api:TableListField]`.) You can specify limits and filters for the resultset by customizing query-settings (mostly the ID-field on the other @@ -128,23 +132,4 @@ Most of the time, you need to override the following methods: * ComplexTableField->sourceItems() - querying * ComplexTableField->DetailForm() - form output -* ComplexTableField_Popup->saveComplexTableField() - saving - -### Examples - -* `[api:AssetTableField]` -* `[api:MemberTableField]` - -## API Documentation - -`[api:ComplexTableField]` - -## Todo - -* Find a less fragile solution for accessing this field through the main controller and ReferencedField, e.g. build a -seperate CTF-instance (doesn't necessarly have to be connected to the original by ReferencedField) -* Control width/height of popup by constructor (hardcoded at the moment) -* Integrate search from MemberTableField.php directly on `[api:ComplexTableField]` -* Less performance-hungry implementation of detail-view paging (don't return all items on a single view) -* Use automatic has-many and many-many functions to return a ComponentSet rather than building the join manually -* Javascript/Ajax-Sorting (see [http://www.activewidgets.com/grid/](http://www.activewidgets.com/grid/) and [http://openrico.org/rico/livegrid.page](http://openrico.org/rico/livegrid.page)) +* ComplexTableField_Popup->saveComplexTableField() - saving \ No newline at end of file diff --git a/docs/en/reference/dataextension.md b/docs/en/reference/dataextension.md index 70f47ec56..ace00e9aa 100644 --- a/docs/en/reference/dataextension.md +++ b/docs/en/reference/dataextension.md @@ -45,56 +45,42 @@ For example above we want to override Member with a Custom Member so we would wr ### Adding extra database fields -Extra database fields can be added with a extension by defining an **extraStatics()** method. These will be added to the table of the base object - the extension will actually edit the $db, $has_one, etc static variables on load. +Extra database fields can be added with a extension in the same manner as if they +were placed on the `DataObject` class they're applied to. These will be added to the table of the base object - the extension will actually edit the $db, $has_one, etc static variables on load. The function should return a map where the keys are the names of the static variables to update: :::php class CustomMember extends DataExtension { - - public function extraStatics() { - return array( - 'db' => array( - 'AvatarURL' => 'Varchar', - ), - 'has_one' => array( - 'RelatedMember' => 'Member', - ), - ); - } + static $db = array( + 'AvatarURL' => 'Varchar', + ); + static $has_one = array( + 'RelatedMember' => 'Member', + ); } - -*NOTE* -If you want to add has_one or db items to a particular class, then that class **must** have that static variable -explicitly defined, even if it's just a blank array. For example, the extension method above wouldn't work if you added -to a class that didn't have static $has_one explicitly declared on the object. This is because of PHP's crappy support -for statics. - - ### Modifying CMS Fields -The member class demonstrates an extension that allows you to update the default CMS fields for an object in a -extension: +The member class demonstrates an extension that allows you to update the default CMS fields for an +object in an extension: :::php public function getCMSFields() { - ... - $this->extend('updateCMSFields', $fields); - return $fields; + // ... + $this->extend('updateCMSFields', $fields); + return $fields; } -The $fields parameter is passed by reference, as it is an object. +The `$`fields parameter is passed by reference, as it is an object. :::php public function updateCMSFields(FieldList $fields) { - $fields->push(new TextField('Position', 'Position Title')); - $fields->push(new UploadField('Image', 'Profile Image')); + $fields->push(new TextField('Position', 'Position Title')); + $fields->push(new UploadField('Image', 'Profile Image')); } - - ### Custom database generation Some extensions are designed to transparently add more sophisticated data-collection capabilities to your data object. diff --git a/docs/en/reference/dataobject.md b/docs/en/reference/dataobject.md index bef4339a2..5aea366f3 100644 --- a/docs/en/reference/dataobject.md +++ b/docs/en/reference/dataobject.md @@ -2,53 +2,67 @@ ## Introduction -A single database record & abstract class for the data-access-model. +The `[api:DataObject]` class represents a single row in a database table, +following the ["Active Record"](http://en.wikipedia.org/wiki/Active_record_pattern) design pattern. -## Usage +## Defining Properties -* [datamodel](/topics/datamodel): The basic pricinples -* [data-types](/topics/data-types): Casting and special property-parsing -* `[api:DataObject]`: A "container" for DataObjects +Properties defined through `DataObject::$db` map to table columns, +and can be declared as different [data-types](/topics/data-types). -## Basics +## Loading and Saving Records -The call to `DataObject->getCMSFields()` is the centerpiece of every data administration interface in SilverStripe, -which returns a `[api:FieldList]`''. +The basic principles around data persistence and querying for objects +is explained in the ["datamodel" topic](/topics/datamodel). + +## Defining Form Fields + +In addition to defining how data is persisted, the class can also +help with editing it by providing form fields through `DataObject->getCMSFields()`. +The resulting `[api:FieldList]` is the centrepiece of many data administration interfaces in SilverStripe. +Many customizations of the SilverStripe CMS interface start here, +by adding, removing or configuring fields. + + Example getCMSFields implementation :::php - class MyPage extends Page { + class MyDataObject extends DataObject { + $db = array( + 'IsActive' => 'Boolean' + ); + public function getCMSFields() { + return new FieldList( + new CheckboxField('IsActive') + ); + } + } + +There's various [form field types](/references/form-field-types), for editing text, dates, +restricting input to numbers, and much more. + +## Scaffolding Form Fields + +The ORM already has a lot of information about the data represented by a `DataObject` +through its `$db` property, so why not use it to create form fields as well? +If you call the parent implementation, the class will use `[api:FormScaffolder]` +to provide reasonable defaults based on the property type (e.g. a checkbox field for booleans). +You can then further customize those fields as required. + + :::php + class MyDataObject extends DataObject { + // ... public function getCMSFields() { $fields = parent::getCMSFields(); - $fields->addFieldToTab('Root.Content',new CheckboxField('CustomProperty')); + $fields->fieldByName('IsActive')->setTitle('Is active?'); return $fields; } } +The `[ModelAdmin](/reference/modeladmin)` class uses this approach to provide +data management interfaces with very little custom coding. -## Scaffolding Formfields - -These calls retrieve a `[api:FieldList]` for the area where you intend to work with the scaffolded form. - -### For the CMS - - :::php - $fields = singleton('MyDataObject')->getCMSFields(); - - -### For the Frontend - -Used for simple frontend forms without relation editing or `[api:TabSet] behaviour. Uses `scaffoldFormFields()` by -default. To customize, either overload this method in your subclass, or extend it by `DataExtension->updateFormFields()`. - - :::php - $fields = singleton('MyDataObject')->getFrontEndFields(); - - -## Customizing Scaffolded Fields - -This section covers how to enhance the default scaffolded form fields from above. It is particularly useful when used -in conjunction with the `[api:ModelAdmin]` in the CMS to make relevant data administration interfaces. - +You can also alter the fields of built-in and module `DataObject` classes through +your own `[DataExtension](/reference/dataextension)`, and a call to `[api:DataExtension->updateCMSFields()]`. ### Searchable Fields diff --git a/docs/en/reference/dataobjectset.md b/docs/en/reference/dataobjectset.md deleted file mode 100644 index 97e45fb4b..000000000 --- a/docs/en/reference/dataobjectset.md +++ /dev/null @@ -1,98 +0,0 @@ -# DataObjectSet - -## Introduction - -This class represents a set of `[api:DataObject]`s, such as the results of a query. It is the base for all -[datamodel](/topics/datamodel)-related querying. It implements the [Iterator -interface](http://php.net/manual/en/language.oop5.iterations.php) introduced in PHP5. - -Relations (`has_many`/`many_many`) are described in `[api:ComponentSet]`, a subclass of `[api:DataObjectSet]`. - -## Usage - -### Getting the size - - :::php - $mySet->Count(); - -### Getting an single element - - :::php - $myFirstDataObject = $mySet->First(); - $myLastDataObject = $mySet->Last(); - - -### Getting multiple elements - - :::php - $mySpecialDataObjects = $mySet->find('Status', 'special'); - $startingFromTen = $mySet->getOffset(10); - $tenToTwenty = $mySet->getRange(10, 10); - - -### Getting one property - - :::php - $myIDArray = $mySet->column('ID'); - -### Grouping - -You can group a set by a specific column. Consider using `[api:SQLQuery]` with a *GROUP BY* statement for enhanced -performance. - - :::php - $groupedSet = $mySet->groupBy('Lastname'); - -### Sorting - -Sort a set by a specific column. - - :::php - $mySet->sort('Lastname'); //ascending - $mySet->sort('Lastname', 'DESC'); //descending - -This works on the object itself, so do NOT do something like this: - - :::php - $sortedSet = $mySet->sort('Lastname'); //ascending - -## Merge with other `[api:DataObjectSet]`s - - :::php - $myFirstSet->merge($mySecondSet); - // $myFirstSet now contains all combined values - - -### Mapping for Dropdowns - -When using `[api:DropdownField]` and its numerous subclasses to select a value from a set, you can easily map -the records to a compatible array: - - :::php - $map = $mySet->toDropDownMap('ID', 'Title'); - $dropdownField = new DropdownField('myField', 'my label', $map); - - -### Converting to array - - :::php - $myArray = $mySet->toArray(); - -### Checking for existence - -It is good practice to check for empty sets before doing any iteration. - - :::php - $mySet = DataObject::get('Players'); - if($mySet->exists()) foreach($mySet as $player) - -### Paging - -`[api:DataObject]`s have native support for dealing with **pagination**. -See *setPageLimits*, *setPageLength*, etc. - -FIXME Complete pagination documentation - - -## API Documentation -`[api:DataObjectSet]` diff --git a/docs/en/reference/director.md b/docs/en/reference/director.md index 1ee1bcd3a..87303e13c 100644 --- a/docs/en/reference/director.md +++ b/docs/en/reference/director.md @@ -2,76 +2,80 @@ ## Introduction -`[api:Director]` is the first step in the "execution pipeline". It parses the URL, matching it to one of a number of patterns, -and determines the controller, action and any argument to be used. It then runs the controller, which will finally run -the viewer and/or perform processing steps. - -## Best Practices - -* Checking for an Ajax-Request: Use Director::is_ajax() instead of checking for $_REQUEST['ajax']. - -## Redirection - -The `[api:Director]` class has a number of methods to facilitate 301 and 302 HTTP redirection. - -* **Director::redirect("action-name")**: If there's no slash in the URL passed to redirect, then it is assumed that you -want to go to a different action on the current controller. -* **Director::redirect("relative/url")**: If there is a slash in the URL, it's taken to be a normal URL. Relative URLs -will are assumed to be relative to the site-root; so Director::redirect("home/") will work no matter what the current -URL is. -* **Director::redirect("http://www.absoluteurl.com")**: Of course, you can pass redirect() absolute URL s too. -* **Director::redirectPerm("any-url")**: redirectPerm takes the same arguments as redirect, but it will send a 301 -(permanent) instead of a 302 (temporary) header. It improves search rankings, so this should be used whenever the -following two conditions are true: - * Nothing happens server-side prior to the redirection - * The redirection will always occur -* **Director::redirectBack()**: This will return you to the previous page. There's no permanent version of -redirectBack(). - +`[api:Director]` is the first step in the "execution pipeline". It parses the +URL, matching it to one of a number of patterns, and determines the controller, +action and any argument to be used. It then runs the controller, which will +finally run the viewer and/or perform processing steps. ## Request processing -The `[api:Director]` is the entry point in Silverstring Framework for processing a request. You can read through -the execution steps in `[api:Director]``::direct()`, but in short +The `[api:Director]` is the entry point in Silverstring Framework for processing +a request. You can read through the execution steps in `[api:Director]``::direct()`, +but in short -* File uploads are first analysed to remove potentially harmful uploads (this will likely change!) +* File uploads are first analysed to remove potentially harmful uploads (this +will likely change!) * The `[api:SS_HTTPRequest]` object is created * The session object is created -* The `[api:Injector]` is first referenced, and asks the registered `[api:RequestProcessor]` to pre-process - the request object. This allows for analysis of the current request, and allow filtering of parameters - etc before any of the core of the application executes +* The `[api:Injector]` is first referenced, and asks the registered `[api:RequestProcessor]` +to pre-process the request object. This allows for analysis of the current +request, and allow filtering of parameters etc before any of the core of the +application executes. * The request is handled and response checked -* The `[api:RequestProcessor]` is called to post-process the request to allow further filtering before - content is sent to the end user. +* The `[api:RequestProcessor]` is called to post-process the request to allow +further filtering before content is sent to the end user * The response is output -The framework provides the ability to hook into the request both before and after it is handled to allow -developers to bind in their own custom pre- or post- request logic; see the `[api:RequestFilter]` to see how -this can be used to authenticate the request before the request is handled. +The framework provides the ability to hook into the request both before and +after it is handled to allow developers to bind in their own custom pre- or +post- request logic; see the `[api:RequestFilter]` to see how this can be used +to authenticate the request before the request is handled. -## Custom Rewrite Rules +## Routing -You can influence the way URLs are resolved one of 2 ways +You can influence the way URLs are resolved in the following ways -1. Adding rules to `[api:Director]` in `/_config.php` (See Default Rewrite Rules below for examples) -2. Adding rules in your extended `[api:Controller]` class via the *$url_handlers* static variable +1. Adding rules to `[api:Director]` in `/_config/routes.yml` +2. Adding rules to `[api:Director]` in `/_config.php (deprecated) +3. Adding rules in your extended `[api:Controller]` class via the *$url_handlers* +static variable -See [controller](/topics/controller) for examples and explanations on how the rules get processed for both 1 and 2 above. - -* Static redirect for specific URL - - :::php - Director::addRules(100, array( - 'myPermanentRedirect' => 'redirect:http://www.mysite.com' - )); +See [controller](/topics/controller) for examples and explanations on how the +rules get processed for those methods. -## Default Rewrite Rules +### Routing Rules -SilverStripe comes with certain rewrite rules (e.g. for *admin/assets*). +SilverStripe comes with certain rules which map a URI to a `[api:Controller]` +class (e.g. *dev/* -> DevelopmentAdmin). These routes are either stored in +a routes.yml configuration file located a `_config` directory or inside a +`_config.php` file (deprecated). -* [framework/_config.php](https://github.com/silverstripe/sapphire/blob/master/_config.php) -* [cms/_config.php](https://github.com/silverstripe/silverstripe-cms/blob/master/_config.php) +To add your own custom routes for your application create a routes.yml file +in `/_config/routes.yml` with the following format: + + :::yaml + --- + Name: customroutes + After: framework/routes#coreroutes + --- + Director: + rules: + 'subscriptions/$Action' : 'SubscriptionController' + +The [Controller](/topics/controller) documentation has a wide range of examples +and explanations on how the rules get processed for those methods. + +See: + +* [framework/_config/routes.yml](https://github.com/silverstripe/sapphire/blob/master/_config/routes.yml) +* [cms/_config/routes.yml](https://github.com/silverstripe/silverstripe-cms/blob/master/_config/routes.yml) + + +## Best Practices + +* Checking for an Ajax-Request: Use Director::is_ajax() instead of checking +for $_REQUEST['ajax']. ## Links diff --git a/docs/en/reference/execution-pipeline.md b/docs/en/reference/execution-pipeline.md index 588a978a2..a196cd9b9 100644 --- a/docs/en/reference/execution-pipeline.md +++ b/docs/en/reference/execution-pipeline.md @@ -48,17 +48,14 @@ mod_rewrite works. ## main.php -All requests go through main.php, which sets up the environment and then hands control over to Director. +All requests go through `main.`php, which sets up the environment and then hands control over to `Director`. -**See:** The API documentation of `[api:Main]` for information about how main.php processes requests. ## Director and URL patterns main.php relies on `[api:Director]` to work out which controller should handle this request. `[api:Director]` will instantiate that controller object and then call `[api:Controller::run()]`. -**See:** The API documentation of `[api:Director]` for information about how Director parses URLs and hands control over to a controller object. - -In general, the URL is build up as follows: page/action/ID/otherID - e.g. http://www.mysite.com/mypage/addToCart/12. +In general, the URL is build up as follows: `page/action/ID/otherID` - e.g. http://www.mysite.com/mypage/addToCart/12. This will add an object with ID 12 to the cart. When you create a function, you can access the ID like this: @@ -67,7 +64,7 @@ When you create a function, you can access the ID like this: public function addToCart ($request) { $param = $r->allParams(); echo "my ID = ".$param["ID"]; - $obj = DataObject::get("myProduct", $param["ID"]); + $obj = MyProduct::get()->byID($param["ID"]); $obj->addNow(); } diff --git a/docs/en/reference/form-field-types.md b/docs/en/reference/form-field-types.md index ff4ee484c..cc9ef2b36 100644 --- a/docs/en/reference/form-field-types.md +++ b/docs/en/reference/form-field-types.md @@ -1,83 +1,73 @@ # Form Field Types -This is a highlevel overview of available `[apiFormField]` subclasses. An automatically generated list is available through our [API](api:FormField) +This is a highlevel overview of available `[api:apiFormField]` subclasses. An automatically generated list is available through our [API] + +## Basic + + * `[api:CheckboxField]`: Single checkbox field. + * `[api:DropdownField]`: A `` and `