Compare commits

...

4 Commits

Author SHA1 Message Date
Garion Herman
801133bc64
Merge 4b6c248296 into 6bb9a0b33d 2024-10-14 05:30:35 +13:00
github-actions
6bb9a0b33d Merge branch '5' into 6 2024-10-13 08:43:04 +00:00
Guy Sartorelli
ebbd6427b2
ENH Allow overriding GridFieldFilterHeader placeholder (#11418) 2024-10-11 15:49:39 +13:00
Garion Herman
4b6c248296 API Remove ALC renewal, tweak extension point
The ALC token is no longer rotated during an active login. Also removed related
`replace_token_during_session_renewal` config. The extension point that was
previously provided in the `renew()` method has been renamed and is now triggered
externally in the `CookieAuthenticationHandler::authenticateRequest()` method.
2024-10-01 12:55:44 +09:00
4 changed files with 76 additions and 62 deletions

View File

@ -49,6 +49,8 @@ class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridFi
*/ */
protected ?string $searchField = null; protected ?string $searchField = null;
private string $placeHolderText = '';
/** /**
* @inheritDoc * @inheritDoc
*/ */
@ -245,6 +247,24 @@ class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridFi
return false; return false;
} }
/**
* Get the text to be used as a placeholder in the search field.
* If blank, the placeholder will be generated based on the class held in the GridField.
*/
public function getPlaceHolderText(): string
{
return $this->placeHolderText;
}
/**
* Set the text to be used as a placeholder in the search field.
* If blank, this text will be generated based on the class held in the GridField.
*/
public function setPlaceHolderText(string $placeHolderText): static
{
$this->placeHolderText = $placeHolderText;
return $this;
}
/** /**
* Generate a search context based on the model class of the of the GridField * Generate a search context based on the model class of the of the GridField
@ -318,7 +338,7 @@ class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridFi
$schema = [ $schema = [
'formSchemaUrl' => $schemaUrl, 'formSchemaUrl' => $schemaUrl,
'name' => $searchField, 'name' => $searchField,
'placeholder' => _t(__CLASS__ . '.Search', 'Search "{name}"', ['name' => $this->getTitle($gridField, $inst)]), 'placeholder' => $this->getPlaceHolder($inst),
'filters' => $filters ?: new \stdClass, // stdClass maps to empty json object '{}' 'filters' => $filters ?: new \stdClass, // stdClass maps to empty json object '{}'
'gridfield' => $gridField->getName(), 'gridfield' => $gridField->getName(),
'searchAction' => $searchAction->getAttribute('name'), 'searchAction' => $searchAction->getAttribute('name'),
@ -330,19 +350,6 @@ class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridFi
return json_encode($schema); return json_encode($schema);
} }
private function getTitle(GridField $gridField, object $inst): string
{
if ($gridField->Title) {
return $gridField->Title;
}
if (ClassInfo::hasMethod($inst, 'i18n_plural_name')) {
return $inst->i18n_plural_name();
}
return ClassInfo::shortName($inst);
}
/** /**
* Returns the search form for the component * Returns the search form for the component
* *
@ -386,7 +393,7 @@ class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridFi
$field->addExtraClass('stacked no-change-track'); $field->addExtraClass('stacked no-change-track');
} }
$name = $this->getTitle($gridField, singleton($gridField->getModelClass())); $name = $this->getTitle(singleton($gridField->getModelClass()));
$this->searchForm = $form = new Form( $this->searchForm = $form = new Form(
$gridField, $gridField,
@ -456,4 +463,32 @@ class GridFieldFilterHeader extends AbstractGridFieldComponent implements GridFi
) )
]; ];
} }
/**
* Get the text that will be used as a placeholder in the search field.
*
* @param object $obj An instance of the class that will be searched against.
* If getPlaceHolderText is empty, this object will be used to build the placeholder
* e.g. 'Search "My Data Object"'
*/
private function getPlaceHolder(object $obj): string
{
$placeholder = $this->getPlaceHolderText();
if (!empty($placeholder)) {
return $placeholder;
}
if ($obj) {
return _t(__CLASS__ . '.Search', 'Search "{name}"', ['name' => $this->getTitle($obj)]);
}
return _t(__CLASS__ . '.Search_Default', 'Search');
}
private function getTitle(object $inst): string
{
if (ClassInfo::hasMethod($inst, 'i18n_plural_name')) {
return $inst->i18n_plural_name();
}
return ClassInfo::shortName($inst);
}
} }

View File

@ -175,22 +175,8 @@ class CookieAuthenticationHandler implements AuthenticationHandler
$this->cascadeInTo->logIn($member, false, $request); $this->cascadeInTo->logIn($member, false, $request);
} }
// Renew the token // Session renewal hook
Deprecation::withSuppressedNotice(fn() => $rememberLoginHash->renew()); $rememberLoginHash->extend('onAfterRenewSession');
// Send the new token to the client if it was changed
if ($rememberLoginHash->getToken()) {
$tokenExpiryDays = RememberLoginHash::config()->uninherited('token_expiry_days');
Cookie::set(
$this->getTokenCookieName(),
$member->ID . ':' . $rememberLoginHash->getToken(),
$tokenExpiryDays,
null,
null,
false,
true
);
}
// Audit logging hook // Audit logging hook
$member->extend('memberAutoLoggedIn'); $member->extend('memberAutoLoggedIn');

View File

@ -82,15 +82,6 @@ class RememberLoginHash extends DataObject
*/ */
private static $force_single_token = false; private static $force_single_token = false;
/**
* If true, the token will be replaced during session renewal. This can cause unexpected
* logouts if the new token does not reach the client (e.g. due to a network error).
*
* This can be disabled as of CMS 5.3, and renewal will be removed entirely in CMS 6.
* @deprecated 5.3.0 Will be removed without equivalent functionality
*/
private static bool $replace_token_during_session_renewal = true;
/** /**
* The token used for the hash. Only present during the lifetime of the request * The token used for the hash. Only present during the lifetime of the request
* that generates it, as the hash representation is stored in the database and * that generates it, as the hash representation is stored in the database and
@ -201,28 +192,6 @@ class RememberLoginHash extends DataObject
return $rememberLoginHash; return $rememberLoginHash;
} }
/**
* Generates a new hash for this member but keeps the device ID intact
*
* @deprecated 5.3.0 Will be removed without equivalent functionality
* @return RememberLoginHash
*/
public function renew()
{
// Only regenerate token if configured to do so
Deprecation::notice('5.3.0', 'Will be removed without equivalent functionality');
$replaceToken = RememberLoginHash::config()->get('replace_token_during_session_renewal');
if ($replaceToken) {
$hash = $this->getNewHash($this->Member());
$this->Hash = $hash;
}
$this->extend('onAfterRenewToken', $replaceToken);
$this->write();
return $this;
}
/** /**
* Deletes existing tokens for this member * Deletes existing tokens for this member
* if logout_across_devices is true, all tokens are deleted, otherwise * if logout_across_devices is true, all tokens are deleted, otherwise

View File

@ -3,6 +3,7 @@
namespace SilverStripe\Forms\Tests\GridField; namespace SilverStripe\Forms\Tests\GridField;
use LogicException; use LogicException;
use ReflectionMethod;
use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
@ -117,6 +118,29 @@ class GridFieldFilterHeaderTest extends SapphireTest
$this->assertEquals('testfield', $searchSchema->gridfield); $this->assertEquals('testfield', $searchSchema->gridfield);
} }
/**
* Tests the private method that returns the placeholder for the search field
*/
public function testGetPlaceHolder()
{
$gridField = new GridField('test');
$filterHeader = new GridFieldFilterHeader();
$reflectionGetPlaceHolder = new ReflectionMethod($filterHeader, 'getPlaceHolder');
$reflectionGetPlaceHolder->setAccessible(true);
// No explicit placeholder or model i18n_plural_name method
$this->assertSame('Search "ArrayData"', $reflectionGetPlaceHolder->invoke($filterHeader, new ArrayData()));
// No explicit placeholder, but model has i18n_plural_name method
$model = new DataObject();
$this->assertSame('Search "' . $model->i18n_plural_name() . '"', $reflectionGetPlaceHolder->invoke($filterHeader, $model));
// Explicit placeholder is set, which overrides both of the above cases
$filterHeader->setPlaceHolderText('This is the text');
$this->assertSame('This is the text', $reflectionGetPlaceHolder->invoke($filterHeader, $model));
$this->assertSame('This is the text', $reflectionGetPlaceHolder->invoke($filterHeader, new ArrayData()));
}
public function testHandleActionReset() public function testHandleActionReset()
{ {
// Init Grid state with some pre-existing filters // Init Grid state with some pre-existing filters