mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge 3 into master
# Conflicts: # CONTRIBUTING.md # admin/css/screen.css # admin/css/screen.css.map # admin/javascript/LeftAndMain.EditForm.js # admin/javascript/LeftAndMain.js # admin/scss/_forms.scss # dev/Debug.php # docs/en/05_Contributing/01_Code.md # forms/DropdownField.php # model/DataObject.php # model/Versioned.php # model/fieldtypes/DBLocale.php # tests/forms/gridfield/GridFieldExportButtonTest.yml # tests/model/MoneyTest.php # tests/model/MoneyTest.yml # tests/model/SQLQueryTest.php
This commit is contained in:
commit
574bc6038b
@ -4,24 +4,6 @@ Any open source product is only as good as the community behind it. You can part
|
|||||||
|
|
||||||
See our [high level overview](http://silverstripe.org/contributing-to-silverstripe) on silverstripe.org on how you can help out.
|
See our [high level overview](http://silverstripe.org/contributing-to-silverstripe) on silverstripe.org on how you can help out.
|
||||||
|
|
||||||
## Contributing to the correct version
|
## Contributing code
|
||||||
|
|
||||||
SilverStripe core and module releases (since the 3.1.8 release) follow the [Semantic Versioning](http://semver.org)
|
See [contributing code](docs/en/05_Contributing/01_Code.md)
|
||||||
(SemVar) specification for releases. Using this specification declares to the entire development community the severity
|
|
||||||
and intention of each release. It gives developers the ability to safely declare their dependencies and understand the
|
|
||||||
scope involved in each upgrade.
|
|
||||||
|
|
||||||
Each release is labeled in the format `$MAJOR`.`$MINOR`.`$PATCH`. For example, 3.1.8 or 3.2.0.
|
|
||||||
|
|
||||||
* `$MAJOR` version is incremented if any backwards incompatible changes are introduced to the public API.
|
|
||||||
* `$MINOR` version is incremented if new, backwards compatible **functionality** is introduced to the public API or
|
|
||||||
improvements are introduced within the private code.
|
|
||||||
* `$PATCH` version is incremented if only backwards compatible **bug fixes** are introduced. A bug fix is defined as
|
|
||||||
an internal change that fixes incorrect behavior.
|
|
||||||
|
|
||||||
Git Branches are setup for each `$MINOR` version (e.g. 3.1, 3.2). Each `$PATCH` release is a git tag off the `$MINOR`
|
|
||||||
branch. For example, 3.1.8 will be a git tag of 3.1.8.
|
|
||||||
|
|
||||||
When contributing code, be aware of the scope of your changes. If your change is backwards incompatible, raise your
|
|
||||||
change against the `master` branch. The master branch contains the next `$MAJOR` release. If the change is backwards
|
|
||||||
compatible raise it against the correct `$MINOR` branch.
|
|
@ -77,13 +77,35 @@ class ErrorControlChain {
|
|||||||
*/
|
*/
|
||||||
public function setSuppression($suppression) {
|
public function setSuppression($suppression) {
|
||||||
$this->suppression = (bool)$suppression;
|
$this->suppression = (bool)$suppression;
|
||||||
// Don't modify errors unless handling fatal errors, and if errors were
|
// If handling fatal errors, conditionally disable, or restore error display
|
||||||
// originally allowed to be displayed.
|
// Note: original value of display_errors could also evaluate to "off"
|
||||||
if ($this->handleFatalErrors && $this->originalDisplayErrors) {
|
if ($this->handleFatalErrors) {
|
||||||
ini_set('display_errors', !$suppression);
|
if($suppression) {
|
||||||
|
$this->setDisplayErrors(0);
|
||||||
|
} else {
|
||||||
|
$this->setDisplayErrors($this->originalDisplayErrors);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set display_errors
|
||||||
|
*
|
||||||
|
* @param mixed $errors
|
||||||
|
*/
|
||||||
|
protected function setDisplayErrors($errors) {
|
||||||
|
ini_set('display_errors', $errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get value of display_errors ini value
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function getDisplayErrors() {
|
||||||
|
return ini_get('display_errors');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add this callback to the chain of callbacks to call along with the state
|
* Add this callback to the chain of callbacks to call along with the state
|
||||||
* that $error must be in this point in the chain for the callback to be called
|
* that $error must be in this point in the chain for the callback to be called
|
||||||
@ -178,7 +200,7 @@ class ErrorControlChain {
|
|||||||
register_shutdown_function(array($this, 'handleFatalError'));
|
register_shutdown_function(array($this, 'handleFatalError'));
|
||||||
$this->handleFatalErrors = true;
|
$this->handleFatalErrors = true;
|
||||||
|
|
||||||
$this->originalDisplayErrors = ini_get('display_errors');
|
$this->originalDisplayErrors = $this->getDisplayErrors();
|
||||||
$this->setSuppression($this->suppression);
|
$this->setSuppression($this->suppression);
|
||||||
|
|
||||||
$this->step();
|
$this->step();
|
||||||
@ -202,7 +224,7 @@ class ErrorControlChain {
|
|||||||
else {
|
else {
|
||||||
// Now clean up
|
// Now clean up
|
||||||
$this->handleFatalErrors = false;
|
$this->handleFatalErrors = false;
|
||||||
ini_set('display_errors', $this->originalDisplayErrors);
|
$this->setDisplayErrors($this->originalDisplayErrors);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,3 +14,20 @@ was affected by these:
|
|||||||
* When FormFields are rendered, leading & trailing whitespace is now stripped. The resulting HTML for form fields is
|
* When FormFields are rendered, leading & trailing whitespace is now stripped. The resulting HTML for form fields is
|
||||||
the same for the default fields, but if you have a custom form field that is relying on trailing whitespace being
|
the same for the default fields, but if you have a custom form field that is relying on trailing whitespace being
|
||||||
outputted.
|
outputted.
|
||||||
|
* DataObject::isChanged() now defaults to only checking database fields. If you rely on this method
|
||||||
|
for checking changes to non-db field properties, use getChangedFields() instead.
|
||||||
|
|
||||||
|
### Error handling
|
||||||
|
|
||||||
|
Up until 3.4.0 error responses handled by SilverStripe have normally returned HTTP 200. The correct http response
|
||||||
|
code can be turned on by setting `Debug.friendly_error_httpcode` config to true. This option will be removed in
|
||||||
|
4.0 and fixed to always on.
|
||||||
|
|
||||||
|
|
||||||
|
:::yaml
|
||||||
|
---
|
||||||
|
Name: mydebug
|
||||||
|
---
|
||||||
|
Debug:
|
||||||
|
friendly_error_httpcode: true
|
||||||
|
|
||||||
|
@ -69,6 +69,39 @@ We ask for this so that the ownership in the license is clear and unambiguous, a
|
|||||||
The core team is then responsible for reviewing patches and deciding if they will make it into core. If
|
The core team is then responsible for reviewing patches and deciding if they will make it into core. If
|
||||||
there are any problems they will follow up with you, so please ensure they have a way to contact you!
|
there are any problems they will follow up with you, so please ensure they have a way to contact you!
|
||||||
|
|
||||||
|
|
||||||
|
### Picking the right version
|
||||||
|
|
||||||
|
SilverStripe core and module releases (since the 3.1.8 release) follow the [Semantic Versioning](http://semver.org)
|
||||||
|
(SemVer) specification for releases. Using this specification declares to the entire development community the severity
|
||||||
|
and intention of each release. It gives developers the ability to safely declare their dependencies and understand the
|
||||||
|
scope involved in each upgrade.
|
||||||
|
|
||||||
|
Each release is labeled in the format `$MAJOR`.`$MINOR`.`$PATCH`. For example, 3.1.8 or 3.2.0.
|
||||||
|
|
||||||
|
* `$MAJOR` version is incremented if any backwards incompatible changes are introduced to the public API.
|
||||||
|
* `$MINOR` version is incremented if new, backwards compatible **functionality** is introduced to the public API or
|
||||||
|
improvements are introduced within the private code.
|
||||||
|
* `$PATCH` version is incremented if only backwards compatible **bug fixes** are introduced. A bug fix is defined as
|
||||||
|
an internal change that fixes incorrect behavior.
|
||||||
|
|
||||||
|
**Public API** refers to any aspect of the system that has been designed to be used by SilverStripe modules & site developers. In SilverStripe 3, because we haven't been clear, in principle we have to treat every public or protected method as *potentially* part of the public API, but sometimes it comes to a judgement call about how likely it is that a given method will have been used in a particular way. If we were strict about never changing publicly exposed behaviour, it would be difficult to fix any bug whatsoever, which isn't in the interests of our user community.
|
||||||
|
|
||||||
|
In future major releases of SilverStripe, we will endeavour to be more explicit about documenting the public API.
|
||||||
|
|
||||||
|
**Contributing bug fixes**
|
||||||
|
|
||||||
|
Bug fixes should be raised against the most recent MINOR release branch. For example, If your project is on 3.3.1 and 3.4.0 is released, please raise your bugfix against the `3.4` branch. Older MINOR release branches are primarily intended for critical bugfixes and security issues.
|
||||||
|
|
||||||
|
**Contributing features**
|
||||||
|
|
||||||
|
When contributing a backwards compatible change, raise it against the same MAJOR branch as your project. For example, if your project is on 3.3.1, raise it against the `3` branch. It will be included in the next MINOR release, e.g. 3.4.0. And then when it is released, you should upgrade your project to use it. As it is a MINOR change, it shouldn't break anything, and be a relatively painless upgrade.
|
||||||
|
|
||||||
|
**Contributing backwards-incompatible public API changes, and removing or radically changing existing feautres**
|
||||||
|
|
||||||
|
When contributing a backwards incompatible change, you must raise it against the `master` branch.
|
||||||
|
|
||||||
|
|
||||||
### The Pull Request Process
|
### The Pull Request Process
|
||||||
|
|
||||||
Once your pull request is issued, it's not the end of the road. A [core committer](/contributing/core_committers/) will most likely have some questions for you and may ask you to make some changes depending on discussions you have.
|
Once your pull request is issued, it's not the end of the road. A [core committer](/contributing/core_committers/) will most likely have some questions for you and may ask you to make some changes depending on discussions you have.
|
||||||
@ -123,10 +156,7 @@ If you're familiar with it, here's the short version of what you need to know. O
|
|||||||
|
|
||||||
* **Squash your commits, so that each commit addresses a single issue.** After you rebase your work on top of the upstream master, you can squash multiple commits into one. Say, for instance, you've got three commits in related to Issue #100. Squash all three into one with the message "Description of the issue here (fixes #100)" We won't accept pull requests for multiple commits related to a single issue; it's up to you to squash and clean your commit tree. (Remember, if you squash commits you've already pushed to GitHub, you won't be able to push that same branch again. Create a new local branch, squash, and push the new squashed branch.)
|
* **Squash your commits, so that each commit addresses a single issue.** After you rebase your work on top of the upstream master, you can squash multiple commits into one. Say, for instance, you've got three commits in related to Issue #100. Squash all three into one with the message "Description of the issue here (fixes #100)" We won't accept pull requests for multiple commits related to a single issue; it's up to you to squash and clean your commit tree. (Remember, if you squash commits you've already pushed to GitHub, you won't be able to push that same branch again. Create a new local branch, squash, and push the new squashed branch.)
|
||||||
|
|
||||||
* **Choose the correct branch**: Assume the current release is 3.0.3, and 3.1.0 is in beta state.
|
* **Choose the correct branch**: see [Picking the right version](#picking-the-right-version).
|
||||||
Most pull requests should go against the `3.1` *pre-release branch*, only critical bugfixes
|
|
||||||
against the `3.0` *release branch*. If you're changing an API or introducing a major feature,
|
|
||||||
the pull request should go against `master` (read more about our [release process](03_Release_Process.md)). Branches are periodically merged "upwards" (3.0 into 3.1, 3.1 into master).
|
|
||||||
|
|
||||||
### Editing files directly on GitHub.com
|
### Editing files directly on GitHub.com
|
||||||
|
|
||||||
|
@ -54,13 +54,28 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
*/
|
*/
|
||||||
protected $showOnClick = false;
|
protected $showOnClick = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the existing password should be entered first
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $requireExistingPassword = false;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A place to temporarly store the confirm password value
|
* A place to temporarily store the confirm password value
|
||||||
|
*
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $confirmValue;
|
protected $confirmValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store value of "Current Password" field
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $currentPasswordValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Title for the link that triggers the visibility of password fields.
|
* Title for the link that triggers the visibility of password fields.
|
||||||
*
|
*
|
||||||
@ -107,6 +122,7 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
|
|
||||||
// disable auto complete
|
// disable auto complete
|
||||||
foreach($this->children as $child) {
|
foreach($this->children as $child) {
|
||||||
|
/** @var FormField $child */
|
||||||
$child->setAttribute('autocomplete', 'off');
|
$child->setAttribute('autocomplete', 'off');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +131,7 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
// we have labels for the subfields
|
// we have labels for the subfields
|
||||||
$title = false;
|
$title = false;
|
||||||
|
|
||||||
parent::__construct($name, $title, null, $form);
|
parent::__construct($name, $title);
|
||||||
$this->setValue($value);
|
$this->setValue($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +165,7 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->children as $field) {
|
foreach($this->children as $field) {
|
||||||
|
/** @var FormField $field */
|
||||||
$field->setDisabled($this->isDisabled());
|
$field->setDisabled($this->isDisabled());
|
||||||
$field->setReadonly($this->isReadonly());
|
$field->setReadonly($this->isReadonly());
|
||||||
|
|
||||||
@ -222,6 +239,7 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
*/
|
*/
|
||||||
public function setRightTitle($title) {
|
public function setRightTitle($title) {
|
||||||
foreach($this->children as $field) {
|
foreach($this->children as $field) {
|
||||||
|
/** @var FormField $field */
|
||||||
$field->setRightTitle($title);
|
$field->setRightTitle($title);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,15 +247,20 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $titles 2 entry array with the customized title for each
|
* Set child field titles. Titles in order should be:
|
||||||
* of the 2 children.
|
* - "Current Password" (if getRequireExistingPassword() is set)
|
||||||
|
* - "Password"
|
||||||
|
* - "Confirm Password"
|
||||||
*
|
*
|
||||||
* @return ConfirmedPasswordField
|
* @param array $titles List of child titles
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setChildrenTitles($titles) {
|
public function setChildrenTitles($titles) {
|
||||||
if(is_array($titles) && count($titles) == 2) {
|
$expectedChildren = $this->getRequireExistingPassword() ? 3 : 2;
|
||||||
|
if(is_array($titles) && count($titles) == $expectedChildren) {
|
||||||
foreach($this->children as $field) {
|
foreach($this->children as $field) {
|
||||||
if(isset($titles[0])) {
|
if(isset($titles[0])) {
|
||||||
|
/** @var FormField $field */
|
||||||
$field->setTitle($titles[0]);
|
$field->setTitle($titles[0]);
|
||||||
|
|
||||||
array_shift($titles);
|
array_shift($titles);
|
||||||
@ -253,8 +276,8 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
* to handle both cases.
|
* to handle both cases.
|
||||||
*
|
*
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
*
|
* @param mixed $data
|
||||||
* @return ConfirmedPasswordField
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setValue($value, $data = null) {
|
public function setValue($value, $data = null) {
|
||||||
// If $data is a DataObject, don't use the value, since it's a hashed value
|
// If $data is a DataObject, don't use the value, since it's a hashed value
|
||||||
@ -266,6 +289,9 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
if(is_array($value)) {
|
if(is_array($value)) {
|
||||||
$this->value = $value['_Password'];
|
$this->value = $value['_Password'];
|
||||||
$this->confirmValue = $value['_ConfirmPassword'];
|
$this->confirmValue = $value['_ConfirmPassword'];
|
||||||
|
$this->currentPasswordValue = ($this->getRequireExistingPassword() && isset($value['_CurrentPassword']))
|
||||||
|
? $value['_CurrentPassword']
|
||||||
|
: null;
|
||||||
|
|
||||||
if($this->showOnClick && isset($value['_PasswordFieldVisible'])) {
|
if($this->showOnClick && isset($value['_PasswordFieldVisible'])) {
|
||||||
$this->children->fieldByName($this->getName() . '[_PasswordFieldVisible]')
|
$this->children->fieldByName($this->getName() . '[_PasswordFieldVisible]')
|
||||||
@ -294,6 +320,7 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
* Update the names of the child fields when updating name of field.
|
* Update the names of the child fields when updating name of field.
|
||||||
*
|
*
|
||||||
* @param string $name new name to give to the field.
|
* @param string $name new name to give to the field.
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setName($name) {
|
public function setName($name) {
|
||||||
$this->children->fieldByName($this->getName() . '[_Password]')
|
$this->children->fieldByName($this->getName() . '[_Password]')
|
||||||
@ -342,8 +369,7 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
$validator->validationError(
|
$validator->validationError(
|
||||||
$name,
|
$name,
|
||||||
_t('Form.VALIDATIONPASSWORDSDONTMATCH',"Passwords don't match"),
|
_t('Form.VALIDATIONPASSWORDSDONTMATCH',"Passwords don't match"),
|
||||||
"validation",
|
"validation"
|
||||||
false
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -355,8 +381,7 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
$validator->validationError(
|
$validator->validationError(
|
||||||
$name,
|
$name,
|
||||||
_t('Form.VALIDATIONPASSWORDSNOTEMPTY', "Passwords can't be empty"),
|
_t('Form.VALIDATIONPASSWORDSNOTEMPTY', "Passwords can't be empty"),
|
||||||
"validation",
|
"validation"
|
||||||
false
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -365,6 +390,8 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
|
|
||||||
// lengths
|
// lengths
|
||||||
if(($this->minLength || $this->maxLength)) {
|
if(($this->minLength || $this->maxLength)) {
|
||||||
|
$errorMsg = null;
|
||||||
|
$limit = null;
|
||||||
if($this->minLength && $this->maxLength) {
|
if($this->minLength && $this->maxLength) {
|
||||||
$limit = "{{$this->minLength},{$this->maxLength}}";
|
$limit = "{{$this->minLength},{$this->maxLength}}";
|
||||||
$errorMsg = _t(
|
$errorMsg = _t(
|
||||||
@ -392,8 +419,7 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
$validator->validationError(
|
$validator->validationError(
|
||||||
$name,
|
$name,
|
||||||
$errorMsg,
|
$errorMsg,
|
||||||
"validation",
|
"validation"
|
||||||
false
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -404,14 +430,56 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
$name,
|
$name,
|
||||||
_t('Form.VALIDATIONSTRONGPASSWORD',
|
_t('Form.VALIDATIONSTRONGPASSWORD',
|
||||||
"Passwords must have at least one digit and one alphanumeric character"),
|
"Passwords must have at least one digit and one alphanumeric character"),
|
||||||
"validation",
|
"validation"
|
||||||
false
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if current password is valid
|
||||||
|
if(!empty($value) && $this->getRequireExistingPassword()) {
|
||||||
|
if(!$this->currentPasswordValue) {
|
||||||
|
$validator->validationError(
|
||||||
|
$name,
|
||||||
|
_t(
|
||||||
|
'ConfirmedPasswordField.CURRENT_PASSWORD_MISSING',
|
||||||
|
"You must enter your current password."
|
||||||
|
),
|
||||||
|
"validation"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check this password is valid for the current user
|
||||||
|
$member = Member::currentUser();
|
||||||
|
if(!$member) {
|
||||||
|
$validator->validationError(
|
||||||
|
$name,
|
||||||
|
_t(
|
||||||
|
'ConfirmedPasswordField.LOGGED_IN_ERROR',
|
||||||
|
"You must be logged in to change your password."
|
||||||
|
),
|
||||||
|
"validation"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// With a valid user and password, check the password is correct
|
||||||
|
$checkResult = $member->checkPassword($this->currentPasswordValue);
|
||||||
|
if(!$checkResult->valid()) {
|
||||||
|
$validator->validationError(
|
||||||
|
$name,
|
||||||
|
_t(
|
||||||
|
'ConfirmedPasswordField.CURRENT_PASSWORD_ERROR',
|
||||||
|
"The current password you have entered is not correct."
|
||||||
|
),
|
||||||
|
"validation"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,4 +512,36 @@ class ConfirmedPasswordField extends FormField {
|
|||||||
|
|
||||||
return $field;
|
return $field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if existing password is required
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function getRequireExistingPassword() {
|
||||||
|
return $this->requireExistingPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if the existing password should be required
|
||||||
|
*
|
||||||
|
* @param bool $show Flag to show or hide this field
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setRequireExistingPassword($show) {
|
||||||
|
// Don't modify if already added / removed
|
||||||
|
if((bool)$show === $this->requireExistingPassword) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
$this->requireExistingPassword = $show;
|
||||||
|
$name = $this->getName();
|
||||||
|
$currentName = "{$name}[_CurrentPassword]";
|
||||||
|
if ($show) {
|
||||||
|
$confirmField = PasswordField::create($currentName, _t('Member.CURRENT_PASSWORD', 'Current Password'));
|
||||||
|
$this->children->unshift($confirmField);
|
||||||
|
} else {
|
||||||
|
$this->children->removeByName($currentName, true);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,6 +184,11 @@ abstract class SelectField extends FormField {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Safety check against casting arrays as strings in PHP>5.4
|
||||||
|
if(is_array($dataValue) || is_array($userValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// For non-falsey values do loose comparison
|
// For non-falsey values do loose comparison
|
||||||
if($dataValue) {
|
if($dataValue) {
|
||||||
return $dataValue == $userValue;
|
return $dataValue == $userValue;
|
||||||
|
@ -149,7 +149,7 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP
|
|||||||
} else {
|
} else {
|
||||||
$value = $gridField->getDataFieldValue($item, $columnSource);
|
$value = $gridField->getDataFieldValue($item, $columnSource);
|
||||||
|
|
||||||
if(!$value) {
|
if($value === null) {
|
||||||
$value = $gridField->getDataFieldValue($item, $columnHeader);
|
$value = $gridField->getDataFieldValue($item, $columnHeader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1038,7 +1038,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
* Doesn't write to the database. Only sets fields as changed
|
* Doesn't write to the database. Only sets fields as changed
|
||||||
* if they are not already marked as changed.
|
* if they are not already marked as changed.
|
||||||
*
|
*
|
||||||
* @return DataObject $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function forceChange() {
|
public function forceChange() {
|
||||||
// Ensure lazy fields loaded
|
// Ensure lazy fields loaded
|
||||||
@ -1233,22 +1233,16 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
* @param bool $forceChanges If set to true, force all fields to be treated as changed
|
* @param bool $forceChanges If set to true, force all fields to be treated as changed
|
||||||
* @return bool True if any changes are detected
|
* @return bool True if any changes are detected
|
||||||
*/
|
*/
|
||||||
protected function updateChanges($forceChanges = false) {
|
protected function updateChanges($forceChanges = false)
|
||||||
// Update the changed array with references to changed obj-fields
|
{
|
||||||
foreach($this->record as $field => $value) {
|
if($forceChanges) {
|
||||||
// Only mark ID as changed if $forceChanges
|
// Force changes, but only for loaded fields
|
||||||
if($field === 'ID' && !$forceChanges) continue;
|
foreach($this->record as $field => $value) {
|
||||||
// Determine if this field should be forced, or can mark itself, changed
|
$this->changed[$field] = static::CHANGE_VALUE;
|
||||||
if($forceChanges
|
|
||||||
|| !$this->isInDB()
|
|
||||||
|| (is_object($value) && method_exists($value, 'isChanged') && $value->isChanged())
|
|
||||||
) {
|
|
||||||
$this->changed[$field] = self::CHANGE_VALUE;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return $this->isChanged();
|
||||||
// Check changes exist, abort if there are no changes
|
|
||||||
return $this->changed && (bool)array_filter($this->changed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1383,7 +1377,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
$isNewRecord = !$this->isInDB() || $forceInsert;
|
$isNewRecord = !$this->isInDB() || $forceInsert;
|
||||||
|
|
||||||
// Check changes exist, abort if there are none
|
// Check changes exist, abort if there are none
|
||||||
$hasChanges = $this->updateChanges($forceInsert);
|
$hasChanges = $this->updateChanges($isNewRecord);
|
||||||
if($hasChanges || $forceWrite || $isNewRecord) {
|
if($hasChanges || $forceWrite || $isNewRecord) {
|
||||||
// New records have their insert into the base data table done first, so that they can pass the
|
// New records have their insert into the base data table done first, so that they can pass the
|
||||||
// generated primary key on to the rest of the manipulation
|
// generated primary key on to the rest of the manipulation
|
||||||
@ -2476,9 +2470,14 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
* Loads all the stub fields that an initial lazy load didn't load fully.
|
* Loads all the stub fields that an initial lazy load didn't load fully.
|
||||||
*
|
*
|
||||||
* @param string $tableClass Base table to load the values from. Others are joined as required.
|
* @param string $tableClass Base table to load the values from. Others are joined as required.
|
||||||
* Not specifying a tableClass will load all lazy fields from all tables.
|
* Not specifying a tableClass will load all lazy fields from all tables.
|
||||||
|
* @return bool Flag if lazy loading succeeded
|
||||||
*/
|
*/
|
||||||
protected function loadLazyFields($tableClass = null) {
|
protected function loadLazyFields($tableClass = null) {
|
||||||
|
if(!$this->isInDB() || !is_numeric($this->ID)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$tableClass) {
|
if (!$tableClass) {
|
||||||
$loaded = array();
|
$loaded = array();
|
||||||
|
|
||||||
@ -2489,7 +2488,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$dataQuery = new DataQuery($tableClass);
|
$dataQuery = new DataQuery($tableClass);
|
||||||
@ -2501,11 +2500,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TableField sets the record ID to "new" on new row data, so don't try doing anything in that case
|
|
||||||
if(!is_numeric($this->record['ID'])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limit query to the current record, unless it has the Versioned extension,
|
// Limit query to the current record, unless it has the Versioned extension,
|
||||||
// in which case it requires special handling through augmentLoadLazyFields()
|
// in which case it requires special handling through augmentLoadLazyFields()
|
||||||
$baseTable = ClassInfo::baseDataClass($this);
|
$baseTable = ClassInfo::baseDataClass($this);
|
||||||
@ -2551,6 +2545,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2624,7 +2619,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function isChanged($fieldName = null, $changeLevel = self::CHANGE_STRICT) {
|
public function isChanged($fieldName = null, $changeLevel = self::CHANGE_STRICT) {
|
||||||
$fields = $fieldName ? array($fieldName) : false;
|
$fields = $fieldName ? array($fieldName) : true;
|
||||||
$changed = $this->getChangedFields($fields, $changeLevel);
|
$changed = $this->getChangedFields($fields, $changeLevel);
|
||||||
if(!isset($fieldName)) {
|
if(!isset($fieldName)) {
|
||||||
return !empty($changed);
|
return !empty($changed);
|
||||||
|
@ -14,7 +14,7 @@ use i18n;
|
|||||||
*/
|
*/
|
||||||
class DBLocale extends DBVarchar {
|
class DBLocale extends DBVarchar {
|
||||||
|
|
||||||
public function __construct($name, $size = 16) {
|
public function __construct($name = null, $size = 16) {
|
||||||
parent::__construct($name, $size);
|
parent::__construct($name, $size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
/**
|
||||||
* DataObjects that use the Hierarchy extension can be be organised as a hierarchy, with children and parents.
|
* DataObjects that use the Hierarchy extension can be be organised as a hierarchy, with children and parents. The most
|
||||||
* The most obvious example of this is SiteTree.
|
* obvious example of this is SiteTree.
|
||||||
|
*
|
||||||
* @package framework
|
* @package framework
|
||||||
* @subpackage model
|
* @subpackage model
|
||||||
|
*
|
||||||
|
* @property int ParentID
|
||||||
|
* @property DataObject owner
|
||||||
|
* @method DataObject Parent
|
||||||
*/
|
*/
|
||||||
class Hierarchy extends DataExtension {
|
class Hierarchy extends DataExtension {
|
||||||
|
|
||||||
@ -11,30 +16,28 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
protected $markingFilter;
|
protected $markingFilter;
|
||||||
|
|
||||||
/**
|
/** @var int */
|
||||||
* @var Int
|
|
||||||
*/
|
|
||||||
protected $_cache_numChildren;
|
protected $_cache_numChildren;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* The lower bounds for the amount of nodes to mark. If set, the logic will expand nodes until it reaches at least
|
||||||
|
* this number, and then stops. Root nodes will always show regardless of this settting. Further nodes can be
|
||||||
|
* lazy-loaded via ajax. This isn't a hard limit. Example: On a value of 10, with 20 root nodes, each having 30
|
||||||
|
* children, the actual node count will be 50 (all root nodes plus first expanded child).
|
||||||
|
*
|
||||||
* @config
|
* @config
|
||||||
* @var integer The lower bounds for the amount of nodes to mark. If set, the logic will expand
|
* @var int
|
||||||
* nodes until it reaches at least this number, and then stops. Root nodes will always
|
|
||||||
* show regardless of this settting. Further nodes can be lazy-loaded via ajax.
|
|
||||||
* This isn't a hard limit. Example: On a value of 10, with 20 root nodes, each having
|
|
||||||
* 30 children, the actual node count will be 50 (all root nodes plus first expanded child).
|
|
||||||
*/
|
*/
|
||||||
private static $node_threshold_total = 50;
|
private static $node_threshold_total = 50;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Limit on the maximum children a specific node can display. Serves as a hard limit to avoid exceeding available
|
||||||
|
* server resources in generating the tree, and browser resources in rendering it. Nodes with children exceeding
|
||||||
|
* this value typically won't display any children, although this is configurable through the $nodeCountCallback
|
||||||
|
* parameter in {@link getChildrenAsUL()}. "Root" nodes will always show all children, regardless of this setting.
|
||||||
|
*
|
||||||
* @config
|
* @config
|
||||||
* @var integer Limit on the maximum children a specific node can display.
|
* @var int
|
||||||
* Serves as a hard limit to avoid exceeding available server resources
|
|
||||||
* in generating the tree, and browser resources in rendering it.
|
|
||||||
* Nodes with children exceeding this value typically won't display
|
|
||||||
* any children, although this is configurable through the $nodeCountCallback
|
|
||||||
* parameter in {@link getChildrenAsUL()}. "Root" nodes will always show
|
|
||||||
* all children, regardless of this setting.
|
|
||||||
*/
|
*/
|
||||||
private static $node_threshold_leaf = 250;
|
private static $node_threshold_leaf = 250;
|
||||||
|
|
||||||
@ -46,6 +49,8 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate the owner object - check for existence of infinite loops.
|
* Validate the owner object - check for existence of infinite loops.
|
||||||
|
*
|
||||||
|
* @param ValidationResult $validationResult
|
||||||
*/
|
*/
|
||||||
public function validate(ValidationResult $validationResult) {
|
public function validate(ValidationResult $validationResult) {
|
||||||
// The object is new, won't be looping.
|
// The object is new, won't be looping.
|
||||||
@ -78,19 +83,21 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the children of this DataObject as an XHTML UL. This will be called recursively on each child,
|
* Returns the children of this DataObject as an XHTML UL. This will be called recursively on each child, so if they
|
||||||
* so if they have children they will be displayed as a UL inside a LI.
|
* have children they will be displayed as a UL inside a LI.
|
||||||
* @param string $attributes Attributes to add to the UL.
|
*
|
||||||
* @param string|callable $titleEval PHP code to evaluate to start each child - this should include '<li>'
|
* @param string $attributes Attributes to add to the UL
|
||||||
* @param string $extraArg Extra arguments that will be passed on to children, for if they overload this function.
|
* @param string|callable $titleEval PHP code to evaluate to start each child - this should include '<li>'
|
||||||
* @param boolean $limitToMarked Display only marked children.
|
* @param string $extraArg Extra arguments that will be passed on to children, for if they
|
||||||
* @param string $childrenMethod The name of the method used to get children from each object
|
* overload this function
|
||||||
* @param boolean $rootCall Set to true for this first call, and then to false for calls inside the recursion. You
|
* @param bool $limitToMarked Display only marked children
|
||||||
* should not change this.
|
* @param string $childrenMethod The name of the method used to get children from each object
|
||||||
* @param int $nodeCountThreshold See {@link self::$node_threshold_total}
|
* @param bool $rootCall Set to true for this first call, and then to false for calls inside
|
||||||
* @param callable $nodeCountCallback Called with the node count, which gives the callback an opportunity
|
* the recursion. You should not change this.
|
||||||
* to intercept the query. Useful e.g. to avoid excessive children listings
|
* @param int $nodeCountThreshold See {@link self::$node_threshold_total}
|
||||||
* (Arguments: $parent, $numChildren)
|
* @param callable $nodeCountCallback Called with the node count, which gives the callback an opportunity to
|
||||||
|
* intercept the query. Useful e.g. to avoid excessive children listings
|
||||||
|
* (Arguments: $parent, $numChildren)
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
@ -175,11 +182,12 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark a segment of the tree, by calling mark().
|
* Mark a segment of the tree, by calling mark().
|
||||||
* The method performs a breadth-first traversal until the number of nodes is more than minCount.
|
|
||||||
* This is used to get a limited number of tree nodes to show in the CMS initially.
|
|
||||||
*
|
*
|
||||||
* This method returns the number of nodes marked. After this method is called other methods
|
* The method performs a breadth-first traversal until the number of nodes is more than minCount. This is used to
|
||||||
* can check isExpanded() and isMarked() on individual nodes.
|
* get a limited number of tree nodes to show in the CMS initially.
|
||||||
|
*
|
||||||
|
* This method returns the number of nodes marked. After this method is called other methods can check
|
||||||
|
* {@link isExpanded()} and {@link isMarked()} on individual nodes.
|
||||||
*
|
*
|
||||||
* @param int $nodeCountThreshold See {@link getChildrenAsUL()}
|
* @param int $nodeCountThreshold See {@link getChildrenAsUL()}
|
||||||
* @return int The actual number of nodes marked.
|
* @return int The actual number of nodes marked.
|
||||||
@ -205,9 +213,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter the marking to only those object with $node->$parameterName = $parameterValue
|
* Filter the marking to only those object with $node->$parameterName == $parameterValue
|
||||||
* @param string $parameterName The parameter on each node to check when marking.
|
*
|
||||||
* @param mixed $parameterValue The value the parameter must be to be marked.
|
* @param string $parameterName The parameter on each node to check when marking.
|
||||||
|
* @param mixed $parameterValue The value the parameter must be to be marked.
|
||||||
*/
|
*/
|
||||||
public function setMarkingFilter($parameterName, $parameterValue) {
|
public function setMarkingFilter($parameterName, $parameterValue) {
|
||||||
$this->markingFilter = array(
|
$this->markingFilter = array(
|
||||||
@ -217,9 +226,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter the marking to only those where the function returns true.
|
* Filter the marking to only those where the function returns true. The node in question will be passed to the
|
||||||
* The node in question will be passed to the function.
|
* function.
|
||||||
* @param string $funcName The function name.
|
*
|
||||||
|
* @param string $funcName The name of the function to call
|
||||||
*/
|
*/
|
||||||
public function setMarkingFilterFunction($funcName) {
|
public function setMarkingFilterFunction($funcName) {
|
||||||
$this->markingFilter = array(
|
$this->markingFilter = array(
|
||||||
@ -229,8 +239,9 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the marking filter matches on the given node.
|
* Returns true if the marking filter matches on the given node.
|
||||||
* @param DataObject $node Node to check.
|
*
|
||||||
* @return boolean
|
* @param DataObject $node Node to check
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function markingFilterMatches($node) {
|
public function markingFilterMatches($node) {
|
||||||
if(!$this->markingFilter) {
|
if(!$this->markingFilter) {
|
||||||
@ -257,7 +268,11 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark all children of the given node that match the marking filter.
|
* Mark all children of the given node that match the marking filter.
|
||||||
* @param DataObject $node Parent node.
|
*
|
||||||
|
* @param DataObject $node Parent node
|
||||||
|
* @param mixed $context
|
||||||
|
* @param string $childrenMethod The name of the instance method to call to get the object's list of children
|
||||||
|
* @param string $numChildrenMethod The name of the instance method to call to count the object's children
|
||||||
* @return DataList
|
* @return DataList
|
||||||
*/
|
*/
|
||||||
public function markChildren($node, $context = null, $childrenMethod = "AllChildrenIncludingDeleted",
|
public function markChildren($node, $context = null, $childrenMethod = "AllChildrenIncludingDeleted",
|
||||||
@ -288,8 +303,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure marked nodes that have children are also marked expanded.
|
* Ensure marked nodes that have children are also marked expanded. Call this after marking but before iterating
|
||||||
* Call this after marking but before iterating over the tree.
|
* over the tree.
|
||||||
|
*
|
||||||
|
* @param string $numChildrenMethod The name of the instance method to call to count the object's children
|
||||||
*/
|
*/
|
||||||
protected function markingFinished($numChildrenMethod = "numChildren") {
|
protected function markingFinished($numChildrenMethod = "numChildren") {
|
||||||
// Mark childless nodes as expanded.
|
// Mark childless nodes as expanded.
|
||||||
@ -303,9 +320,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return CSS classes of 'unexpanded', 'closed', both, or neither, as well as a
|
* Return CSS classes of 'unexpanded', 'closed', both, or neither, as well as a 'jstree-*' state depending on the
|
||||||
* 'jstree-*' state depending on the marking of this DataObject.
|
* marking of this DataObject.
|
||||||
*
|
*
|
||||||
|
* @param string $numChildrenMethod The name of the instance method to call to count the object's children
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function markingClasses($numChildrenMethod="numChildren") {
|
public function markingClasses($numChildrenMethod="numChildren") {
|
||||||
@ -327,8 +345,10 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the children of the DataObject with the given ID.
|
* Mark the children of the DataObject with the given ID.
|
||||||
* @param int $id ID of parent node.
|
*
|
||||||
* @param boolean $open If this is true, mark the parent node as opened.
|
* @param int $id ID of parent node
|
||||||
|
* @param bool $open If this is true, mark the parent node as opened
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function markById($id, $open = false) {
|
public function markById($id, $open = false) {
|
||||||
if(isset($this->markedNodes[$id])) {
|
if(isset($this->markedNodes[$id])) {
|
||||||
@ -344,6 +364,7 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose the given object in the tree, by marking this page and all it ancestors.
|
* Expose the given object in the tree, by marking this page and all it ancestors.
|
||||||
|
*
|
||||||
* @param DataObject $childObj
|
* @param DataObject $childObj
|
||||||
*/
|
*/
|
||||||
public function markToExpose($childObj) {
|
public function markToExpose($childObj) {
|
||||||
@ -356,7 +377,9 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the IDs of all the marked nodes
|
* Return the IDs of all the marked nodes.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function markedNodeIDs() {
|
public function markedNodeIDs() {
|
||||||
return array_keys($this->markedNodes);
|
return array_keys($this->markedNodes);
|
||||||
@ -364,7 +387,8 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return an array of this page and its ancestors, ordered item -> root.
|
* Return an array of this page and its ancestors, ordered item -> root.
|
||||||
* @return array
|
*
|
||||||
|
* @return SiteTree[]
|
||||||
*/
|
*/
|
||||||
public function parentStack() {
|
public function parentStack() {
|
||||||
$p = $this->owner;
|
$p = $this->owner;
|
||||||
@ -378,20 +402,20 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this DataObject is marked.
|
* Cache of DataObjects' marked statuses: [ClassName][ID] = bool
|
||||||
* @var boolean
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $marked = array();
|
protected static $marked = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this DataObject is expanded.
|
* Cache of DataObjects' expanded statuses: [ClassName][ID] = bool
|
||||||
* @var boolean
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $expanded = array();
|
protected static $expanded = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this DataObject is opened.
|
* Cache of DataObjects' opened statuses: [ClassName][ID] = bool
|
||||||
* @var boolean
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $treeOpened = array();
|
protected static $treeOpened = array();
|
||||||
|
|
||||||
@ -430,7 +454,8 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this DataObject is marked.
|
* Check if this DataObject is marked.
|
||||||
* @return boolean
|
*
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isMarked() {
|
public function isMarked() {
|
||||||
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
||||||
@ -440,7 +465,8 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this DataObject is expanded.
|
* Check if this DataObject is expanded.
|
||||||
* @return boolean
|
*
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isExpanded() {
|
public function isExpanded() {
|
||||||
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
||||||
@ -450,6 +476,8 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this DataObject's tree is opened.
|
* Check if this DataObject's tree is opened.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isTreeOpened() {
|
public function isTreeOpened() {
|
||||||
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
$baseClass = ClassInfo::baseDataClass($this->owner->class);
|
||||||
@ -459,7 +487,8 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of this DataObject's and all it's descendants IDs.
|
* Get a list of this DataObject's and all it's descendants IDs.
|
||||||
* @return int
|
*
|
||||||
|
* @return int[]
|
||||||
*/
|
*/
|
||||||
public function getDescendantIDList() {
|
public function getDescendantIDList() {
|
||||||
$idList = array();
|
$idList = array();
|
||||||
@ -468,8 +497,9 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a list of this DataObject's and all it's descendants ID, and put it in $idList.
|
* Get a list of this DataObject's and all it's descendants ID, and put them in $idList.
|
||||||
* @var array $idList Array to put results in.
|
*
|
||||||
|
* @param array $idList Array to put results in.
|
||||||
*/
|
*/
|
||||||
public function loadDescendantIDListInto(&$idList) {
|
public function loadDescendantIDListInto(&$idList) {
|
||||||
if($children = $this->AllChildren()) {
|
if($children = $this->AllChildren()) {
|
||||||
@ -488,7 +518,8 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the children for this DataObject.
|
* Get the children for this DataObject.
|
||||||
* @return ArrayList
|
*
|
||||||
|
* @return DataList
|
||||||
*/
|
*/
|
||||||
public function Children() {
|
public function Children() {
|
||||||
if(!(isset($this->_cache_children) && $this->_cache_children)) {
|
if(!(isset($this->_cache_children) && $this->_cache_children)) {
|
||||||
@ -506,7 +537,8 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all children, including those 'not in menus'.
|
* Return all children, including those 'not in menus'.
|
||||||
* @return SS_List
|
*
|
||||||
|
* @return DataList
|
||||||
*/
|
*/
|
||||||
public function AllChildren() {
|
public function AllChildren() {
|
||||||
return $this->owner->stageChildren(true);
|
return $this->owner->stageChildren(true);
|
||||||
@ -514,11 +546,13 @@ class Hierarchy extends DataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all children, including those that have been deleted but are still in live.
|
* Return all children, including those that have been deleted but are still in live.
|
||||||
* Deleted children will be marked as "DeletedFromStage"
|
* - Deleted children will be marked as "DeletedFromStage"
|
||||||
* Added children will be marked as "AddedToStage"
|
* - Added children will be marked as "AddedToStage"
|
||||||
* Modified children will be marked as "ModifiedOnStage"
|
* - Modified children will be marked as "ModifiedOnStage"
|
||||||
* Everything else has "SameOnStage" set, as an indicator that this information has been looked up.
|
* - Everything else has "SameOnStage" set, as an indicator that this information has been looked up.
|
||||||
* @return SS_List
|
*
|
||||||
|
* @param mixed $context
|
||||||
|
* @return ArrayList
|
||||||
*/
|
*/
|
||||||
public function AllChildrenIncludingDeleted($context = null) {
|
public function AllChildrenIncludingDeleted($context = null) {
|
||||||
return $this->doAllChildrenIncludingDeleted($context);
|
return $this->doAllChildrenIncludingDeleted($context);
|
||||||
@ -527,8 +561,8 @@ class Hierarchy extends DataExtension {
|
|||||||
/**
|
/**
|
||||||
* @see AllChildrenIncludingDeleted
|
* @see AllChildrenIncludingDeleted
|
||||||
*
|
*
|
||||||
* @param unknown_type $context
|
* @param mixed $context
|
||||||
* @return SS_List
|
* @return ArrayList
|
||||||
*/
|
*/
|
||||||
public function doAllChildrenIncludingDeleted($context = null) {
|
public function doAllChildrenIncludingDeleted($context = null) {
|
||||||
if(!$this->owner) user_error('Hierarchy::doAllChildrenIncludingDeleted() called without $this->owner');
|
if(!$this->owner) user_error('Hierarchy::doAllChildrenIncludingDeleted() called without $this->owner');
|
||||||
@ -560,8 +594,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all the children that this page had, including pages that were deleted
|
* Return all the children that this page had, including pages that were deleted from both stage & live.
|
||||||
* from both stage & live.
|
*
|
||||||
|
* @return DataList
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function AllHistoricalChildren() {
|
public function AllHistoricalChildren() {
|
||||||
if(!$this->owner->hasExtension('Versioned')) {
|
if(!$this->owner->hasExtension('Versioned')) {
|
||||||
@ -574,7 +610,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the number of children that this page ever had, including pages that were deleted
|
* Return the number of children that this page ever had, including pages that were deleted.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function numHistoricalChildren() {
|
public function numHistoricalChildren() {
|
||||||
if(!$this->owner->hasExtension('Versioned')) {
|
if(!$this->owner->hasExtension('Versioned')) {
|
||||||
@ -586,11 +625,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the number of direct children.
|
* Return the number of direct children. By default, values are cached after the first invocation. Can be
|
||||||
* By default, values are cached after the first invocation.
|
* augumented by {@link augmentNumChildrenCountQuery()}.
|
||||||
* Can be augumented by {@link augmentNumChildrenCountQuery()}.
|
|
||||||
*
|
*
|
||||||
* @param Boolean $cache
|
* @param bool $cache Whether to retrieve values from cache
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public function numChildren($cache = true) {
|
public function numChildren($cache = true) {
|
||||||
@ -606,10 +644,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return children from the stage site
|
* Return children in the stage site.
|
||||||
*
|
*
|
||||||
* @param showAll Inlcude all of the elements, even those not shown in the menus.
|
* @param bool $showAll Include all of the elements, even those not shown in the menus. Only applicable when
|
||||||
* (only applicable when extension is applied to {@link SiteTree}).
|
* extension is applied to {@link SiteTree}.
|
||||||
* @return DataList
|
* @return DataList
|
||||||
*/
|
*/
|
||||||
public function stageChildren($showAll = false) {
|
public function stageChildren($showAll = false) {
|
||||||
@ -625,12 +663,13 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return children from the live site, if it exists.
|
* Return children in the live site, if it exists.
|
||||||
*
|
*
|
||||||
* @param boolean $showAll Include all of the elements, even those not shown in the menus.
|
* @param bool $showAll Include all of the elements, even those not shown in the menus. Only
|
||||||
* (only applicable when extension is applied to {@link SiteTree}).
|
* applicable when extension is applied to {@link SiteTree}.
|
||||||
* @param boolean $onlyDeletedFromStage Only return items that have been deleted from stage
|
* @param bool $onlyDeletedFromStage Only return items that have been deleted from stage
|
||||||
* @return SS_List
|
* @return DataList
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function liveChildren($showAll = false, $onlyDeletedFromStage = false) {
|
public function liveChildren($showAll = false, $onlyDeletedFromStage = false) {
|
||||||
if(!$this->owner->hasExtension('Versioned')) {
|
if(!$this->owner->hasExtension('Versioned')) {
|
||||||
@ -652,7 +691,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parent of this class.
|
* Get this object's parent, optionally filtered by an SQL clause. If the clause doesn't match the parent, nothing
|
||||||
|
* is returned.
|
||||||
|
*
|
||||||
|
* @param string $filter
|
||||||
* @return DataObject
|
* @return DataObject
|
||||||
*/
|
*/
|
||||||
public function getParent($filter = null) {
|
public function getParent($filter = null) {
|
||||||
@ -669,7 +711,7 @@ class Hierarchy extends DataExtension {
|
|||||||
/**
|
/**
|
||||||
* Return all the parents of this class in a set ordered from the lowest to highest parent.
|
* Return all the parents of this class in a set ordered from the lowest to highest parent.
|
||||||
*
|
*
|
||||||
* @return SS_List
|
* @return ArrayList
|
||||||
*/
|
*/
|
||||||
public function getAncestors() {
|
public function getAncestors() {
|
||||||
$ancestors = new ArrayList();
|
$ancestors = new ArrayList();
|
||||||
@ -683,11 +725,10 @@ class Hierarchy extends DataExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a human-readable, flattened representation of the path to the object,
|
* Returns a human-readable, flattened representation of the path to the object, using its {@link Title} attribute.
|
||||||
* using its {@link Title()} attribute.
|
|
||||||
*
|
*
|
||||||
* @param String
|
* @param string $separator
|
||||||
* @return String
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getBreadcrumbs($separator = ' » ') {
|
public function getBreadcrumbs($separator = ' » ') {
|
||||||
$crumbs = array();
|
$crumbs = array();
|
||||||
@ -702,22 +743,25 @@ class Hierarchy extends DataExtension {
|
|||||||
* then search the parents.
|
* then search the parents.
|
||||||
*
|
*
|
||||||
* @todo Write!
|
* @todo Write!
|
||||||
|
*
|
||||||
|
* @param string $className Class name of the node to find
|
||||||
|
* @param DataObject $afterNode Used for recursive calls to this function
|
||||||
|
* @return DataObject
|
||||||
*/
|
*/
|
||||||
public function naturalPrev( $className, $afterNode = null ) {
|
public function naturalPrev($className, $afterNode = null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next node in the tree of the type. If there is no instance of the className descended from this node,
|
* Get the next node in the tree of the type. If there is no instance of the className descended from this node,
|
||||||
* then search the parents.
|
* then search the parents.
|
||||||
* @param string $className Class name of the node to find.
|
* @param string $className Class name of the node to find.
|
||||||
* @param string|int $root ID/ClassName of the node to limit the search to
|
* @param string|int $root ID/ClassName of the node to limit the search to
|
||||||
* @param DataObject afterNode Used for recursive calls to this function
|
* @param DataObject $afterNode Used for recursive calls to this function
|
||||||
* @return DataObject
|
* @return DataObject
|
||||||
*/
|
*/
|
||||||
public function naturalNext( $className = null, $root = 0, $afterNode = null ) {
|
public function naturalNext($className = null, $root = 0, $afterNode = null ) {
|
||||||
// If this node is not the node we are searching from, then we can possibly return this
|
// If this node is not the node we are searching from, then we can possibly return this node as a solution
|
||||||
// node as a solution
|
|
||||||
if($afterNode && $afterNode->ID != $this->owner->ID) {
|
if($afterNode && $afterNode->ID != $this->owner->ID) {
|
||||||
if(!$className || ($className && $this->owner->class == $className)) {
|
if(!$className || ($className && $this->owner->class == $className)) {
|
||||||
return $this->owner;
|
return $this->owner;
|
||||||
@ -761,6 +805,14 @@ class Hierarchy extends DataExtension {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush all Hierarchy caches:
|
||||||
|
* - Children (instance)
|
||||||
|
* - NumChildren (instance)
|
||||||
|
* - Marked (global)
|
||||||
|
* - Expanded (global)
|
||||||
|
* - TreeOpened (global)
|
||||||
|
*/
|
||||||
public function flushCache() {
|
public function flushCache() {
|
||||||
$this->_cache_children = null;
|
$this->_cache_children = null;
|
||||||
$this->_cache_numChildren = null;
|
$this->_cache_numChildren = null;
|
||||||
@ -769,6 +821,12 @@ class Hierarchy extends DataExtension {
|
|||||||
self::$treeOpened = array();
|
self::$treeOpened = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset global Hierarchy caches:
|
||||||
|
* - Marked
|
||||||
|
* - Expanded
|
||||||
|
* - TreeOpened
|
||||||
|
*/
|
||||||
public static function reset() {
|
public static function reset() {
|
||||||
self::$marked = array();
|
self::$marked = array();
|
||||||
self::$expanded = array();
|
self::$expanded = array();
|
||||||
|
@ -650,9 +650,8 @@ abstract class SQLConditionalExpression extends SQLExpression {
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function filtersOnID() {
|
public function filtersOnID() {
|
||||||
$regexp = '/^(.*\.)?("|`)?ID("|`)?\s?=/';
|
$regexp = '/^(.*\.)?("|`)?ID("|`)?\s?(=|IN)/';
|
||||||
|
|
||||||
// @todo - Test this works with paramaterised queries
|
|
||||||
foreach($this->getWhereParameterised($parameters) as $predicate) {
|
foreach($this->getWhereParameterised($parameters) as $predicate) {
|
||||||
if(preg_match($regexp, $predicate)) return true;
|
if(preg_match($regexp, $predicate)) return true;
|
||||||
}
|
}
|
||||||
@ -668,7 +667,7 @@ abstract class SQLConditionalExpression extends SQLExpression {
|
|||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function filtersOnFK() {
|
public function filtersOnFK() {
|
||||||
$regexp = '/^(.*\.)?("|`)?[a-zA-Z]+ID("|`)?\s?=/';
|
$regexp = '/^(.*\.)?("|`)?[a-zA-Z]+ID("|`)?\s?(=|IN)/';
|
||||||
|
|
||||||
// @todo - Test this works with paramaterised queries
|
// @todo - Test this works with paramaterised queries
|
||||||
foreach($this->getWhereParameterised($parameters) as $predicate) {
|
foreach($this->getWhereParameterised($parameters) as $predicate) {
|
||||||
|
@ -290,15 +290,23 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
/**
|
/**
|
||||||
* Check if the passed password matches the stored one (if the member is not locked out).
|
* Check if the passed password matches the stored one (if the member is not locked out).
|
||||||
*
|
*
|
||||||
* @param string $password
|
* @param string $password
|
||||||
* @return ValidationResult
|
* @return ValidationResult
|
||||||
*/
|
*/
|
||||||
public function checkPassword($password) {
|
public function checkPassword($password) {
|
||||||
$result = $this->canLogIn();
|
$result = $this->canLogIn();
|
||||||
|
|
||||||
// Short-circuit the result upon failure, no further checks needed.
|
// Short-circuit the result upon failure, no further checks needed.
|
||||||
if (!$result->valid()) return $result;
|
if (!$result->valid()) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow default admin to login as self
|
||||||
|
if($this->isDefaultAdmin() && Security::check_default_admin($this->Email, $password)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check a password is set on this member
|
||||||
if(empty($this->Password) && $this->exists()) {
|
if(empty($this->Password) && $this->exists()) {
|
||||||
$result->error(_t('Member.NoPassword','There is no password on this member.'));
|
$result->error(_t('Member.NoPassword','There is no password on this member.'));
|
||||||
return $result;
|
return $result;
|
||||||
@ -315,6 +323,16 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this user is the currently configured default admin
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isDefaultAdmin() {
|
||||||
|
return Security::has_default_admin()
|
||||||
|
&& $this->Email === Security::default_admin_username();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a valid {@link ValidationResult} if this member can currently log in, or an invalid
|
* Returns a valid {@link ValidationResult} if this member can currently log in, or an invalid
|
||||||
* one with error messages to display if the member is locked out.
|
* one with error messages to display if the member is locked out.
|
||||||
@ -725,14 +743,7 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
public function getMemberFormFields() {
|
public function getMemberFormFields() {
|
||||||
$fields = parent::getFrontendFields();
|
$fields = parent::getFrontendFields();
|
||||||
|
|
||||||
$fields->replaceField('Password', $password = new ConfirmedPasswordField (
|
$fields->replaceField('Password', $this->getMemberPasswordField());
|
||||||
'Password',
|
|
||||||
$this->fieldLabel('Password'),
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
(bool) $this->ID
|
|
||||||
));
|
|
||||||
$password->setCanBeEmpty(true);
|
|
||||||
|
|
||||||
$fields->replaceField('Locale', new DropdownField (
|
$fields->replaceField('Locale', new DropdownField (
|
||||||
'Locale',
|
'Locale',
|
||||||
@ -748,6 +759,36 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
return $fields;
|
return $fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds "Change / Create Password" field for this member
|
||||||
|
*
|
||||||
|
* @return ConfirmedPasswordField
|
||||||
|
*/
|
||||||
|
public function getMemberPasswordField() {
|
||||||
|
$editingPassword = $this->isInDB();
|
||||||
|
$label = $editingPassword
|
||||||
|
? _t('Member.EDIT_PASSWORD', 'New Password')
|
||||||
|
: $this->fieldLabel('Password');
|
||||||
|
/** @var ConfirmedPasswordField $password */
|
||||||
|
$password = ConfirmedPasswordField::create(
|
||||||
|
'Password',
|
||||||
|
$label,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$editingPassword
|
||||||
|
);
|
||||||
|
|
||||||
|
// If editing own password, require confirmation of existing
|
||||||
|
if($editingPassword && $this->ID == Member::currentUserID()) {
|
||||||
|
$password->setRequireExistingPassword(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$password->setCanBeEmpty(true);
|
||||||
|
$this->extend('updateMemberPasswordField', $password);
|
||||||
|
return $password;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link RequiredFields} instance for the Member object. This
|
* Returns the {@link RequiredFields} instance for the Member object. This
|
||||||
* Validator is used when saving a {@link CMSProfileController} or added to
|
* Validator is used when saving a {@link CMSProfileController} or added to
|
||||||
@ -1337,19 +1378,12 @@ class Member extends DataObject implements TemplateGlobalProvider {
|
|||||||
require_once 'Zend/Date.php';
|
require_once 'Zend/Date.php';
|
||||||
|
|
||||||
$self = $this;
|
$self = $this;
|
||||||
$this->beforeUpdateCMSFields(function($fields) use ($self) {
|
$this->beforeUpdateCMSFields(function(FieldList $fields) use ($self) {
|
||||||
$mainFields = $fields->fieldByName("Root")->fieldByName("Main")->Children;
|
/** @var FieldList $mainFields */
|
||||||
|
$mainFields = $fields->fieldByName("Root")->fieldByName("Main")->getChildren();
|
||||||
|
|
||||||
$password = new ConfirmedPasswordField(
|
// Build change password field
|
||||||
'Password',
|
$mainFields->replaceField('Password', $self->getMemberPasswordField());
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
true // showOnClick
|
|
||||||
);
|
|
||||||
$password->setCanBeEmpty(true);
|
|
||||||
if( ! $self->ID) $password->showOnClick = false;
|
|
||||||
$mainFields->replaceField('Password', $password);
|
|
||||||
|
|
||||||
$mainFields->replaceField('Locale', new DropdownField(
|
$mainFields->replaceField('Locale', new DropdownField(
|
||||||
"Locale",
|
"Locale",
|
||||||
|
@ -19,9 +19,18 @@ Feature: Manage my own settings
|
|||||||
Then I should see "Jack"
|
Then I should see "Jack"
|
||||||
And I should see "Johnson"
|
And I should see "Johnson"
|
||||||
|
|
||||||
|
Scenario: I can't reset the password without the original
|
||||||
|
Given I follow "Change Password"
|
||||||
|
And I fill in "Current Password" with "idontknow"
|
||||||
|
And I fill in "New Password" with "newsecret"
|
||||||
|
And I fill in "Confirm Password" with "newsecret"
|
||||||
|
And I press the "Save" button
|
||||||
|
Then I should see "The current password you have entered is not correct."
|
||||||
|
|
||||||
Scenario: I can change my password
|
Scenario: I can change my password
|
||||||
Given I follow "Change Password"
|
Given I follow "Change Password"
|
||||||
And I fill in "Password" with "newsecret"
|
And I fill in "Current Password" with "secret"
|
||||||
|
And I fill in "New Password" with "newsecret"
|
||||||
And I fill in "Confirm Password" with "newsecret"
|
And I fill in "Confirm Password" with "newsecret"
|
||||||
And I press the "Save" button
|
And I press the "Save" button
|
||||||
And I am not logged in
|
And I am not logged in
|
||||||
@ -35,4 +44,4 @@ Feature: Manage my own settings
|
|||||||
Then I should see "Sprache"
|
Then I should see "Sprache"
|
||||||
|
|
||||||
# TODO Date/time format - Difficult because its not exposed anywhere in the CMS?
|
# TODO Date/time format - Difficult because its not exposed anywhere in the CMS?
|
||||||
# TODO Group modification as ADMIN user
|
# TODO Group modification as ADMIN user
|
||||||
|
@ -8,6 +8,30 @@
|
|||||||
*/
|
*/
|
||||||
class ErrorControlChainTest_Chain extends ErrorControlChain {
|
class ErrorControlChainTest_Chain extends ErrorControlChain {
|
||||||
|
|
||||||
|
protected $displayErrors = 'STDERR';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify method visibility to public for testing
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDisplayErrors()
|
||||||
|
{
|
||||||
|
// Protect manipulation of underlying php_ini values
|
||||||
|
return $this->displayErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify method visibility to public for testing
|
||||||
|
*
|
||||||
|
* @param mixed $errors
|
||||||
|
*/
|
||||||
|
public function setDisplayErrors($errors)
|
||||||
|
{
|
||||||
|
// Protect manipulation of underlying php_ini values
|
||||||
|
$this->displayErrors = $errors;
|
||||||
|
}
|
||||||
|
|
||||||
// Change function visibility to be testable directly
|
// Change function visibility to be testable directly
|
||||||
public function translateMemstring($memstring) {
|
public function translateMemstring($memstring) {
|
||||||
return parent::translateMemstring($memstring);
|
return parent::translateMemstring($memstring);
|
||||||
@ -63,10 +87,7 @@ require_once '$classpath';
|
|||||||
|
|
||||||
class ErrorControlChainTest extends SapphireTest {
|
class ErrorControlChainTest extends SapphireTest {
|
||||||
|
|
||||||
protected $displayErrors = null;
|
|
||||||
|
|
||||||
function setUp() {
|
function setUp() {
|
||||||
$this->displayErrors = (bool)ini_get('display_errors');
|
|
||||||
|
|
||||||
// Check we can run PHP at all
|
// Check we can run PHP at all
|
||||||
$null = is_writeable('/dev/null') ? '/dev/null' : 'NUL';
|
$null = is_writeable('/dev/null') ? '/dev/null' : 'NUL';
|
||||||
@ -79,50 +100,55 @@ class ErrorControlChainTest extends SapphireTest {
|
|||||||
parent::setUp();
|
parent::setUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown() {
|
|
||||||
if($this->displayErrors !== null) {
|
|
||||||
ini_set('display_errors', $this->displayErrors);
|
|
||||||
$this->displayErrors = null;
|
|
||||||
}
|
|
||||||
parent::tearDown(); // TODO: Change the autogenerated stub
|
|
||||||
}
|
|
||||||
|
|
||||||
function testErrorSuppression() {
|
function testErrorSuppression() {
|
||||||
|
|
||||||
// Errors disabled by default
|
// Errors disabled by default
|
||||||
ini_set('display_errors', false);
|
$chain = new ErrorControlChainTest_Chain();
|
||||||
$chain = new ErrorControlChain();
|
$chain->setDisplayErrors('Off'); // mocks display_errors: Off
|
||||||
|
$initialValue = null;
|
||||||
$whenNotSuppressed = null;
|
$whenNotSuppressed = null;
|
||||||
$whenSuppressed = null;
|
$whenSuppressed = null;
|
||||||
$chain->then(function($chain) use(&$whenNotSuppressed, &$whenSuppressed) {
|
$chain->then(
|
||||||
$chain->setSuppression(true);
|
function(ErrorControlChainTest_Chain $chain)
|
||||||
$whenSuppressed = ini_get('display_errors');
|
use(&$initialValue, &$whenNotSuppressed, &$whenSuppressed) {
|
||||||
$chain->setSuppression(false);
|
$initialValue = $chain->getDisplayErrors();
|
||||||
$whenNotSuppressed = ini_get('display_errors');
|
$chain->setSuppression(false);
|
||||||
})->execute();
|
$whenNotSuppressed = $chain->getDisplayErrors();
|
||||||
|
$chain->setSuppression(true);
|
||||||
|
$whenSuppressed = $chain->getDisplayErrors();
|
||||||
|
}
|
||||||
|
)->execute();
|
||||||
|
|
||||||
// Disabled errors never un-disable
|
// Disabled errors never un-disable
|
||||||
$this->assertFalse((bool)$whenNotSuppressed);
|
$this->assertEquals(0, $initialValue); // Chain starts suppressed
|
||||||
$this->assertFalse((bool)$whenSuppressed);
|
$this->assertEquals(0, $whenSuppressed); // false value used internally when suppressed
|
||||||
|
$this->assertEquals('Off', $whenNotSuppressed); // false value set by php ini when suppression lifted
|
||||||
|
$this->assertEquals('Off', $chain->getDisplayErrors()); // Correctly restored after run
|
||||||
|
|
||||||
// Errors enabled by default
|
// Errors enabled by default
|
||||||
ini_set('display_errors', true);
|
$chain = new ErrorControlChainTest_Chain();
|
||||||
$chain = new ErrorControlChain();
|
$chain->setDisplayErrors('Yes'); // non-falsey ini value
|
||||||
|
$initialValue = null;
|
||||||
$whenNotSuppressed = null;
|
$whenNotSuppressed = null;
|
||||||
$whenSuppressed = null;
|
$whenSuppressed = null;
|
||||||
$chain->then(function($chain) use(&$whenNotSuppressed, &$whenSuppressed) {
|
$chain->then(
|
||||||
$chain->setSuppression(true);
|
function(ErrorControlChainTest_Chain $chain)
|
||||||
$whenSuppressed = ini_get('display_errors');
|
use(&$initialValue, &$whenNotSuppressed, &$whenSuppressed) {
|
||||||
$chain->setSuppression(false);
|
$initialValue = $chain->getDisplayErrors();
|
||||||
$whenNotSuppressed = ini_get('display_errors');
|
$chain->setSuppression(true);
|
||||||
})->execute();
|
$whenSuppressed = $chain->getDisplayErrors();
|
||||||
|
$chain->setSuppression(false);
|
||||||
|
$whenNotSuppressed = $chain->getDisplayErrors();
|
||||||
|
}
|
||||||
|
)->execute();
|
||||||
|
|
||||||
// Errors can be suppressed an un-suppressed when initially enabled
|
// Errors can be suppressed an un-suppressed when initially enabled
|
||||||
$this->assertTrue((bool)$whenNotSuppressed);
|
$this->assertEquals(0, $initialValue); // Chain starts suppressed
|
||||||
$this->assertFalse((bool)$whenSuppressed);
|
$this->assertEquals(0, $whenSuppressed); // false value used internally when suppressed
|
||||||
|
$this->assertEquals('Yes', $whenNotSuppressed); // false value set by php ini when suppression lifted
|
||||||
|
$this->assertEquals('Yes', $chain->getDisplayErrors()); // Correctly restored after run
|
||||||
|
|
||||||
// Fatal error
|
// Fatal error
|
||||||
|
|
||||||
$chain = new ErrorControlChainTest_Chain();
|
$chain = new ErrorControlChainTest_Chain();
|
||||||
|
|
||||||
list($out, $code) = $chain
|
list($out, $code) = $chain
|
||||||
|
@ -254,6 +254,32 @@ class DropdownFieldTest extends SapphireTest {
|
|||||||
$this->assertEquals(count($disabledOptions), 0, 'There are no disabled options');
|
$this->assertEquals(count($disabledOptions), 0, 'There are no disabled options');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Field() method should be able to handle arrays as values in an edge case. If it couldn't handle it then
|
||||||
|
* this test would trigger an array to string conversion PHP notice
|
||||||
|
*
|
||||||
|
* @dataProvider arrayValueProvider
|
||||||
|
*/
|
||||||
|
public function testDropdownWithArrayValues($value) {
|
||||||
|
$field = $this->createDropdownField();
|
||||||
|
$field->setValue($value);
|
||||||
|
$this->assertInstanceOf('SilverStripe\Model\FieldType\DBHTMLText', $field->Field());
|
||||||
|
$this->assertSame($value, $field->Value());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function arrayValueProvider() {
|
||||||
|
return array(
|
||||||
|
array(array()),
|
||||||
|
array(array(0)),
|
||||||
|
array(array(123)),
|
||||||
|
array(array('string')),
|
||||||
|
array('Regression-ish test.')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a test dropdown field, with the option to
|
* Create a test dropdown field, with the option to
|
||||||
* set what source and blank value it should contain
|
* set what source and blank value it should contain
|
||||||
|
@ -137,6 +137,18 @@ class GridFieldExportButtonTest extends SapphireTest {
|
|||||||
$button->generateExportFileData($this->gridField)
|
$button->generateExportFileData($this->gridField)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testZeroValue() {
|
||||||
|
$button = new GridFieldExportButton();
|
||||||
|
$button->setExportColumns(array(
|
||||||
|
'RugbyTeamNumber' => 'Rugby Team Number'
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
"\"Rugby Team Number\"\n2\n0\n",
|
||||||
|
$button->generateExportFileData($this->gridField)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,7 +159,8 @@ class GridFieldExportButtonTest_Team extends DataObject implements TestOnly {
|
|||||||
|
|
||||||
private static $db = array(
|
private static $db = array(
|
||||||
'Name' => 'Varchar',
|
'Name' => 'Varchar',
|
||||||
'City' => 'Varchar'
|
'City' => 'Varchar',
|
||||||
|
'RugbyTeamNumber' => 'Int'
|
||||||
);
|
);
|
||||||
|
|
||||||
public function canView($member = null) {
|
public function canView($member = null) {
|
||||||
@ -164,7 +177,8 @@ class GridFieldExportButtonTest_NoView extends DataObject implements TestOnly {
|
|||||||
|
|
||||||
private static $db = array(
|
private static $db = array(
|
||||||
'Name' => 'Varchar',
|
'Name' => 'Varchar',
|
||||||
'City' => 'Varchar'
|
'City' => 'Varchar',
|
||||||
|
'RugbyTeamNumber' => 'Int'
|
||||||
);
|
);
|
||||||
|
|
||||||
public function canView($member = null) {
|
public function canView($member = null) {
|
||||||
|
@ -2,9 +2,11 @@ GridFieldExportButtonTest_Team:
|
|||||||
test-team-1:
|
test-team-1:
|
||||||
Name: Test
|
Name: Test
|
||||||
City: City
|
City: City
|
||||||
|
RugbyTeamNumber: 2
|
||||||
test-team-2:
|
test-team-2:
|
||||||
Name: Test2
|
Name: Test2
|
||||||
City: 'Quoted "City" 2'
|
City: 'Quoted "City" 2'
|
||||||
|
RugbyTeamNumber: 0
|
||||||
GridFieldExportButtonTest_NoView:
|
GridFieldExportButtonTest_NoView:
|
||||||
item1:
|
item1:
|
||||||
Name: Foo
|
Name: Foo
|
||||||
|
@ -74,6 +74,35 @@ class DBMoneyTest extends SapphireTest {
|
|||||||
$this->assertEquals(0.0000, $moneyTest->MyMoneyAmount);
|
$this->assertEquals(0.0000, $moneyTest->MyMoneyAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testIsChanged() {
|
||||||
|
$obj1 = $this->objFromFixture('MoneyTest_DataObject', 'test1');
|
||||||
|
$this->assertFalse($obj1->isChanged());
|
||||||
|
$this->assertFalse($obj1->isChanged('MyMoney'));
|
||||||
|
|
||||||
|
// modify non-db field
|
||||||
|
$m1 = new DBMoney();
|
||||||
|
$m1->setAmount(500);
|
||||||
|
$m1->setCurrency('NZD');
|
||||||
|
$obj1->NonDBMoneyField = $m1;
|
||||||
|
$this->assertFalse($obj1->isChanged()); // Because only detects DB fields
|
||||||
|
$this->assertTrue($obj1->isChanged('NonDBMoneyField')); // Allow change detection to non-db fields explicitly named
|
||||||
|
|
||||||
|
// Modify db field
|
||||||
|
$obj2 = $this->objFromFixture('MoneyTest_DataObject', 'test2');
|
||||||
|
$m2 = new DBMoney();
|
||||||
|
$m2->setAmount(500);
|
||||||
|
$m2->setCurrency('NZD');
|
||||||
|
$obj2->MyMoney = $m2;
|
||||||
|
$this->assertTrue($obj2->isChanged()); // Detects change to DB field
|
||||||
|
$this->assertTrue($obj2->ischanged('MyMoney'));
|
||||||
|
|
||||||
|
// Modify sub-fields
|
||||||
|
$obj3 = $this->objFromFixture('MoneyTest_DataObject', 'test3');
|
||||||
|
$obj3->MyMoneyCurrency = 'USD';
|
||||||
|
$this->assertTrue($obj3->isChanged()); // Detects change to DB field
|
||||||
|
$this->assertTrue($obj3->ischanged('MyMoneyCurrency'));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a Money object to the database, then re-read it to ensure it
|
* Write a Money object to the database, then re-read it to ensure it
|
||||||
* is re-read properly.
|
* is re-read properly.
|
||||||
|
@ -565,29 +565,29 @@ class DataObjectTest extends SapphireTest {
|
|||||||
$obj->IsRetired = true;
|
$obj->IsRetired = true;
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$obj->getChangedFields(false, 1),
|
$obj->getChangedFields(true, DataObject::CHANGE_STRICT),
|
||||||
array(
|
array(
|
||||||
'FirstName' => array(
|
'FirstName' => array(
|
||||||
'before' => 'Captain',
|
'before' => 'Captain',
|
||||||
'after' => 'Captain-changed',
|
'after' => 'Captain-changed',
|
||||||
'level' => 2
|
'level' => DataObject::CHANGE_VALUE
|
||||||
),
|
),
|
||||||
'IsRetired' => array(
|
'IsRetired' => array(
|
||||||
'before' => 1,
|
'before' => 1,
|
||||||
'after' => true,
|
'after' => true,
|
||||||
'level' => 1
|
'level' => DataObject::CHANGE_STRICT
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
'Changed fields are correctly detected with strict type changes (level=1)'
|
'Changed fields are correctly detected with strict type changes (level=1)'
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$obj->getChangedFields(false, 2),
|
$obj->getChangedFields(true, DataObject::CHANGE_VALUE),
|
||||||
array(
|
array(
|
||||||
'FirstName' => array(
|
'FirstName' => array(
|
||||||
'before'=>'Captain',
|
'before'=>'Captain',
|
||||||
'after'=>'Captain-changed',
|
'after'=>'Captain-changed',
|
||||||
'level' => 2
|
'level' => DataObject::CHANGE_VALUE
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
'Changed fields are correctly detected while ignoring type changes (level=2)'
|
'Changed fields are correctly detected while ignoring type changes (level=2)'
|
||||||
@ -596,50 +596,58 @@ class DataObjectTest extends SapphireTest {
|
|||||||
$newObj = new DataObjectTest_Player();
|
$newObj = new DataObjectTest_Player();
|
||||||
$newObj->FirstName = "New Player";
|
$newObj->FirstName = "New Player";
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$newObj->getChangedFields(false, 2),
|
|
||||||
array(
|
array(
|
||||||
'FirstName' => array(
|
'FirstName' => array(
|
||||||
'before' => null,
|
'before' => null,
|
||||||
'after' => 'New Player',
|
'after' => 'New Player',
|
||||||
'level' => 2
|
'level' => DataObject::CHANGE_VALUE
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
$newObj->getChangedFields(true, DataObject::CHANGE_VALUE),
|
||||||
'Initialised fields are correctly detected as full changes'
|
'Initialised fields are correctly detected as full changes'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsChanged() {
|
public function testIsChanged() {
|
||||||
$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
|
$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
|
||||||
|
$obj->NonDBField = 'bob';
|
||||||
$obj->FirstName = 'Captain-changed';
|
$obj->FirstName = 'Captain-changed';
|
||||||
$obj->IsRetired = true; // type change only, database stores "1"
|
$obj->IsRetired = true; // type change only, database stores "1"
|
||||||
|
|
||||||
$this->assertTrue($obj->isChanged('FirstName', 1));
|
// Now that DB fields are changed, isChanged is true
|
||||||
$this->assertTrue($obj->isChanged('FirstName', 2));
|
$this->assertTrue($obj->isChanged('NonDBField'));
|
||||||
$this->assertTrue($obj->isChanged('IsRetired', 1));
|
$this->assertFalse($obj->isChanged('NonField'));
|
||||||
$this->assertFalse($obj->isChanged('IsRetired', 2));
|
$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
|
||||||
|
$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
|
||||||
|
$this->assertTrue($obj->isChanged('IsRetired', DataObject::CHANGE_STRICT));
|
||||||
|
$this->assertFalse($obj->isChanged('IsRetired', DataObject::CHANGE_VALUE));
|
||||||
$this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property');
|
$this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property');
|
||||||
$this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property');
|
$this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property');
|
||||||
|
|
||||||
$newObj = new DataObjectTest_Player();
|
$newObj = new DataObjectTest_Player();
|
||||||
$newObj->FirstName = "New Player";
|
$newObj->FirstName = "New Player";
|
||||||
$this->assertTrue($newObj->isChanged('FirstName', 1));
|
$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
|
||||||
$this->assertTrue($newObj->isChanged('FirstName', 2));
|
$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
|
||||||
$this->assertFalse($newObj->isChanged('Email', 1));
|
$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
|
||||||
$this->assertFalse($newObj->isChanged('Email', 2));
|
$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
|
||||||
|
|
||||||
$newObj->write();
|
$newObj->write();
|
||||||
$this->assertFalse($newObj->isChanged('FirstName', 1));
|
$this->assertFalse($newObj->ischanged());
|
||||||
$this->assertFalse($newObj->isChanged('FirstName', 2));
|
$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
|
||||||
$this->assertFalse($newObj->isChanged('Email', 1));
|
$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
|
||||||
$this->assertFalse($newObj->isChanged('Email', 2));
|
$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
|
||||||
|
$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
|
||||||
|
|
||||||
$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
|
$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
|
||||||
$obj->FirstName = null;
|
$obj->FirstName = null;
|
||||||
$this->assertTrue($obj->isChanged('FirstName', 1));
|
$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
|
||||||
$this->assertTrue($obj->isChanged('FirstName', 2));
|
$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
|
||||||
|
|
||||||
/* Test when there's not field provided */
|
/* Test when there's not field provided */
|
||||||
$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
|
$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
|
||||||
|
$this->assertFalse($obj->isChanged());
|
||||||
|
$obj->NonDBField = 'new value';
|
||||||
|
$this->assertFalse($obj->isChanged());
|
||||||
$obj->FirstName = "New Player";
|
$obj->FirstName = "New Player";
|
||||||
$this->assertTrue($obj->isChanged());
|
$this->assertTrue($obj->isChanged());
|
||||||
|
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
MoneyTest_DataObject:
|
MoneyTest_DataObject:
|
||||||
test1:
|
test1:
|
||||||
MyMoneyCurrency: EUR
|
MyMoneyCurrency: EUR
|
||||||
MyMoneyAmount: 1.23
|
MyMoneyAmount: 1.23
|
||||||
|
test2:
|
||||||
|
MyMoneyCurrency: USD
|
||||||
|
MyMoneyAmount: 4.45
|
||||||
|
test3:
|
||||||
|
MyMoneyCurrency: NZD
|
||||||
|
MyMoneyAmount: 7.66
|
||||||
MoneyTest_SubClass:
|
MoneyTest_SubClass:
|
||||||
test2:
|
test2:
|
||||||
MyOtherMoneyCurrency: GBP
|
MyOtherMoneyCurrency: GBP
|
||||||
MyOtherMoneyAmount: 2.46
|
MyOtherMoneyAmount: 2.46
|
||||||
|
@ -315,6 +315,41 @@ class SQLSelectTest extends SapphireTest {
|
|||||||
"filtersOnID() is true with simple unquoted column name"
|
"filtersOnID() is true with simple unquoted column name"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$query = new SQLSelect();
|
||||||
|
$query->setWhere('"ID" = 5');
|
||||||
|
$this->assertTrue(
|
||||||
|
$query->filtersOnID(),
|
||||||
|
"filtersOnID() is true with simple quoted column name"
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = new SQLSelect();
|
||||||
|
$query->setWhere(array('"ID"' => 4));
|
||||||
|
$this->assertTrue(
|
||||||
|
$query->filtersOnID(),
|
||||||
|
"filtersOnID() is true with parameterised quoted column name"
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = new SQLSelect();
|
||||||
|
$query->setWhere(array('"ID" = ?' => 4));
|
||||||
|
$this->assertTrue(
|
||||||
|
$query->filtersOnID(),
|
||||||
|
"filtersOnID() is true with parameterised quoted column name"
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = new SQLSelect();
|
||||||
|
$query->setWhere('"ID" IN (5,4)');
|
||||||
|
$this->assertTrue(
|
||||||
|
$query->filtersOnID(),
|
||||||
|
"filtersOnID() is true with WHERE ID IN"
|
||||||
|
);
|
||||||
|
|
||||||
|
$query = new SQLSelect();
|
||||||
|
$query->setWhere(array('"ID" IN ?' => array(1,2)));
|
||||||
|
$this->assertTrue(
|
||||||
|
$query->filtersOnID(),
|
||||||
|
"filtersOnID() is true with parameterised WHERE ID IN"
|
||||||
|
);
|
||||||
|
|
||||||
$query = new SQLSelect();
|
$query = new SQLSelect();
|
||||||
$query->setWhere("ID=5");
|
$query->setWhere("ID=5");
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
|
Loading…
Reference in New Issue
Block a user