mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Merge remote-tracking branch 'origin/3.1'
Conflicts: _config/routes.yml docs/en/topics/datamodel.md forms/DropdownField.php
This commit is contained in:
commit
0d7e9a9692
@ -15,9 +15,6 @@ After:
|
||||
Director:
|
||||
rules:
|
||||
'Security//$Action/$ID/$OtherID': 'Security'
|
||||
'api/v1/live': 'VersionedRestfulServer'
|
||||
'api/v1': 'RestfulServer'
|
||||
'soap/v1': 'SOAPModelAccess'
|
||||
'dev': 'DevelopmentAdmin'
|
||||
'interactive': 'SapphireREPL'
|
||||
'InstallerTest//$Action/$ID/$OtherID': 'InstallerTest'
|
||||
|
@ -928,6 +928,8 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
$data = array();
|
||||
$ids = explode(',', $request->getVar('ids'));
|
||||
foreach($ids as $id) {
|
||||
if($id === "") continue; // $id may be a blank string, which is invalid and should be skipped over
|
||||
|
||||
$record = $this->getRecord($id);
|
||||
$recordController = ($this->stat('tree_class') == 'SiteTree')
|
||||
? singleton('CMSPageEditController')
|
||||
|
@ -317,11 +317,11 @@ fieldset.switch-states.size_2 input:checked:nth-of-type(2) ~ .slide-button { lef
|
||||
fieldset.switch-states.size_2 input:checked:nth-of-type(3) ~ .slide-button { left: 100%; }
|
||||
fieldset.switch-states.size_2 input:checked:nth-of-type(4) ~ .slide-button { left: 150%; }
|
||||
fieldset.switch-states.size_2 input:checked:nth-of-type(5) ~ .slide-button { left: 200%; }
|
||||
fieldset.switch-states.size_3 label, fieldset.switch-states.size_3 .slide-button { width: 33.333%; }
|
||||
fieldset.switch-states.size_3 input:checked:nth-of-type(2) ~ .slide-button { left: 33.333%; }
|
||||
fieldset.switch-states.size_3 input:checked:nth-of-type(3) ~ .slide-button { left: 66.667%; }
|
||||
fieldset.switch-states.size_3 label, fieldset.switch-states.size_3 .slide-button { width: 33.33333%; }
|
||||
fieldset.switch-states.size_3 input:checked:nth-of-type(2) ~ .slide-button { left: 33.33333%; }
|
||||
fieldset.switch-states.size_3 input:checked:nth-of-type(3) ~ .slide-button { left: 66.66667%; }
|
||||
fieldset.switch-states.size_3 input:checked:nth-of-type(4) ~ .slide-button { left: 100%; }
|
||||
fieldset.switch-states.size_3 input:checked:nth-of-type(5) ~ .slide-button { left: 133.333%; }
|
||||
fieldset.switch-states.size_3 input:checked:nth-of-type(5) ~ .slide-button { left: 133.33333%; }
|
||||
fieldset.switch-states.size_4 label, fieldset.switch-states.size_4 .slide-button { width: 25%; }
|
||||
fieldset.switch-states.size_4 input:checked:nth-of-type(2) ~ .slide-button { left: 25%; }
|
||||
fieldset.switch-states.size_4 input:checked:nth-of-type(3) ~ .slide-button { left: 50%; }
|
||||
@ -393,9 +393,10 @@ body.cms { overflow: hidden; }
|
||||
.ui-tabs .ui-tabs-nav li { top: 0; float: left; border-bottom: 0 !important; }
|
||||
.ui-tabs .ui-tabs-nav li a { display: -moz-inline-stack; display: inline-block; vertical-align: middle; *vertical-align: auto; zoom: 1; *display: inline; float: none; font-weight: bold; color: #444444; line-height: 32px; padding: 0 16px 0; }
|
||||
.ui-tabs .ui-tabs-nav li:last-child { margin-right: 0; }
|
||||
.ui-tabs .ui-tabs-nav li.ui-tabs-active { margin-bottom: 0; }
|
||||
.ui-tabs .ui-tabs-nav .ui-state-default { border: 1px solid #c0c0c2; background: #ced7dc; }
|
||||
.ui-tabs .ui-tabs-nav .ui-state-default a { color: #5e5e5e; text-shadow: #e6e6e6 0 1px 0; }
|
||||
.ui-tabs .ui-tabs-nav .ui-state-active { padding-bottom: 1px; border: 1px solid #c0c0c2; background-color: #eceff1; }
|
||||
.ui-tabs .ui-tabs-nav .ui-state-active { padding-bottom: 1px; border: 1px solid #c0c0c2; background-color: #e6eaed; }
|
||||
.ui-tabs .ui-tabs-nav .ui-state-active a { color: #444444; }
|
||||
.ui-tabs .ui-tabs-nav.ui-state-active { border-color: gray; }
|
||||
.ui-tabs .ui-tabs-nav li.cms-tabset-icon { text-indent: -9999em; }
|
||||
@ -724,7 +725,7 @@ form.import-form label.left { width: 250px; }
|
||||
/** -------------------------------------------- Page Edit Controller -------------------------------------------- */
|
||||
/*.cms-container {
|
||||
.CMSPageEditController, .CMSPageSettingsController, .CMSPageHistoryController {
|
||||
/* Fix pixel gap between nav tree and main page header */
|
||||
// Fix pixel gap between nav tree and main page header
|
||||
margin-left: -1px; // Removed to close gap far right of right tabs?
|
||||
}
|
||||
}*/
|
||||
|
@ -61,15 +61,15 @@
|
||||
|
||||
if(this.is(':checked')) {
|
||||
checkboxes.each(function() {
|
||||
$(this).data('SecurityAdmin.oldChecked', $(this).attr('checked'));
|
||||
$(this).data('SecurityAdmin.oldDisabled', $(this).attr('disabled'));
|
||||
$(this).attr('disabled', 'disabled');
|
||||
$(this).attr('checked', 'checked');
|
||||
$(this).data('SecurityAdmin.oldChecked', $(this).is(':checked'));
|
||||
$(this).data('SecurityAdmin.oldDisabled', $(this).is(':disabled'));
|
||||
$(this).prop('disabled', true);
|
||||
$(this).prop('checked', true);
|
||||
});
|
||||
} else {
|
||||
checkboxes.each(function() {
|
||||
$(this).attr('checked', $(this).data('SecurityAdmin.oldChecked'));
|
||||
$(this).attr('disabled', $(this).data('SecurityAdmin.oldDisabled'));
|
||||
$(this).prop('checked', $(this).data('SecurityAdmin.oldChecked'));
|
||||
$(this).prop('disabled', $(this).data('SecurityAdmin.oldDisabled'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -188,59 +188,63 @@ body.cms {
|
||||
}
|
||||
|
||||
.ui-tabs-nav {
|
||||
float: right;
|
||||
margin: $grid-x*2 0 -1px 0;
|
||||
padding: 0 $grid-x*1.5 0 0;
|
||||
border-bottom: none;
|
||||
float: right;
|
||||
margin: $grid-x*2 0 -1px 0;
|
||||
padding: 0 $grid-x*1.5 0 0;
|
||||
border-bottom: none;
|
||||
|
||||
~ .ui-tabs-panel {
|
||||
border-top:1px solid $color-button-generic-border;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
~ .ui-tabs-panel {
|
||||
border-top:1px solid $color-button-generic-border;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
li {
|
||||
top: 0;
|
||||
float: left;
|
||||
border-bottom: 0 !important;
|
||||
top: 0;
|
||||
float: left;
|
||||
border-bottom: 0 !important;
|
||||
|
||||
a {
|
||||
@include inline-block;
|
||||
float: none;
|
||||
font-weight: bold;
|
||||
color: $color-text;
|
||||
line-height: $grid-y * 4;
|
||||
padding: 0 $grid-x*2 0;
|
||||
}
|
||||
color: $color-text;
|
||||
line-height: $grid-y * 4;
|
||||
padding: 0 $grid-x*2 0;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
// correctly right-align last tab
|
||||
margin-right: 0;
|
||||
}
|
||||
&:last-child {
|
||||
// correctly right-align last tab
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.ui-tabs-active {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-state-default {
|
||||
border:1px solid $color-button-generic-border;
|
||||
background:darken($color-widget-bg, 10%);
|
||||
background: darken($color-widget-bg, 10%);
|
||||
|
||||
a {
|
||||
color: lighten($color-text, 10%);
|
||||
text-shadow: lighten($color-tab, 5%) 0 1px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-state-active {
|
||||
padding-bottom:1px;
|
||||
border:1px solid $color-button-generic-border;
|
||||
background-color: $tab-panel-texture-color;
|
||||
padding-bottom: 1px;
|
||||
border: 1px solid $color-button-generic-border;
|
||||
background-color: darken($tab-panel-texture-color, 2%);
|
||||
|
||||
a {
|
||||
color: $color-text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.ui-state-active {
|
||||
border-color: $color-medium-separator;
|
||||
}
|
||||
}
|
||||
|
||||
li.cms-tabset-icon {
|
||||
text-indent:-9999em;
|
||||
@ -249,7 +253,7 @@ body.cms {
|
||||
display: block;
|
||||
padding-left: 40px; // icon width
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.list a {background: sprite($sprites64, tab-list) no-repeat;}
|
||||
&.tree a {background: sprite($sprites64, tab-tree) no-repeat;}
|
||||
@ -262,8 +266,8 @@ body.cms {
|
||||
&.gallery.ui-state-active a {background: sprite($sprites64, tab-gallery-hover) no-repeat;}
|
||||
&.edit.ui-state-active a {background: sprite($sprites64, tab-edit-hover) no-repeat;}
|
||||
&.search.ui-state-active a {background: sprite($sprites64, tab-search-hover) no-repeat;}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cms-panel-padded {
|
||||
.ui-tabs-panel {
|
||||
@ -1096,7 +1100,7 @@ form.member-profile-form {
|
||||
// can trigger longer pages and the extra scroll bar doesn't fire our sizing bar
|
||||
overflow-y: auto;
|
||||
overflow-x: auto;
|
||||
background:darken($tab-panel-texture-color,2%);
|
||||
background: darken($tab-panel-texture-color, 2%);
|
||||
width:100%;
|
||||
#Root_Main {
|
||||
.confirmedpassword {
|
||||
|
@ -17,7 +17,7 @@ class CMSMenuTest extends SapphireTest implements TestOnly {
|
||||
$menuItems = CMSMenu::get_menu_items();
|
||||
$menuItem = $menuItems['CMSMenuTest_LeftAndMainController'];
|
||||
$this->assertInstanceOf('CMSMenuItem', $menuItem, 'Controller menu item is of class CMSMenuItem');
|
||||
$this->assertEquals($menuItem->url, singleton('CMSMenuTest_LeftAndMainController')->Link(),
|
||||
$this->assertContains($menuItem->url, singleton('CMSMenuTest_LeftAndMainController')->Link(),
|
||||
'Controller menu item has the correct link');
|
||||
$this->assertEquals($menuItem->controller, 'CMSMenuTest_LeftAndMainController',
|
||||
'Controller menu item has the correct controller class');
|
||||
@ -71,7 +71,7 @@ class CMSMenuTest extends SapphireTest implements TestOnly {
|
||||
CMSMenu::populate_menu();
|
||||
$menuItem = CMSMenu::get_menu_item('SecurityAdmin');
|
||||
$this->assertInstanceOf('CMSMenuItem', $menuItem, 'SecurityAdmin menu item exists');
|
||||
$this->assertEquals($menuItem->url, singleton('SecurityAdmin')->Link(), 'Menu item has the correct link');
|
||||
$this->assertContains($menuItem->url, singleton('SecurityAdmin')->Link(), 'Menu item has the correct link');
|
||||
$this->assertEquals($menuItem->controller, 'SecurityAdmin', 'Menu item has the correct controller class');
|
||||
$this->assertEquals(
|
||||
$menuItem->priority,
|
||||
|
@ -459,7 +459,7 @@ class Director implements TemplateGlobalProvider {
|
||||
*/
|
||||
public static function protocol() {
|
||||
return (self::is_https()) ? 'https://' : 'http://';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the site is running as under HTTPS.
|
||||
@ -469,18 +469,23 @@ class Director implements TemplateGlobalProvider {
|
||||
public static function is_https() {
|
||||
if ($protocol = Config::inst()->get('Director', 'alternate_protocol')) {
|
||||
return $protocol == 'https';
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) {
|
||||
if(strtolower($_SERVER['HTTP_X_FORWARDED_PROTOCOL']) == 'https') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_SERVER['X-Forwarded-Proto'])) {
|
||||
if(strtolower($_SERVER['X-Forwarded-Proto']) == "https") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')) {
|
||||
return true;
|
||||
}
|
||||
else if(isset($_SERVER['SSL'])) {
|
||||
} else if(isset($_SERVER['SSL'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -507,11 +512,11 @@ class Director implements TemplateGlobalProvider {
|
||||
$baseURL = '/';
|
||||
} else {
|
||||
$baseURL = $base . '/';
|
||||
}
|
||||
}
|
||||
|
||||
if(defined('BASE_SCRIPT_URL')) {
|
||||
return $baseURL . BASE_SCRIPT_URL;
|
||||
}
|
||||
}
|
||||
|
||||
return $baseURL;
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
*
|
||||
* @author marcus@silverstripe.com.au
|
||||
* @license BSD License http://silverstripe.org/bsd-license/
|
||||
* @package framework
|
||||
* @subpackage control
|
||||
*/
|
||||
interface RequestFilter {
|
||||
/**
|
||||
|
@ -169,7 +169,7 @@ class Session {
|
||||
*/
|
||||
public static function set_cookie_domain($domain) {
|
||||
Deprecation::notice('3.2', 'Use the "Session.cookie_domain" config setting instead');
|
||||
Config::inst()->update('Session', 'cookie_domain', $age);
|
||||
Config::inst()->update('Session', 'cookie_domain', $domain);
|
||||
}
|
||||
|
||||
/**
|
||||
|
17
core/DAG.php
17
core/DAG.php
@ -3,6 +3,9 @@
|
||||
/**
|
||||
* A Directed Acyclic Graph - used for doing topological sorts on dependencies, such as the before/after conditions
|
||||
* in config yaml fragments
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage manifest
|
||||
*/
|
||||
class SS_DAG implements IteratorAggregate {
|
||||
/**
|
||||
@ -88,10 +91,20 @@ class SS_DAG implements IteratorAggregate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when the {@link SS_DAG} class is unable to resolve sorting the DAG due to cyclic dependencies.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage manifest
|
||||
*/
|
||||
class SS_DAG_CyclicException extends Exception {
|
||||
|
||||
public $dag;
|
||||
|
||||
/**
|
||||
* @param string $message The Exception message
|
||||
* @param SS_DAG $dag The remainder of the Directed Acyclic Graph (DAG) after the last successful sort
|
||||
*/
|
||||
public function __construct($message, $dag) {
|
||||
$this->dag = $dag;
|
||||
parent::__construct($message);
|
||||
@ -99,6 +112,10 @@ class SS_DAG_CyclicException extends Exception {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage manifest
|
||||
*/
|
||||
class SS_DAG_Iterator implements Iterator {
|
||||
|
||||
protected $data;
|
||||
|
@ -353,7 +353,7 @@ class PaginatedList extends SS_ListDecorator {
|
||||
* @return bool
|
||||
*/
|
||||
public function NotLastPage() {
|
||||
return $this->CurrentPage() != $this->TotalPages();
|
||||
return $this->CurrentPage() < $this->TotalPages();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -421,4 +421,4 @@ class PaginatedList extends SS_ListDecorator {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
/**
|
||||
* Returns the temporary folder path that silverstripe should use for its cache files.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage core
|
||||
*
|
||||
* @param $base The base path to use for determining the temporary path
|
||||
* @return string Path to temp
|
||||
*/
|
||||
@ -20,6 +23,9 @@ function getTempFolder($base = null) {
|
||||
|
||||
/**
|
||||
* Returns as best a representation of the current username as we can glean.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage core
|
||||
*/
|
||||
function getTempFolderUsername() {
|
||||
$user = getenv('APACHE_RUN_USER');
|
||||
@ -38,6 +44,9 @@ function getTempFolderUsername() {
|
||||
* Return the parent folder of the temp folder.
|
||||
* The temp folder will be a subfolder of this, named by username.
|
||||
* This structure prevents permission problems.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage core
|
||||
*/
|
||||
function getTempParentFolder($base = null) {
|
||||
if(!$base && defined('BASE_PATH')) $base = BASE_PATH;
|
||||
|
@ -9,6 +9,7 @@
|
||||
* optionally catch attempts to modify the config statics (otherwise the modification will appear
|
||||
* to work, but won't actually have any effect - the equvilent of failing silently)
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage manifest
|
||||
*/
|
||||
class SS_ConfigStaticManifest {
|
||||
@ -150,6 +151,9 @@ class SS_ConfigStaticManifest {
|
||||
* We can't do this using TokenisedRegularExpression because we need to keep track of state
|
||||
* as we process the token list (when we enter and leave a namespace or class, when we see
|
||||
* an access level keyword, etc)
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage manifest
|
||||
*/
|
||||
class SS_ConfigStaticManifest_Parser {
|
||||
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
/**
|
||||
* A basic caching interface that manifests use to store data.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage manifest
|
||||
*/
|
||||
interface ManifestCache {
|
||||
public function __construct($name);
|
||||
@ -12,6 +15,9 @@ interface ManifestCache {
|
||||
|
||||
/**
|
||||
* Stores manifest data in files in TEMP_DIR dir on filesystem
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage manifest
|
||||
*/
|
||||
class ManifestCache_File implements ManifestCache {
|
||||
function __construct($name) {
|
||||
@ -37,6 +43,9 @@ class ManifestCache_File implements ManifestCache {
|
||||
/**
|
||||
* Same as ManifestCache_File, but stores the data as valid PHP which gets included to load
|
||||
* This is a bit faster if you have an opcode cache installed, but slower otherwise
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage manifest
|
||||
*/
|
||||
class ManifestCache_File_PHP extends ManifestCache_File {
|
||||
function load($key) {
|
||||
@ -58,6 +67,9 @@ class ManifestCache_File_PHP extends ManifestCache_File {
|
||||
/**
|
||||
* Stores manifest data in APC.
|
||||
* Note: benchmarks seem to indicate this is not particularly faster than _File
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage manifest
|
||||
*/
|
||||
class ManifestCache_APC implements ManifestCache {
|
||||
protected $pre;
|
||||
|
@ -1,8 +1,12 @@
|
||||
<?php
|
||||
|
||||
// Inject SilverStripe 'setUpOnce' and 'tearDownOnce' unittest extension methods into phpunit
|
||||
// This is already in later SilverStripe 2.4 versions, but having it here extends compatibility to older versions
|
||||
|
||||
/**
|
||||
* Inject SilverStripe 'setUpOnce' and 'tearDownOnce' unittest extension methods into PHPUnit.
|
||||
*
|
||||
* This is already in later SilverStripe 2.4 versions, but having it here extends compatibility to older versions.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage testing
|
||||
*/
|
||||
class SilverStripeListener implements PHPUnit_Framework_TestListener {
|
||||
|
||||
protected function isValidClass($name) {
|
||||
|
@ -1,7 +1,10 @@
|
||||
<?php
|
||||
|
||||
// Bind TeamCity test listener. Echos messages to stdout that TeamCity interprets into the test results
|
||||
|
||||
/**
|
||||
* Bind TeamCity test listener. Echos messages to stdout that TeamCity interprets into the test results
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage testing
|
||||
*/
|
||||
class TeamCityListener implements PHPUnit_Framework_TestListener {
|
||||
|
||||
private function escape($str) {
|
||||
|
@ -76,6 +76,20 @@ 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.
|
||||
|
||||
## Setting the limit of items to be displayed on a page ##
|
||||
|
||||
To set the limit of items displayed in a paginated page use the `[api:PaginatedList->setPageLength()]` method. e.g:
|
||||
|
||||
:::php
|
||||
/**
|
||||
* Returns a paginated list of all pages in the site, and limits the items displayed to 4 per page.
|
||||
*/
|
||||
public function PaginatedPagesLimit() {
|
||||
$paginatedItems = new PaginatedList(Page::get(), $this->request);
|
||||
$paginatedItems->setPageLength(4);
|
||||
return $pagination;
|
||||
}
|
||||
|
||||
## Related
|
||||
|
||||
* [Howto: "Grouping Lists"](/howto/grouping-dataobjectsets)
|
||||
* [Howto: "Grouping Lists"](/howto/grouping-dataobjectsets)
|
||||
|
19
docs/en/topics/controller.md
Normal file → Executable file
19
docs/en/topics/controller.md
Normal file → Executable file
@ -54,6 +54,25 @@ making any code changes to your controller.
|
||||
so a `MyController` class is accessible through `http://localhost/MyController`.
|
||||
</div>
|
||||
|
||||
## Linking to a controller
|
||||
|
||||
Each controller has a built-in `Link()` method,
|
||||
which can be used to avoid hardcoding your routing in views etc.
|
||||
The method should return a value that makes sense with your custom route (see above):
|
||||
|
||||
:::php
|
||||
<?php
|
||||
class FastFood_Controller extends Controller {
|
||||
public function Link($action = null) {
|
||||
return Controller::join_links('fastfood', $action);
|
||||
}
|
||||
}
|
||||
|
||||
The [api:Controller::join_links()] invocation is optional, but makes `Link()` more flexible
|
||||
by allowing an `$action` argument, and concatenates the path segments with slashes.
|
||||
The action should map to a method on your controller. `join_links()` also supports
|
||||
|
||||
|
||||
## Access Control
|
||||
|
||||
### Through $allowed_actions
|
||||
|
@ -291,7 +291,7 @@ start with S, who has logged in since 1/1/2011.
|
||||
:::php
|
||||
$members = Member::get()->filter(array(
|
||||
'FirstName:StartsWith:Not' => 'S'
|
||||
'Birthday:GreaterThan' => '2011-01-01'
|
||||
'LastVisited:GreaterThan' => '2011-01-01'
|
||||
));
|
||||
|
||||
### Subtract
|
||||
|
@ -323,19 +323,14 @@ in your `mysite/_config.php`:
|
||||
|
||||
## Javascript Usage
|
||||
|
||||
i18n in javascript works with mostly the same assumption as its PHP-equivalent.
|
||||
|
||||
The i18n system in JavaScript is similar to its PHP equivalent.
|
||||
Languages are typically stored in `<my-module-dir>/javascript/lang`.
|
||||
Unlike the PHP logic, these files aren't auto-discovered and have to be included manually.
|
||||
|
||||
### Requirements
|
||||
|
||||
Add the i18n library requirement to your code.
|
||||
|
||||
:::php
|
||||
Requirements::javascript(FRAMEWORK_DIR . "/javascript/i18n.js");
|
||||
|
||||
|
||||
Each language has its own language table in a separate file.
|
||||
To save bandwidth, only three tables are actually loaded by
|
||||
To save bandwidth, only two files are actually loaded by
|
||||
the browser: The current locale, and the default locale as a fallback.
|
||||
The `Requirements` class has a special method to determine these includes:
|
||||
Just point it to a directory instead of a file, and the class will figure out the includes.
|
||||
@ -346,30 +341,31 @@ Just point it to a directory instead of a file, and the class will figure out th
|
||||
|
||||
### Translation Tables in JavaScript
|
||||
|
||||
Translation tables are automatically included as required, depending on the configured locale in *i18n::get_locale()*.
|
||||
As a fallback for partially translated tables we always include the master table (en_US.js) as well.
|
||||
Translation tables are automatically included as required, depending on the configured locale in `i18n::get_locale()`.
|
||||
As a fallback for partially translated tables we always include the master table (`en.js`) as well.
|
||||
|
||||
Master Table (mymodule/javascript/lang/en_US.js)
|
||||
Master Table (`<my-module-dir>/javascript/lang/en.js`)
|
||||
|
||||
:::js
|
||||
if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
|
||||
console.error('Class ss.i18n not defined');
|
||||
} else {
|
||||
ss.i18n.addDictionary('en_US', {
|
||||
ss.i18n.addDictionary('en', {
|
||||
'MYMODULE.MYENTITY' : "Really delete these articles?"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Example Translation Table (mymodule/javascript/lang/de_DE.js)
|
||||
Example Translation Table (`<my-module-dir>/javascript/lang/de.js`)
|
||||
|
||||
:::js
|
||||
ss.i18n.addDictionary('de_DE', {
|
||||
ss.i18n.addDictionary('de', {
|
||||
'MYMODULE.MYENTITY' : "Artikel wirklich löschen?"
|
||||
});
|
||||
|
||||
For most core modules, these files are generated by a
|
||||
[build task](https://github.com/silverstripe/silverstripe-buildtools/blob/master/src/GenerateJavascriptI18nTask.php), with the actual source files in a JSON
|
||||
[build task](https://github.com/silverstripe/silverstripe-buildtools/blob/master/src/GenerateJavascriptI18nTask.php),
|
||||
with the actual source files in a JSON
|
||||
format which can be processed more easily by external translation providers (see `javascript/lang/src`).
|
||||
|
||||
### Basic Usage
|
||||
@ -386,6 +382,7 @@ format which can be processed more easily by external translation providers (see
|
||||
ss.i18n._t('MYMODULE.MYENTITY'),
|
||||
42,
|
||||
'Douglas Adams'
|
||||
));
|
||||
// Displays: "Really delete 42 articles by Douglas Adams?"
|
||||
|
||||
|
||||
|
@ -8,16 +8,17 @@ example "framework" and "cms". These two modules are the core functionality and
|
||||
templates for any initial installation.
|
||||
|
||||
If you want to add generic functionality that isn't specific to your
|
||||
project, like a forum, an ecommerce package or a blog you can do it like this;
|
||||
project, like a forum, an ecommerce package or a blog you can do it like this:
|
||||
|
||||
1. Create another directory at the root level (same level as "framework" and
|
||||
"cms")
|
||||
2. You must create a _config.php inside your module directory, or else
|
||||
SilverStripe will not include it
|
||||
3. Inside your module directory, follow our [directory structure guidelines](/topics/directory-structure#module_structure)
|
||||
1. Create another directory at the root level (same level as "framework"
|
||||
and "cms"). This will contain all your module files.
|
||||
2. The module directory must contain a `_config` sub-directory, or a
|
||||
`_config.php` file to be recognised.
|
||||
3. Inside your module directory, follow our
|
||||
[directory structure guidelines](/topics/directory-structure#module_structure)
|
||||
|
||||
As long as your module has a `_config.php` file inside it, SilverStripe will
|
||||
automatically include any PHP classes from that module.
|
||||
Once this is done, SilverStripe will automatically include any PHP classes and
|
||||
templates from within your module.
|
||||
|
||||
## Tips
|
||||
|
||||
@ -93,7 +94,9 @@ provide an extension to [SiteConfig](/reference/siteconfig).
|
||||
If you wish to submit your module to our public directory, you take
|
||||
responsibility for a certain level of code quality, adherence to conventions,
|
||||
writing documentation, and releasing updates. See
|
||||
[contributing](/misc/contributing).
|
||||
[contributing](/misc/contributing). All modules should be published
|
||||
on [addons.silverstripe.org](http://addons.silverstripe.org) to make them
|
||||
discoverable by others.
|
||||
|
||||
### Composer and Packagist
|
||||
|
||||
@ -109,60 +112,67 @@ A basic usage of a module for 3.1 that requires the CMS would look similar to
|
||||
this:
|
||||
|
||||
{
|
||||
"name": "yourname/silverstripe-modulename",
|
||||
"description": "..",
|
||||
"type": "silverstripe-module",
|
||||
"keywords": ["silverstripe", ".."],
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [{
|
||||
"name": "Your Name",
|
||||
"email": "Your Email"
|
||||
}],
|
||||
"require": {
|
||||
"silverstripe/framework": ">=3.1.x-dev,<4.0"
|
||||
}
|
||||
"name": "your-vendor-name/module-name",
|
||||
"description": "One-liner describing your module",
|
||||
"type": "silverstripe-module",
|
||||
"homepage": "http://github.com/your-vendor-name/module-name",
|
||||
"keywords": ["silverstripe", "some-tag", "some-other-tag"],
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
{"name": "Your Name","email": "your@email.com"}
|
||||
],
|
||||
"support": {
|
||||
"issues": "http://github.com/your-vendor-name/module-name/issues"
|
||||
},
|
||||
"require": {
|
||||
"silverstripe/cms": "~3.1",
|
||||
"silverstripe/framework": "~3.1"
|
||||
},
|
||||
"extra": {
|
||||
"installer-name": "module-name",
|
||||
"screenshots": [
|
||||
"relative/path/screenshot1.png",
|
||||
"http://myhost.com/screenshot2.png"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Once your module is released, submit it to [Packagist](https://packagist.org/)
|
||||
to have the module accessible to developers.
|
||||
to have the module accessible to developers. It'll automatically get picked
|
||||
up by [addons.silverstripe.org](http://addons.silverstripe.org/).
|
||||
|
||||
### Versioning
|
||||
|
||||
Over time you may have to release new versions of your module to continue to
|
||||
work with newer versions of SilverStripe. By using composer, this is made easy
|
||||
work with newer versions of SilverStripe. By using Composer, this is made easy
|
||||
for developers by allowing them to specify what version they want to use. Each
|
||||
version of your module should be a separate branch in your version control and
|
||||
each branch should have a `composer.json` file explicitly defining what versions
|
||||
of SilverStripe you support.
|
||||
|
||||
<div class="notice" markdown='1'>
|
||||
The convention to follow for support is the `master` or `trunk` branch of your
|
||||
code should always be the one to work with the `master` branch of SilverStripe.
|
||||
Other branches should be created as needed for other SilverStripe versions you
|
||||
want to support.
|
||||
</div>
|
||||
Say you have a module which supports SilverStripe 3.0.
|
||||
A new release of this module takes advantage of new features
|
||||
in SilverStripe 3.1. In this case, you would create a new branch
|
||||
for the 3.0 compatible codebase of your module.
|
||||
This allows you to continue fixing bugs on this older release branch.
|
||||
|
||||
For example, if you release a module for 3.0 which works well but doesn't work
|
||||
in 3.1.0 you should provide a separate `branch` of the module for 3.0 support.
|
||||
As a convention, the `master` or `trunk` branch of your
|
||||
module should always work with the `master` branch of SilverStripe.
|
||||
Other branches should be created on your module as needed if they're
|
||||
required to support specific SilverStripe releases.
|
||||
|
||||
// for module that supports 3.0.1. (git branch 1.0)
|
||||
"require": {
|
||||
"silverstripe/framework": "3.0.*",
|
||||
}
|
||||
You can have an overlap in supported versions,
|
||||
e.g two branches in your module both support SilverStripe 3.1.
|
||||
In this case, you should explain the differences in your `README.md` file.
|
||||
|
||||
// for branch of the module that only supports 3.1 (git branch master)
|
||||
"require": {
|
||||
"silverstripe/framework": ">=3.1.*",
|
||||
}
|
||||
|
||||
You can have an overlap in supported versions (e.g two branches for 3.1) but you
|
||||
should explain the differences in your `README.md` file.
|
||||
|
||||
If you want to change the minimum supported version of your module, make sure
|
||||
you create a new branch which continues to support the minimum version as it
|
||||
stands before you update the main branch.
|
||||
Here's some common values for your `require` section
|
||||
(see [getcomposer.org](http://getcomposer.org/doc/01-basic-usage.md#package-versions) for details):
|
||||
|
||||
* `3.0.*`: Version `3.0`, including `3.0.1`, `3.0.2` etc, excluding `3.1`
|
||||
* `~3.0`: Version `3.0` or higher, including `3.0.1` and `3.1` etc, excluding `4.0`
|
||||
* `~3.0,<3.2`: Version `3.0` or higher, up until `3.2`, which is excluded
|
||||
* `~3.0,>3.0.4`: Version `3.0` or higher, starting with `3.0.4`
|
||||
|
||||
## Reference
|
||||
|
||||
|
@ -112,11 +112,10 @@ The base_tag variable is replaced with the HTML [base element](http://www.w3.org
|
||||
ensures the browser knows where to locate your site's images and css files.
|
||||
|
||||
:::ss
|
||||
$MetaTitle
|
||||
$Title
|
||||
$SiteConfig.Title
|
||||
|
||||
These three variables are found within the html `<title>` tag, and are replaced by the text set in the "Meta Title", "Page Name", or "Settings -> Site Title" fields in the CMS.
|
||||
These two variables are found within the html `<title>` tag, and are replaced by the "Page Name" and "Settings -> Site Title" fields in the CMS.
|
||||
|
||||
:::ss
|
||||
$MetaTags
|
||||
|
@ -437,6 +437,8 @@ class Mailer extends Object {
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage email
|
||||
* @deprecated 3.1
|
||||
*/
|
||||
function htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles = false, $customheaders = false,
|
||||
@ -449,6 +451,8 @@ function htmlEmail($to, $from, $subject, $htmlContent, $attachedFiles = false, $
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage email
|
||||
* @deprecated 3.1
|
||||
*/
|
||||
function plaintextEmail($to, $from, $subject, $plainContent, $attachedFiles, $customheaders = false) {
|
||||
@ -459,6 +463,8 @@ function plaintextEmail($to, $from, $subject, $plainContent, $attachedFiles, $cu
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage email
|
||||
* @deprecated 3.1
|
||||
*/
|
||||
function encodeMultipart($parts, $contentType, $headers = false) {
|
||||
@ -469,6 +475,8 @@ function encodeMultipart($parts, $contentType, $headers = false) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage email
|
||||
* @deprecated 3.1
|
||||
*/
|
||||
function wrapImagesInline($htmlContent) {
|
||||
@ -479,6 +487,8 @@ function wrapImagesInline($htmlContent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage email
|
||||
* @deprecated 3.1
|
||||
*/
|
||||
function wrapImagesInline_rewriter($url) {
|
||||
@ -490,6 +500,8 @@ function wrapImagesInline_rewriter($url) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage email
|
||||
* @deprecated 3.1
|
||||
*/
|
||||
function processHeaders($headers, $body = false) {
|
||||
@ -500,6 +512,8 @@ function processHeaders($headers, $body = false) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage email
|
||||
* @deprecated 3.1
|
||||
*/
|
||||
function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $extraHeaders = "") {
|
||||
@ -510,6 +524,8 @@ function encodeFileForEmail($file, $destFileName = false, $disposition = NULL, $
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage email
|
||||
* @deprecated 3.1
|
||||
*/
|
||||
function QuotedPrintable_encode($quotprint) {
|
||||
@ -520,6 +536,8 @@ function QuotedPrintable_encode($quotprint) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage email
|
||||
* @deprecated 3.1
|
||||
*/
|
||||
function validEmailAddr($emailAddress) {
|
||||
|
@ -61,6 +61,18 @@
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage filesystem
|
||||
*
|
||||
* @property string Name Basename of the file
|
||||
* @property string Title Title of the file
|
||||
* @property string Filename Filename including path
|
||||
* @property string Content
|
||||
* @property string ShowInSearch Boolean that indicates if file is shown in search. Doesn't apply to Folder
|
||||
*
|
||||
* @property int ParentID ID of parent File/Folder
|
||||
* @property int OwnerID ID of Member who owns the file
|
||||
*
|
||||
* @method File Parent() Returns parent File
|
||||
* @method Member Owner() Returns Member object of file owner.
|
||||
*/
|
||||
class File extends DataObject {
|
||||
|
||||
@ -596,9 +608,14 @@ class File extends DataObject {
|
||||
$base = pathinfo($name, PATHINFO_BASENAME);
|
||||
$ext = self::get_file_extension($name);
|
||||
$suffix = 1;
|
||||
while(DataObject::get_one("File", "\"Name\" = '" . Convert::raw2sql($name)
|
||||
. "' AND \"ParentID\" = " . (int)$this->ParentID)) {
|
||||
|
||||
while(File::get()->filter(array(
|
||||
'Name' => $name,
|
||||
'ParentID' => (int) $this->ParentID
|
||||
))->exclude(array(
|
||||
'ID' => $this->ID
|
||||
))->first()
|
||||
) {
|
||||
$suffix++;
|
||||
$name = "$base-$suffix$ext";
|
||||
}
|
||||
|
@ -486,7 +486,10 @@ class GDBackend extends Object implements Image_Backend {
|
||||
}
|
||||
|
||||
/**
|
||||
* Backwards compatibility
|
||||
* This class is maintained for backwards-compatibility only. Please use the {@link GDBackend} class instead.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage filesystem
|
||||
*/
|
||||
class GD extends GDBackend {
|
||||
|
||||
|
@ -90,8 +90,13 @@ class CheckboxSetField extends OptionsetField {
|
||||
if($values instanceof SS_List || is_array($values)) {
|
||||
$items = $values;
|
||||
} else {
|
||||
$items = explode(',', $values);
|
||||
$items = str_replace('{comma}', ',', $items);
|
||||
if($values === null) {
|
||||
$items = array();
|
||||
}
|
||||
else {
|
||||
$items = explode(',', $values);
|
||||
$items = str_replace('{comma}', ',', $items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,13 @@ class ConfirmedPasswordField extends FormField {
|
||||
* @param boolean $showOnClick
|
||||
*/
|
||||
protected $showOnClick = false;
|
||||
|
||||
|
||||
/**
|
||||
* A place to temporarly store the confirm password value
|
||||
* @var string
|
||||
*/
|
||||
protected $confirmValue;
|
||||
|
||||
/**
|
||||
* Title for the link that triggers the visibility of password fields.
|
||||
@ -255,10 +262,8 @@ class ConfirmedPasswordField extends FormField {
|
||||
$oldValue = $this->value;
|
||||
|
||||
if(is_array($value)) {
|
||||
//only set the value if it's valid!
|
||||
if($this->validate(RequiredFields::create())) {
|
||||
$this->value = $value['_Password'];
|
||||
}
|
||||
$this->value = $value['_Password'];
|
||||
$this->confirmValue = $value['_ConfirmPassword'];
|
||||
|
||||
if($this->showOnClick && isset($value['_PasswordFieldVisible'])) {
|
||||
$this->children->fieldByName($this->getName() . '[_PasswordFieldVisible]')
|
||||
@ -282,6 +287,20 @@ class ConfirmedPasswordField extends FormField {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the names of the child fields when updating name of field.
|
||||
*
|
||||
* @param string $name new name to give to the field.
|
||||
*/
|
||||
public function setName($name) {
|
||||
$this->children->fieldByName($this->getName() . '[_Password]')
|
||||
->setName($name . '[_Password]');
|
||||
$this->children->fieldByName($this->getName() . '[_ConfirmPassword]')
|
||||
->setName($name . '[_ConfirmPassword]');
|
||||
|
||||
return parent::setName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the field was actually shown on the client side - if not,
|
||||
* we don't validate or save it.
|
||||
@ -309,9 +328,9 @@ class ConfirmedPasswordField extends FormField {
|
||||
|
||||
$passwordField = $this->children->fieldByName($name.'[_Password]');
|
||||
$passwordConfirmField = $this->children->fieldByName($name.'[_ConfirmPassword]');
|
||||
$passwordField->setValue($_POST[$name]['_Password']);
|
||||
$passwordConfirmField->setValue($_POST[$name]['_ConfirmPassword']);
|
||||
|
||||
$passwordField->setValue($this->value);
|
||||
$passwordConfirmField->setValue($this->confirmValue);
|
||||
|
||||
$value = $passwordField->Value();
|
||||
|
||||
// both password-fields should be the same
|
||||
|
@ -164,9 +164,24 @@ class DropdownField extends FormField {
|
||||
|
||||
if ($source) {
|
||||
foreach($source as $value => $title) {
|
||||
// check against value, fallback to a type check comparison when !value
|
||||
$selected = ($value) ? ($value == $this->value) : ($value === $this->value);
|
||||
$disabled = (in_array($value, $this->disabledItems, true)) ? 'disabled' : false;
|
||||
$selected = false;
|
||||
if($value === '' && ($this->value === '' || $this->value === null)) {
|
||||
$selected = true;
|
||||
} else {
|
||||
// check against value, fallback to a type check comparison when !value
|
||||
if($value) {
|
||||
$selected = ($value == $this->value);
|
||||
} else {
|
||||
$selected = ($value === $this->value) || (((string) $value) === ((string) $this->value));
|
||||
}
|
||||
|
||||
$this->isSelected = $selected;
|
||||
}
|
||||
|
||||
$disabled = false;
|
||||
if(in_array($value, $this->disabledItems) && $title != $this->emptyString ){
|
||||
$disabled = 'disabled';
|
||||
}
|
||||
|
||||
$options[] = new ArrayData(array(
|
||||
'Title' => $title,
|
||||
|
@ -356,32 +356,33 @@ class FormField extends RequestHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more CSS-classes to the formfield-container.
|
||||
* Add one or more CSS-classes to the formfield-container. Multiple class
|
||||
* names should be space delimited.
|
||||
*
|
||||
* @param $class String
|
||||
* @param string $class
|
||||
*/
|
||||
public function addExtraClass($class) {
|
||||
//split at white space to extract all the classes
|
||||
$classes = preg_split('/\s+/', $class);
|
||||
|
||||
foreach ($classes as $class) {
|
||||
//add each class one by one
|
||||
$this->extraClasses[$class] = $class;
|
||||
$this->extraClasses[$class] = $class;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove one or more CSS-classes from the formfield-container.
|
||||
*
|
||||
* @param $class String
|
||||
* @param string $class
|
||||
*/
|
||||
public function removeExtraClass($class) {
|
||||
//split at white space to extract all the classes
|
||||
$classes = preg_split('/\s+/', $class);
|
||||
|
||||
foreach ($classes as $class) {
|
||||
//unset each class one by one
|
||||
unset($this->extraClasses[$class]);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -759,6 +759,8 @@ class HtmlEditorField_Toolbar extends RequestHandler {
|
||||
* such as file name or the URL.
|
||||
*
|
||||
* @todo Remove once core has support for remote files
|
||||
* @package forms
|
||||
* @subpackage fields-formattedinput
|
||||
*/
|
||||
class HtmlEditorField_File extends ViewableData {
|
||||
|
||||
@ -827,6 +829,13 @@ class HtmlEditorField_File extends ViewableData {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulation of an oembed tag, linking to an external media source.
|
||||
*
|
||||
* @see Oembed
|
||||
* @package forms
|
||||
* @subpackage fields-formattedinput
|
||||
*/
|
||||
class HtmlEditorField_Embed extends HtmlEditorField_File {
|
||||
protected $oembed;
|
||||
|
||||
@ -910,6 +919,12 @@ class HtmlEditorField_Embed extends HtmlEditorField_File {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulation of an image tag, linking to an image either internal or external to the site.
|
||||
*
|
||||
* @package forms
|
||||
* @subpackage fields-formattedinput
|
||||
*/
|
||||
class HtmlEditorField_Image extends HtmlEditorField_File {
|
||||
|
||||
protected $width;
|
||||
|
@ -7,7 +7,8 @@
|
||||
* See www.tinymce.com/wiki.php/configuration:valid_elements for details on the spec of TinyMCE's
|
||||
* whitelist configuration
|
||||
*
|
||||
* Class HtmlEditorSanitiser
|
||||
* @package forms
|
||||
* @subpackage fields-formattedinput
|
||||
*/
|
||||
class HtmlEditorSanitiser {
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* @author Ingo Schommer, SilverStripe Ltd. (<firstname>@silverstripe.com)
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-formattedinput
|
||||
*/
|
||||
class MoneyField extends FormField {
|
||||
@ -120,6 +120,8 @@ class MoneyField extends FormField {
|
||||
*/
|
||||
public function performReadonlyTransformation() {
|
||||
$clone = clone $this;
|
||||
$clone->fieldAmount = $clone->fieldAmount->performReadonlyTransformation();
|
||||
$clone->fieldCurrency = $clone->fieldCurrency->performReadonlyTransformation();
|
||||
$clone->setReadonly(true);
|
||||
return $clone;
|
||||
}
|
||||
|
@ -22,8 +22,8 @@
|
||||
* </code>
|
||||
*
|
||||
* @author Zauberfisch
|
||||
* @package framework
|
||||
* @subpackage forms
|
||||
* @package forms
|
||||
* @subpackages fields-files
|
||||
*/
|
||||
class UploadField extends FileField {
|
||||
|
||||
@ -1349,8 +1349,8 @@ class UploadField extends FileField {
|
||||
* RequestHandler for actions (edit, remove, delete) on a single item (File) of the UploadField
|
||||
*
|
||||
* @author Zauberfisch
|
||||
* @package framework
|
||||
* @subpackage forms
|
||||
* @package forms
|
||||
* @subpackages fields-files
|
||||
*/
|
||||
class UploadField_ItemHandler extends RequestHandler {
|
||||
|
||||
@ -1510,6 +1510,9 @@ class UploadField_ItemHandler extends RequestHandler {
|
||||
|
||||
/**
|
||||
* File selection popup for attaching existing files.
|
||||
*
|
||||
* @package forms
|
||||
* @subpackages fields-files
|
||||
*/
|
||||
class UploadField_SelectHandler extends RequestHandler {
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
* @see SS_List
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
* @property GridState_Data $State The gridstate of this object
|
||||
*/
|
||||
@ -765,7 +765,7 @@ class GridField extends FormField {
|
||||
* This class is the base class when you want to have an action that alters
|
||||
* the state of the {@link GridField}, rendered as a button element.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridField_FormAction extends FormAction {
|
||||
|
@ -15,7 +15,7 @@
|
||||
* For easier setup, have a look at a sample configuration in
|
||||
* {@link GridFieldConfig_RelationEditor}.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldAddExistingAutocompleter
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Only returns a button if {@link DataObject->canCreate()} for this record
|
||||
* returns true.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldAddNewButton implements GridField_HTMLProvider {
|
||||
|
@ -8,7 +8,7 @@
|
||||
* This row provides two new HTML fragment spaces: 'toolbar-header-left' and
|
||||
* 'toolbar-header-right'.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldButtonRow implements GridField_HTMLProvider {
|
||||
|
@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Base interface for all components that can be added to GridField.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
interface GridFieldComponent {
|
||||
@ -13,7 +13,7 @@ interface GridFieldComponent {
|
||||
* A GridField manipulator that provides HTML for the header/footer rows, or f
|
||||
* or before/after the template.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
interface GridField_HTMLProvider extends GridFieldComponent {
|
||||
@ -43,7 +43,7 @@ interface GridField_HTMLProvider extends GridFieldComponent {
|
||||
*
|
||||
* Used once per record/row.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
interface GridField_ColumnProvider extends GridFieldComponent {
|
||||
@ -110,7 +110,7 @@ interface GridField_ColumnProvider extends GridFieldComponent {
|
||||
*
|
||||
* @see {@link GridField_FormAction}.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
interface GridField_ActionProvider extends GridFieldComponent {
|
||||
@ -151,7 +151,7 @@ interface GridField_ActionProvider extends GridFieldComponent {
|
||||
* Generally, the data manipulator will make use of to {@link GridState}
|
||||
* variables to decide how to modify the {@link DataList}.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
interface GridField_DataManipulator extends GridFieldComponent {
|
||||
@ -177,7 +177,7 @@ interface GridField_DataManipulator extends GridFieldComponent {
|
||||
* For example a URL that will return JSON-formatted data for a javascript
|
||||
* grid control.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
interface GridField_URLHandler extends GridFieldComponent {
|
||||
@ -196,7 +196,7 @@ interface GridField_URLHandler extends GridFieldComponent {
|
||||
* A component which is used to handle when a {@link GridField} is saved into
|
||||
* a record.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
interface GridField_SaveHandler extends GridFieldComponent {
|
||||
|
@ -16,7 +16,7 @@
|
||||
* - {@link GridFieldConfig_RecordEditor}
|
||||
* - {@link GridFieldConfig_RelationEditor}
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldConfig extends Object {
|
||||
@ -129,7 +129,7 @@ class GridFieldConfig extends Object {
|
||||
* A simple readonly, paginated view of records, with sortable and searchable
|
||||
* headers.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldConfig_Base extends GridFieldConfig {
|
||||
@ -157,7 +157,7 @@ class GridFieldConfig_Base extends GridFieldConfig {
|
||||
/**
|
||||
* Allows viewing readonly details of individual records.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldConfig_RecordViewer extends GridFieldConfig_Base {
|
||||
@ -174,7 +174,10 @@ class GridFieldConfig_RecordViewer extends GridFieldConfig_Base {
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* Allows editing of records contained within the GridField, instead of only allowing the ability to view records in
|
||||
* the GridField.
|
||||
*
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldConfig_RecordEditor extends GridFieldConfig {
|
||||
@ -223,7 +226,7 @@ class GridFieldConfig_RecordEditor extends GridFieldConfig {
|
||||
* ->setSearchFields('MyField');
|
||||
* </code>
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldConfig_RelationEditor extends GridFieldConfig {
|
||||
|
@ -2,7 +2,7 @@
|
||||
/**
|
||||
* @see GridField
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldDataColumns implements GridField_ColumnProvider {
|
||||
|
@ -15,7 +15,7 @@
|
||||
* $action = new GridFieldDeleteAction(true);
|
||||
* </code>
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldDeleteAction implements GridField_ColumnProvider, GridField_ActionProvider {
|
||||
|
@ -13,7 +13,7 @@
|
||||
* - <FormURL>/field/<GridFieldName>/item/<RecordID>
|
||||
* - <FormURL>/field/<GridFieldName>/item/<RecordID>/edit
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldDetailForm implements GridField_URLHandler {
|
||||
@ -90,6 +90,12 @@ class GridFieldDetailForm implements GridField_URLHandler {
|
||||
$handler = Object::create($class, $gridField, $this, $record, $controller, $this->name);
|
||||
$handler->setTemplate($this->template);
|
||||
|
||||
// if no validator has been set on the GridField and the record has a
|
||||
// CMS validator, use that.
|
||||
if(!$this->getValidator() && method_exists($record, 'getCMSValidator')) {
|
||||
$this->setValidator($record->getCMSValidator());
|
||||
}
|
||||
|
||||
return $handler->handleRequest($request, DataModel::inst());
|
||||
}
|
||||
|
||||
@ -190,7 +196,7 @@ class GridFieldDetailForm implements GridField_URLHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldDetailForm_ItemRequest extends RequestHandler {
|
||||
|
@ -10,7 +10,7 @@
|
||||
* The default routing applies to the {@link GridFieldDetailForm} component,
|
||||
* which has to be added separately to the {@link GridField} configuration.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldEditButton implements GridField_ColumnProvider {
|
||||
|
@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Adds an "Export list" button to the bottom of a {@link GridField}.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
*
|
||||
* @see GridField
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldFilterHeader implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider {
|
||||
|
@ -12,7 +12,7 @@
|
||||
* The purpose of this class is to have a footer that can round off
|
||||
* {@link GridField} without having to use pagination.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldFooter implements GridField_HTMLProvider {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* hierarchical data. Requires the managed record to have a "getParent()"
|
||||
* method or has_one relationship called "Parent".
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldLevelup extends Object implements GridField_HTMLProvider {
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Depends on {@link GridFieldPaginator} being added to the {@link GridField}.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldPageCount implements GridField_HTMLProvider {
|
||||
|
@ -3,7 +3,7 @@
|
||||
* GridFieldPaginator paginates the {@link GridField} list and adds controls
|
||||
* to the bottom of the {@link GridField}.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider {
|
||||
|
@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Adds an "Print" button to the bottom or top of a GridField.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionProvider, GridField_URLHandler {
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* @see GridField
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider {
|
||||
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* The header serves to display the name of the data the GridField is showing.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldToolbarHeader implements GridField_HTMLProvider {
|
||||
|
@ -4,7 +4,7 @@
|
||||
* disabled by default and intended for use in readonly {@link GridField}
|
||||
* instances.
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridFieldViewButton implements GridField_ColumnProvider {
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
* @see GridField
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridState extends HiddenField {
|
||||
@ -121,7 +121,7 @@ class GridState extends HiddenField {
|
||||
*
|
||||
* @see GridState
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridState_Data {
|
||||
@ -192,7 +192,7 @@ class GridState_Data {
|
||||
/**
|
||||
* @see GridState
|
||||
*
|
||||
* @package framework
|
||||
* @package forms
|
||||
* @subpackage fields-gridfield
|
||||
*/
|
||||
class GridState_Component implements GridField_HTMLProvider {
|
||||
|
@ -6,7 +6,6 @@ require_once 'Zend/Translate/Adapter.php';
|
||||
* @package framework
|
||||
* @subpackage i18n
|
||||
*/
|
||||
|
||||
class i18nSSLegacyAdapter extends Zend_Translate_Adapter implements i18nTranslateAdapterInterface {
|
||||
|
||||
/**
|
||||
@ -65,6 +64,10 @@ class i18nSSLegacyAdapter extends Zend_Translate_Adapter implements i18nTranslat
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage i18n
|
||||
*/
|
||||
class i18nSSLegacyAdapter_Iterator extends RecursiveIteratorIterator {
|
||||
|
||||
protected $keyStack = array();
|
||||
|
@ -483,6 +483,9 @@ class i18nTextCollector extends Object {
|
||||
/**
|
||||
* Allows serialization of entity definitions collected through {@link i18nTextCollector}
|
||||
* into a persistent format, usually on the filesystem.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage i18n
|
||||
*/
|
||||
interface i18nTextCollector_Writer {
|
||||
/**
|
||||
@ -499,6 +502,9 @@ interface i18nTextCollector_Writer {
|
||||
|
||||
/**
|
||||
* Legacy writer for 2.x style persistence.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage i18n
|
||||
*/
|
||||
class i18nTextCollector_Writer_Php implements i18nTextCollector_Writer {
|
||||
|
||||
@ -577,6 +583,9 @@ class i18nTextCollector_Writer_Php implements i18nTextCollector_Writer {
|
||||
|
||||
/**
|
||||
* Writes files compatible with {@link i18nRailsYamlAdapter}.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage i18n
|
||||
*/
|
||||
class i18nTextCollector_Writer_RailsYaml implements i18nTextCollector_Writer {
|
||||
|
||||
@ -631,6 +640,9 @@ class i18nTextCollector_Writer_RailsYaml implements i18nTextCollector_Writer {
|
||||
|
||||
/**
|
||||
* Parser that scans through a template and extracts the parameters to the _t and <%t calls
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage i18n
|
||||
*/
|
||||
class i18nTextCollector_Parser extends SSTemplateParser {
|
||||
|
||||
|
@ -10,8 +10,6 @@
|
||||
$('.permissioncheckboxset .valADMIN input').entwine({
|
||||
onmatch: function() {
|
||||
this._super();
|
||||
|
||||
this.toggleCheckboxes();
|
||||
},
|
||||
onunmatch: function() {
|
||||
this._super();
|
||||
@ -55,30 +53,29 @@
|
||||
}).find('.checkbox').not(this);
|
||||
},
|
||||
onmatch: function() {
|
||||
var checkboxes = this.getCheckboxesExceptThisOne();
|
||||
if($(this).is(':checked')) {
|
||||
checkboxes.each(function() {
|
||||
$(this).attr('disabled', 'disabled');
|
||||
$(this).attr('checked', 'checked');
|
||||
});
|
||||
}
|
||||
|
||||
this.toggleCheckboxes();
|
||||
|
||||
this._super();
|
||||
},
|
||||
onunmatch: function() {
|
||||
this._super();
|
||||
},
|
||||
onclick: function(e) {
|
||||
this.toggleCheckboxes();
|
||||
},
|
||||
toggleCheckboxes: function() {
|
||||
var checkboxes = this.getCheckboxesExceptThisOne();
|
||||
if($(this).is(':checked')) {
|
||||
checkboxes.each(function() {
|
||||
$(this).attr('disabled', 'disabled');
|
||||
$(this).attr('checked', 'checked');
|
||||
$(this).data('PermissionCheckboxSetField.oldChecked', $(this).is(':checked'));
|
||||
$(this).data('PermissionCheckboxSetField.oldDisabled', $(this).is(':disabled'));
|
||||
$(this).prop('disabled', 'disabled');
|
||||
$(this).prop('checked', 'checked');
|
||||
});
|
||||
} else {
|
||||
checkboxes.each(function() {
|
||||
$(this).prop('checked', false);
|
||||
$(this).prop('disabled', false);
|
||||
$(this).prop('checked', $(this).data('PermissionCheckboxSetField.oldChecked'));
|
||||
$(this).prop('disabled', $(this).data('PermissionCheckboxSetField.oldDisabled'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -281,9 +281,14 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
|
||||
*/
|
||||
public function map($keyfield = 'ID', $titlefield = 'Title') {
|
||||
$map = array();
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
$map[$this->extractValue($item, $keyfield)] = $this->extractValue($item, $titlefield);
|
||||
$map[$this->extractValue($item, $keyfield)] = $this->extractValue(
|
||||
$item,
|
||||
$titlefield
|
||||
);
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
@ -296,7 +301,9 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
|
||||
*/
|
||||
public function find($key, $value) {
|
||||
foreach ($this->items as $item) {
|
||||
if ($this->extractValue($item, $key) == $value) return $item;
|
||||
if ($this->extractValue($item, $key) == $value) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,9 +315,11 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
|
||||
*/
|
||||
public function column($colName = 'ID') {
|
||||
$result = array();
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
$result[] = $this->extractValue($item, $colName);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
@ -410,9 +419,11 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
|
||||
*/
|
||||
public function canFilterBy($by) {
|
||||
$firstRecord = $this->first();
|
||||
|
||||
if ($firstRecord === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_key_exists($by, $firstRecord);
|
||||
}
|
||||
|
||||
@ -470,9 +481,11 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
|
||||
|
||||
public function byID($id) {
|
||||
$firstElement = $this->filter("ID", $id)->first();
|
||||
|
||||
if ($firstElement === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $firstElement;
|
||||
}
|
||||
|
||||
@ -490,10 +503,13 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
|
||||
gettype($callback)
|
||||
));
|
||||
}
|
||||
|
||||
$output = ArrayList::create();
|
||||
|
||||
foreach($this as $item) {
|
||||
if(call_user_func($callback, $item, $this)) $output->push($item);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
@ -588,7 +604,11 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function offsetSet($offset, $value) {
|
||||
$this->items[$offset] = $value;
|
||||
if($offset == null) {
|
||||
$this->items[] = $value;
|
||||
} else {
|
||||
$this->items[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,6 +65,11 @@
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage model
|
||||
*
|
||||
* @property integer ID ID of the DataObject, 0 if the DataObject doesn't exist in database.
|
||||
* @property string ClassName Class name of the DataObject
|
||||
* @property string LastEdited Date and time of DataObject's last modification.
|
||||
* @property string Created Date and time of DataObject creation.
|
||||
*/
|
||||
class DataObject extends ViewableData implements DataObjectInterface, i18nEntityProvider {
|
||||
|
||||
|
@ -326,7 +326,7 @@ abstract class SS_Database {
|
||||
$this->transCreateTable($table, $options, $extensions);
|
||||
$this->alterationMessage("Table $table: created","created");
|
||||
} else {
|
||||
if(Config::inst()->get('Database', 'check_and_repair_on_build')) {
|
||||
if(Config::inst()->get('SS_Database', 'check_and_repair_on_build')) {
|
||||
$this->checkAndRepairTable($table, $options);
|
||||
}
|
||||
|
||||
@ -1050,4 +1050,4 @@ abstract class SS_Database {
|
||||
public function releaseLock($name) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
* applied, rather than applying the filter in place
|
||||
*
|
||||
* @see SS_List, SS_Sortable, SS_Limitable
|
||||
* @package framework
|
||||
* @subpackage model
|
||||
*/
|
||||
interface SS_Filterable {
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
* applied, rather than applying the limit in place
|
||||
*
|
||||
* @see SS_List, SS_Sortable, SS_Filterable
|
||||
* @package framework
|
||||
* @subpackage model
|
||||
*/
|
||||
interface SS_Limitable {
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
* applied, rather than applying the sort in place
|
||||
*
|
||||
* @see SS_List, SS_Filterable, SS_Limitable
|
||||
* @package framework
|
||||
* @subpackage model
|
||||
*/
|
||||
interface SS_Sortable {
|
||||
|
||||
|
@ -32,6 +32,7 @@ class ValidationResult extends Object {
|
||||
* Record an error against this validation result,
|
||||
* @param $message The validation error message
|
||||
* @param $code An optional error code string, that can be accessed with {@link $this->codeList()}.
|
||||
* @return ValidationResult this
|
||||
*/
|
||||
public function error($message, $code = null) {
|
||||
$this->isValid = false;
|
||||
@ -47,10 +48,13 @@ class ValidationResult extends Object {
|
||||
} else {
|
||||
$this->errorList[] = $message;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the result is valid.
|
||||
* @return boolean
|
||||
*/
|
||||
public function valid() {
|
||||
return $this->isValid;
|
||||
@ -58,6 +62,7 @@ class ValidationResult extends Object {
|
||||
|
||||
/**
|
||||
* Get an array of errors
|
||||
* @return array
|
||||
*/
|
||||
public function messageList() {
|
||||
return $this->errorList;
|
||||
@ -65,6 +70,7 @@ class ValidationResult extends Object {
|
||||
|
||||
/**
|
||||
* Get an array of error codes
|
||||
* @return array
|
||||
*/
|
||||
public function codeList() {
|
||||
$codeList = array();
|
||||
@ -74,6 +80,7 @@ class ValidationResult extends Object {
|
||||
|
||||
/**
|
||||
* Get the error message as a string.
|
||||
* @return string
|
||||
*/
|
||||
public function message() {
|
||||
return implode("; ", $this->errorList);
|
||||
@ -81,6 +88,7 @@ class ValidationResult extends Object {
|
||||
|
||||
/**
|
||||
* Get a starred list of all messages
|
||||
* @return string
|
||||
*/
|
||||
public function starredList() {
|
||||
return " * " . implode("\n * ", $this->errorList);
|
||||
@ -90,10 +98,15 @@ class ValidationResult extends Object {
|
||||
* Combine this Validation Result with the ValidationResult given in other.
|
||||
* It will be valid if both this and the other result are valid.
|
||||
* This object will be modified to contain the new validation information.
|
||||
*
|
||||
* @param ValidationResult the validation result object to combine
|
||||
* @return ValidationResult this
|
||||
*/
|
||||
public function combineAnd(ValidationResult $other) {
|
||||
$this->isValid = $this->isValid && $other->valid();
|
||||
$this->errorList = array_merge($this->errorList, $other->messageList());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1341,6 +1341,22 @@ class Versioned extends DataExtension {
|
||||
public function cacheKeyComponent() {
|
||||
return 'versionedmode-'.self::get_reading_mode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of possible stages.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getVersionedStages() {
|
||||
return $this->stages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultStage() {
|
||||
return $this->defaultStage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@
|
||||
* within a HTMLText or HTMLVarchar field when rendered into a template. The API is inspired by and very similar to the
|
||||
* [Wordpress implementation](http://codex.wordpress.org/Shortcode_API) of shortcodes.
|
||||
*
|
||||
* @see http://doc.silverstripe.org/framework/en/topics/shortcodes
|
||||
* @see http://doc.silverstripe.org/framework/en/reference/shortcodes
|
||||
* @package framework
|
||||
* @subpackage misc
|
||||
*/
|
||||
|
@ -4,6 +4,20 @@
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*
|
||||
* @property string Title Name of the group
|
||||
* @property string Description Description of the group
|
||||
* @property string Code Group code
|
||||
* @property string Locked Boolean indicating whether group is locked in security panel
|
||||
* @property int Sort
|
||||
* @property string HtmlEditorConfig
|
||||
*
|
||||
* @property int ParentID ID of parent group
|
||||
*
|
||||
* @method Group Parent() Return parent group
|
||||
* @method HasManyList Permissions() List of group permissions
|
||||
* @method HasManyList Groups() List of child groups
|
||||
* @method ManyManyList Roles() List of PermissionRoles
|
||||
*/
|
||||
class Group extends DataObject {
|
||||
|
||||
|
@ -11,6 +11,14 @@
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*
|
||||
* @property string Email Email address used for login attempt
|
||||
* @property string Status Status of the login attempt, either 'Success' or 'Failure'
|
||||
* @property string IP IP address of user attempting to login
|
||||
*
|
||||
* @property int MemberID ID of the Member, only if Member with Email exists
|
||||
*
|
||||
* @method Member Member() Member object of the user trying to log in, only if Member with Email exists
|
||||
*/
|
||||
class LoginAttempt extends DataObject {
|
||||
|
||||
|
@ -4,6 +4,24 @@
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*
|
||||
* @property string FirstName
|
||||
* @property string Surname
|
||||
* @property string Email
|
||||
* @property string Password
|
||||
* @property string RememberLoginHash
|
||||
* @property int NumVisit
|
||||
* @property string LastVisited Date and time of last visit
|
||||
* @property string AutoLoginHash
|
||||
* @property string AutoLoginExpired
|
||||
* @property string PasswordEncryption
|
||||
* @property string Salt
|
||||
* @property string PasswordExpiry
|
||||
* @property string LockedOutUntil
|
||||
* @property string Locale
|
||||
* @property int FailedLoginCount
|
||||
* @property string DateFormat
|
||||
* @property string TimeFormat
|
||||
*/
|
||||
class Member extends DataObject implements TemplateGlobalProvider {
|
||||
|
||||
@ -627,14 +645,13 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
||||
/**
|
||||
* Returns the current logged in user
|
||||
*
|
||||
* @return bool|Member Returns the member object of the current logged in
|
||||
* user or FALSE.
|
||||
* @return Member|null
|
||||
*/
|
||||
public static function currentUser() {
|
||||
$id = Member::currentUserID();
|
||||
|
||||
if($id) {
|
||||
return DataObject::get_one("Member", "\"Member\".\"ID\" = $id", true, 1);
|
||||
return Member::get()->byId($id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,14 @@
|
||||
* Keep track of users' previous passwords, so that we can check that new passwords aren't changed back to old ones.
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*
|
||||
* @property string Password
|
||||
* @property string Salt
|
||||
* @property string PasswordEncryption
|
||||
*
|
||||
* @property int MemberID ID of the Member
|
||||
*
|
||||
* @method Member Member() Owner of the password
|
||||
*/
|
||||
class MemberPassword extends DataObject {
|
||||
private static $db = array(
|
||||
|
@ -3,6 +3,14 @@
|
||||
* Represents a permission assigned to a group.
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*
|
||||
* @property string Code
|
||||
* @property int Arg
|
||||
* @property int Type
|
||||
*
|
||||
* @property int GroupID
|
||||
*
|
||||
* @method Group Group()
|
||||
*/
|
||||
class Permission extends DataObject implements TemplateGlobalProvider {
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Throw this exception to register that a user doesn't have permission to do the given action
|
||||
* and potentially redirect them to the log-in page. The exception message may be presented to the
|
||||
* user, so it shouldn't be in nerd-speak.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*/
|
||||
class PermissionFailureException extends Exception {
|
||||
|
||||
|
@ -12,6 +12,12 @@
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*
|
||||
* @property string Title
|
||||
* @property string OnlyAdminCanApply
|
||||
*
|
||||
* @method HasManyList Codes() List of PermissionRoleCode objects
|
||||
* @method ManyManyList Groups() List of Group objects
|
||||
*/
|
||||
class PermissionRole extends DataObject {
|
||||
private static $db = array(
|
||||
|
@ -4,6 +4,12 @@
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage security
|
||||
*
|
||||
* @property string Code
|
||||
*
|
||||
* @property int RoleID
|
||||
*
|
||||
* @method PermissionRole Role()
|
||||
*/
|
||||
class PermissionRoleCode extends DataObject {
|
||||
private static $db = array(
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div <% if $Name %>id="$Name"<% end_if %> class="field $Type">
|
||||
<div <% if $Name %>id="$Name"<% end_if %> class="field <% if $extraClass %>$extraClass<% end_if %>">
|
||||
<% if $Title %><label class="left">$Title</label><% end_if %>
|
||||
|
||||
<div class="middleColumn fieldgroup<% if $Zebra %> fieldgroup-$Zebra<% end_if %>">
|
||||
|
@ -89,20 +89,32 @@ class CmsFormsContext extends BehatContext {
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then /^the "(?P<locator>([^"]*))" HTML field should contain "(?P<html>.*)"$/
|
||||
* @Then /^the "(?P<locator>([^"]*))" HTML field should(?P<negative> not? |\s*)contain "(?P<html>.*)"$/
|
||||
*/
|
||||
public function theHtmlFieldShouldContain($locator, $html) {
|
||||
public function theHtmlFieldShouldContain($locator, $negative, $html) {
|
||||
$page = $this->getSession()->getPage();
|
||||
$element = $page->findField($locator);
|
||||
assertNotNull($element, sprintf('HTML field "%s" not found', $locator));
|
||||
|
||||
$actual = $element->getValue();
|
||||
$regex = '/'.preg_quote($html, '/').'/ui';
|
||||
if (!preg_match($regex, $actual)) {
|
||||
$failed = false;
|
||||
|
||||
if(trim($negative)) {
|
||||
if (preg_match($regex, $actual)) {
|
||||
$failed = true;
|
||||
}
|
||||
} else {
|
||||
if (!preg_match($regex, $actual)) {
|
||||
$failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if($failed) {
|
||||
$message = sprintf(
|
||||
'The string "%s" was not found in the HTML of the element matching %s "%s". Actual content: "%s"',
|
||||
$html,
|
||||
'named',
|
||||
'The string "%s" should%sbe found in the HTML of the element matching name "%s". Actual content: "%s"',
|
||||
$html,
|
||||
$negative,
|
||||
$locator,
|
||||
$actual
|
||||
);
|
||||
|
@ -1,9 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class CheckboxSetFieldTest extends SapphireTest {
|
||||
|
||||
protected static $fixture_file = 'CheckboxSetFieldTest.yml';
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
@ -144,7 +146,13 @@ class CheckboxSetFieldTest extends SapphireTest {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
|
||||
class CheckboxSetFieldTest_Article extends DataObject implements TestOnly {
|
||||
|
||||
private static $db = array(
|
||||
"Content" => "Text",
|
||||
);
|
||||
@ -155,7 +163,12 @@ class CheckboxSetFieldTest_Article extends DataObject implements TestOnly {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class CheckboxSetFieldTest_Tag extends DataObject implements TestOnly {
|
||||
|
||||
private static $belongs_many_many = array(
|
||||
'Articles' => 'CheckboxSetFieldTest_Article'
|
||||
);
|
||||
|
@ -4,6 +4,7 @@
|
||||
* @subpackage tests
|
||||
*/
|
||||
class ConfirmedPasswordFieldTest extends SapphireTest {
|
||||
|
||||
public function testSetValue() {
|
||||
$field = new ConfirmedPasswordField('Test', 'Testing', 'valueA');
|
||||
$this->assertEquals('valueA', $field->Value());
|
||||
@ -52,4 +53,23 @@ class ConfirmedPasswordFieldTest extends SapphireTest {
|
||||
$this->assertNotContains("showOnClick", $fieldHTML,
|
||||
"Test class for hiding/showing the form contents is set");
|
||||
}
|
||||
|
||||
public function testValidation() {
|
||||
$field = new ConfirmedPasswordField('Test', 'Testing', array(
|
||||
"_Password" => "abc123",
|
||||
"_ConfirmPassword" => "abc123"
|
||||
));
|
||||
$validator = new RequiredFields();
|
||||
$form = new Form($this, 'Form', new FieldList($field), new FieldList(), $validator);
|
||||
$this->assertTrue($field->validate($validator));
|
||||
$field->setName("TestNew"); //try changing name of field
|
||||
$this->assertTrue($field->validate($validator));
|
||||
//non-matching password should make the field invalid
|
||||
$field->setValue(array(
|
||||
"_Password" => "abc123",
|
||||
"_ConfirmPassword" => "123abc"
|
||||
));
|
||||
$this->assertFalse($field->validate($validator));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -93,7 +93,52 @@ class DropdownFieldTest extends SapphireTest {
|
||||
'Two options exist in the markup, one for the source, one for empty'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testStringZeroValueSelectedOptionBehaviour() {
|
||||
$field = new DropdownField('Field', null, array(
|
||||
'-1' => 'some negative',
|
||||
'0' => 'none',
|
||||
'1' => 'one',
|
||||
'2+' => 'two or more'
|
||||
), '0');
|
||||
|
||||
$selectedOptions = $this->findSelectedOptionElements($field->Field());
|
||||
$this->assertEquals((string) $selectedOptions[0], 'none', 'The selected option is "none"');
|
||||
|
||||
$field = new DropdownField('Field', null, array(
|
||||
'-1' => 'some negative',
|
||||
'0' => 'none',
|
||||
'1' => 'one',
|
||||
'2+' => 'two or more'
|
||||
), 0);
|
||||
|
||||
$selectedOptions = $this->findSelectedOptionElements($field->Field());
|
||||
$this->assertEquals((string) $selectedOptions[0], 'none', 'The selected option is "none"');
|
||||
}
|
||||
|
||||
public function testStringOneValueSelectedOptionBehaviour() {
|
||||
$field = new DropdownField('Field', null, array(
|
||||
'-1' => 'some negative',
|
||||
'0' => 'none',
|
||||
'1' => 'one',
|
||||
'2+' => 'two or more'
|
||||
), '1');
|
||||
|
||||
|
||||
$selectedOptions = $this->findSelectedOptionElements($field->Field());
|
||||
$this->assertEquals((string) $selectedOptions[0], 'one', 'The selected option is "one"');
|
||||
|
||||
$field = new DropdownField('Field', null, array(
|
||||
'-1' => 'some negative',
|
||||
'0' => 'none',
|
||||
'1' => 'one',
|
||||
'2+' => 'two or more'
|
||||
), 1);
|
||||
|
||||
$selectedOptions = $this->findSelectedOptionElements($field->Field());
|
||||
$this->assertEquals((string) $selectedOptions[0], 'one', 'The selected option is "one"');
|
||||
}
|
||||
|
||||
public function testNumberOfSelectOptionsAvailable() {
|
||||
/* Create a field with a blank value */
|
||||
$field = $this->createDropdownField('(Any)');
|
||||
|
@ -1,6 +1,11 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class GridFieldDetailFormTest extends FunctionalTest {
|
||||
|
||||
protected static $fixture_file = 'GridFieldDetailFormTest.yml';
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
@ -9,6 +14,48 @@ class GridFieldDetailFormTest extends FunctionalTest {
|
||||
'GridFieldDetailFormTest_Category',
|
||||
);
|
||||
|
||||
public function testValidator() {
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
$response = $this->get('GridFieldDetailFormTest_Controller');
|
||||
$this->assertFalse($response->isError());
|
||||
$parser = new CSSContentParser($response->getBody());
|
||||
$addlinkitem = $parser->getBySelector('.ss-gridfield .new-link');
|
||||
$addlink = (string) $addlinkitem[0]['href'];
|
||||
|
||||
$response = $this->get($addlink);
|
||||
$this->assertFalse($response->isError());
|
||||
|
||||
$parser = new CSSContentParser($response->getBody());
|
||||
$addform = $parser->getBySelector('#Form_ItemEditForm');
|
||||
$addformurl = (string) $addform[0]['action'];
|
||||
|
||||
$response = $this->post(
|
||||
$addformurl,
|
||||
array(
|
||||
'FirstName' => 'Jeremiah',
|
||||
'ajax' => 1,
|
||||
'action_doSave' => 1
|
||||
)
|
||||
);
|
||||
|
||||
$parser = new CSSContentParser($response->getBody());
|
||||
$errors = $parser->getBySelector('span.required');
|
||||
$this->assertEquals(1, count($errors));
|
||||
|
||||
$response = $this->post(
|
||||
$addformurl,
|
||||
array(
|
||||
'ajax' => 1,
|
||||
'action_doSave' => 1
|
||||
)
|
||||
);
|
||||
|
||||
$parser = new CSSContentParser($response->getBody());
|
||||
$errors = $parser->getBySelector('span.required');
|
||||
$this->assertEquals(2, count($errors));
|
||||
}
|
||||
|
||||
public function testAddForm() {
|
||||
$this->logInWithPermission('ADMIN');
|
||||
$group = GridFieldDetailFormTest_PeopleGroup::get()
|
||||
@ -247,7 +294,13 @@ class GridFieldDetailFormTest extends FunctionalTest {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
|
||||
class GridFieldDetailFormTest_Person extends DataObject implements TestOnly {
|
||||
|
||||
private static $db = array(
|
||||
'FirstName' => 'Varchar',
|
||||
'Surname' => 'Varchar'
|
||||
@ -280,8 +333,19 @@ class GridFieldDetailFormTest_Person extends DataObject implements TestOnly {
|
||||
);
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function getCMSValidator() {
|
||||
return new RequiredFields(array(
|
||||
'FirstName', 'Surname'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
|
||||
class GridFieldDetailFormTest_PeopleGroup extends DataObject implements TestOnly {
|
||||
private static $db = array(
|
||||
'Name' => 'Varchar'
|
||||
@ -306,6 +370,11 @@ class GridFieldDetailFormTest_PeopleGroup extends DataObject implements TestOnly
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
|
||||
class GridFieldDetailFormTest_Category extends DataObject implements TestOnly {
|
||||
|
||||
private static $db = array(
|
||||
@ -331,6 +400,11 @@ class GridFieldDetailFormTest_Category extends DataObject implements TestOnly {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
|
||||
class GridFieldDetailFormTest_Controller extends Controller implements TestOnly {
|
||||
|
||||
private static $allowed_actions = array('Form');
|
||||
@ -354,6 +428,11 @@ class GridFieldDetailFormTest_Controller extends Controller implements TestOnly
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
|
||||
class GridFieldDetailFormTest_GroupController extends Controller implements TestOnly {
|
||||
|
||||
private static $allowed_actions = array('Form');
|
||||
@ -370,6 +449,11 @@ class GridFieldDetailFormTest_GroupController extends Controller implements Test
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
|
||||
class GridFieldDetailFormTest_CategoryController extends Controller implements TestOnly {
|
||||
|
||||
private static $allowed_actions = array('Form');
|
||||
@ -391,5 +475,8 @@ class GridFieldDetailFormTest_CategoryController extends Controller implements T
|
||||
}
|
||||
}
|
||||
|
||||
class GridFieldDetailFormTest_ItemRequest extends GridFieldDetailForm_ItemRequest implements TestOnly {
|
||||
}
|
||||
/**
|
||||
* @package framework
|
||||
* @subpackage tests
|
||||
*/
|
||||
class GridFieldDetailFormTest_ItemRequest extends GridFieldDetailForm_ItemRequest implements TestOnly { }
|
||||
|
@ -5,6 +5,20 @@
|
||||
*/
|
||||
class ArrayListTest extends SapphireTest {
|
||||
|
||||
public function testPushOperator() {
|
||||
$list = new ArrayList(array(
|
||||
array('Num' => 1)
|
||||
));
|
||||
|
||||
$list[] = array('Num' => 2);
|
||||
$this->assertEquals(2, count($list));
|
||||
$this->assertEquals(array('Num' => 2), $list->last());
|
||||
|
||||
$list[] = array('Num' => 3);
|
||||
$this->assertEquals(3, count($list));
|
||||
$this->assertEquals(array('Num' => 3), $list->last());
|
||||
}
|
||||
|
||||
public function testArrayAccessExists() {
|
||||
$list = new ArrayList(array(
|
||||
$one = new DataObject(array('Title' => 'one')),
|
||||
|
@ -27,8 +27,8 @@ class ValidationExceptionTest extends SapphireTest
|
||||
*/
|
||||
public function testCreateFromComplexValidationResult() {
|
||||
$result = new ValidationResult();
|
||||
$result->error('Invalid type');
|
||||
$result->error('Out of kiwis');
|
||||
$result->error('Invalid type')
|
||||
->error('Out of kiwis');
|
||||
$exception = new ValidationException($result);
|
||||
|
||||
$this->assertEquals(0, $exception->getCode());
|
||||
@ -71,8 +71,8 @@ class ValidationExceptionTest extends SapphireTest
|
||||
*/
|
||||
public function testCreateWithComplexValidationResultAndMessage() {
|
||||
$result = new ValidationResult();
|
||||
$result->error('A spork is not a knife');
|
||||
$result->error('A knife is not a back scratcher');
|
||||
$result->error('A spork is not a knife')
|
||||
->error('A knife is not a back scratcher');
|
||||
$exception = new ValidationException($result, 'An error has occurred', E_USER_WARNING);
|
||||
|
||||
$this->assertEquals(E_USER_WARNING, $exception->getCode());
|
||||
@ -81,4 +81,28 @@ class ValidationExceptionTest extends SapphireTest
|
||||
$this->assertEquals('A spork is not a knife; A knife is not a back scratcher',
|
||||
$exception->getResult()->message());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test combining validation results together
|
||||
*/
|
||||
public function testCombineResults(){
|
||||
$result = new ValidationResult();
|
||||
$anotherresult = new ValidationResult();
|
||||
$yetanotherresult = new ValidationResult();
|
||||
$anotherresult->error("Eat with your mouth closed", "EATING101");
|
||||
$yetanotherresult->error("You didn't wash your hands", "BECLEAN");
|
||||
|
||||
$this->assertTrue($result->valid());
|
||||
$this->assertFalse($anotherresult->valid());
|
||||
$this->assertFalse($yetanotherresult->valid());
|
||||
|
||||
$result->combineAnd($anotherresult)
|
||||
->combineAnd($yetanotherresult);
|
||||
$this->assertFalse($result->valid());
|
||||
$this->assertEquals(array(
|
||||
"EATING101" => "Eat with your mouth closed",
|
||||
"BECLEAN" => "You didn't wash your hands"
|
||||
),$result->messageList());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,9 @@ else {
|
||||
* This is the exception raised when failing to parse a template. Note that we don't currently do any static analysis,
|
||||
* so we can't know if the template will run, just if it's malformed. It also won't catch mistakes that still look
|
||||
* valid.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSTemplateParseException extends Exception {
|
||||
|
||||
@ -64,6 +67,9 @@ class SSTemplateParseException extends Exception {
|
||||
*
|
||||
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
|
||||
* N: eats white space including newlines (using in legacy _t support)
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSTemplateParser extends Parser implements TemplateParser {
|
||||
|
||||
|
@ -41,6 +41,9 @@ else {
|
||||
* This is the exception raised when failing to parse a template. Note that we don't currently do any static analysis,
|
||||
* so we can't know if the template will run, just if it's malformed. It also won't catch mistakes that still look
|
||||
* valid.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSTemplateParseException extends Exception {
|
||||
|
||||
@ -85,6 +88,9 @@ class SSTemplateParseException extends Exception {
|
||||
*
|
||||
* Angle Bracket: angle brackets "<" and ">" are used to eat whitespace between template elements
|
||||
* N: eats white space including newlines (using in legacy _t support)
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSTemplateParser extends Parser implements TemplateParser {
|
||||
|
||||
|
@ -18,7 +18,9 @@
|
||||
* We also keep the index of the current starting point for lookups. A lookup is a sequence of obj calls -
|
||||
* when in a loop or with tag the end result becomes the new scope, but for injections, we throw away the lookup
|
||||
* and revert back to the original scope once we've got the value we're after
|
||||
*
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSViewer_Scope {
|
||||
|
||||
@ -182,6 +184,13 @@ class SSViewer_Scope {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines an extra set of basic methods that can be used in templates
|
||||
* that are not defined on sub-classes of {@link ViewableData}.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSViewer_BasicIteratorSupport implements TemplateIteratorProvider {
|
||||
|
||||
protected $iteratorPos;
|
||||
@ -345,6 +354,9 @@ class SSViewer_BasicIteratorSupport implements TemplateIteratorProvider {
|
||||
* (like $FirstLast etc).
|
||||
*
|
||||
* It's separate from SSViewer_Scope to keep that fairly complex code as clean as possible.
|
||||
*
|
||||
* @package framework
|
||||
* @subpackage view
|
||||
*/
|
||||
class SSViewer_DataPresenter extends SSViewer_Scope {
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user