Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH Add Readonly field status #172

Merged
merged 3 commits into from
Jan 24, 2024

Conversation

sabina-talipova
Copy link
Contributor

@sabina-talipova sabina-talipova commented Jan 16, 2024

Description

The developer can set readonly using the setReadonly method and in the CMS the user will not be able to edit or delete record data. The user will only be able to view these records.
Also, this state will be used if the user can only view the page, but not edit it.

Screenshot 2024-01-16 at 1 43 05 PM

Test steps

  • Add the following code to your test DataObject or Page:
  private static array $has_one = [
       'HasOneLink' => Link::class,
   ];

   private static $has_many = [
       'HasManyLinksOne' => Link::class . '.Owner',
       'HasManyLinksTwo' => Link::class . '.Owner',
   ];

   private static array $owns = [
       'HasOneLink',
       'HasManyLinksOne',
       'HasManyLinksTwo',
   ];

   public function getCMSFields()
   {
       $fields = parent::getCMSFields();
       $fields->removeByName(['HasOneLinkID', 'HasManyLinksOne', 'HasManyLinksTwo']);

       $fields->addFieldsToTab(
           'Root.Main',
           [
               LinkField::create('HasOneLink')
               ->setReadonly(true),
               MultiLinkField::create('HasManyLinksOne'),
               MultiLinkField::create('HasManyLinksTwo')
               ->setReadonly(true),
           ],
       );

       return $fields;
   }

Note

There is some problem here, how this should work if the user can edit the page, but LinkField is set to read only. Which should take precedence over canEdit or ReadOnly? Since if the field is read only, then a user with canEdit permission will not be able to create a link. Should there be a super user who can create links? Based on this discussion: #141 (comment)

Parent issue

@sabina-talipova sabina-talipova force-pushed the pulls/4/readonly-link branch 3 times, most recently from 237f785 to f34fcee Compare January 16, 2024 19:33
@sabina-talipova sabina-talipova marked this pull request as ready for review January 16, 2024 19:35
@sabina-talipova sabina-talipova mentioned this pull request Jan 16, 2024
3 tasks
@sabina-talipova sabina-talipova force-pushed the pulls/4/readonly-link branch 2 times, most recently from c98121f to 1cf61bb Compare January 16, 2024 20:41
$request = $this->getRequest();
$ownerClass = $request->getVar('ownerClass') ?: $request->postVar('OwnerClass');
$ownerRelation = $this->ownerRelationFromRequest();
$isReadOnly = Injector::inst()->get($ownerClass)->getCMSFields()->dataFieldByName($ownerRelation)?->isReadonly();
Copy link
Contributor Author

@sabina-talipova sabina-talipova Jan 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method gets Owner's (e.g. Page) fields and check is this LinkField is read only.

$ownerRelation = $this->ownerRelationFromRequest();
$isReadOnly = Injector::inst()->get($ownerClass)->getCMSFields()->dataFieldByName($ownerRelation)?->isReadonly();

return $isReadOnly ?? false;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isReadOnly can be null in some cases.

@GuySartorelli
Copy link
Member

I'm not doing an actual review of this yet - just answering the question in the note:

There is some problem here, how this should work if the user can edit the page, but LinkField is set to read only. Which should take precedence over canEdit or ReadOnly? Since if the field is read only, then a user with canEdit permission will not be able to create a link. Should there be a super user who can create links? Based on this discussion: #141 (comment)

Readonly is more important than canEdit. It doesn't matter if we can edit anything if the field has been set to readonly - readonly explicitly means we're not allowed to edit the field, even if we have edit permissions for the model or the model's owner.

Copy link
Member

@emteknetnz emteknetnz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't tested locally yet

I really dislike the how 'readonly' has been stuffed into the value of 'canCreate'. It makes things much harder to understand what's going on.

Instead you should make 'readonly' its own separate key / prop

@@ -105,6 +105,10 @@
&--published::before {
display: none;
}

&.readonly {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
&.readonly {
&--readonly {

Safer to use a BEM class rather than a generic/global readonly that may accidentally get values set from somewhere else

You'll need to update this is bunch of places in this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

</div>
</Button>
{canDelete &&
{(canDelete && canCreate) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic looks wrong, shouldn't have a canCreate check for a delete button

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. I have to use readonly, since we should hide "Archive" button if user cannot edit link field.

@@ -449,6 +467,11 @@ private function ownerRelationFromRequest(): string
if (!$ownerRelation) {
$this->jsonError(404, _t(__CLASS__ . '.INVALID_OWNER_RELATION', 'Invalid ownerRelation'));
}

if (strpos($ownerRelation, '_Readonly') !== false) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we doing this with $ownerRelation? Owner relation should be what's set in the private static $has_many, not the _Readonly added to form fields when they are set to read only mode?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

private function isReadOnlyField(): bool
{
$request = $this->getRequest();
$ownerClass = $request->getVar('ownerClass') ?: $request->postVar('OwnerClass');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a new private method getOwnerClassFromRequest() so that it's consistent with the rest of the file and throw a 404 error if the owner class !is_a DataObject

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

$request = $this->getRequest();
$ownerClass = $request->getVar('ownerClass') ?: $request->postVar('OwnerClass');
$ownerRelation = $this->ownerRelationFromRequest();
$isReadOnly = Injector::inst()->get($ownerClass)->getCMSFields()->dataFieldByName($ownerRelation)?->isReadonly();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$isReadOnly = Injector::inst()->get($ownerClass)->getCMSFields()->dataFieldByName($ownerRelation)?->isReadonly();
return (bool) Injector::inst()->get($ownerClass)->getCMSFields()->dataFieldByName($ownerRelation)?->isReadonly();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't take into account scenarios where the form was set to readonly, which I believe is what happens when the user doesn't have edit permission for the owner record (e.g. a page).

Comment on lines 333 to 334

return $isReadOnly ?? false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return $isReadOnly ?? false;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

@sabina-talipova sabina-talipova force-pushed the pulls/4/readonly-link branch 4 times, most recently from 7ea3d91 to 512f05b Compare January 17, 2024 23:39
Copy link
Member

@emteknetnz emteknetnz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still able to sort links in a MultiLinkField when it's in a readonly state

Also needs a rebase

$form->makeReadonly();
$form->setMessage(_t(__CLASS__ . '.READ_ONLY_MESSAGE', 'You do not have enough permissions to edit this form'), 'info');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should be setting a form message here. Can you find any other examples of us doing this?

If we do keep it, the message is wrong when the form has been manually set to read only rather than because of a lack of permissions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emteknetnz, I could not find any similar example. I think it was new requirements from Design team. I moved logic only for readonly field.

@@ -89,3 +91,17 @@ test('LinkPickerTitle main button should not fire the onClick callback while loa
fireEvent.click(container.querySelector('button.link-picker__button'));
expect(mockOnClick).toHaveBeenCalledTimes(0);
});

test('LinkPickerTitle render() should have readonly class if cannot edit', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test('LinkPickerTitle render() should have readonly class if cannot edit', () => {
test('LinkPickerTitle render() should have readonly class if set to readonly', () => {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

expect(container.querySelectorAll('.link-picker__link--readonly')).toHaveLength(1);
});

test('LinkPickerTitle render() should not have readonly class if can edit', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
test('LinkPickerTitle render() should not have readonly class if can edit', () => {
test('LinkPickerTitle render() should not have readonly class if set to readonly', () => {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

@sabina-talipova sabina-talipova force-pushed the pulls/4/readonly-link branch 5 times, most recently from 1994149 to 2192224 Compare January 23, 2024 03:48
$form->makeReadonly();
}

if ($this->isReadOnlyField()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message feels even more out of place now

It will now only show if $this->isReadOnlyField() is explicitly called, though not if $this->canEdit() is false or if the owner page/element canEdit() returns false which seems worse because it's inconsistent.

We also don't show this message anywhere else in the CMS as far as we know

I think we should just remove this message, it's seems pretty obvious that the form is readonly because the background of all the form elements if grey

Perhaps raise a new card to add this to globally add this message to forms through the CMS instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

Copy link
Member

@emteknetnz emteknetnz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Merge conflicts, needs a rebase + run yarn build

@emteknetnz emteknetnz merged commit 9bb3a1a into silverstripe:4 Jan 24, 2024
10 checks passed
@emteknetnz emteknetnz deleted the pulls/4/readonly-link branch January 24, 2024 01:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants