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
EZP-29179: Implement permissions for "Content/Hide" #538
Conversation
@sunpietro @dew326 we need frontend code review here. |
doc.querySelector('#location_update_visibility_data_hidden').checked = !event.target.checked; | ||
const onVisibilityUpdated = (event) => { | ||
const { target } = event; | ||
const { checked: isHidden } = target; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want set a default value if none exists?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I? I think that there is no default value in this case.
|
||
doc.querySelector('#location_update_visibility_data_location').value = event.target.value; | ||
doc.querySelector('#location_update_visibility_data_hidden').checked = !event.target.checked; | ||
const onVisibilityUpdated = (event) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
({ target }) => {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, remove empty line above the const onVisibilityUpdated
target.closest('.ez-checkbox-icon').classList.toggle('is-checked', isHidden); | ||
}; | ||
const handleUpdateError = global.eZ.services.notification.showErrorNotification; | ||
const handleUpdateResponse = (event) => (response) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it intentional? You created a higher order function. Function that returns function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's intentional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, use binding instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I saw comment below.
showDangerNotification(error.message); | ||
}; | ||
|
||
eZ.services = eZ.services || {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if services
is an accurate name. Maybe helpers
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about this and I actually searched for an answer on the Internet and I encountered this thread:
https://softwareengineering.stackexchange.com/questions/132067/difference-between-a-service-class-and-a-helper-class
The top answer says:
A Service class/interface provides a way of a client to interact with some functionality in the application.
and for me this code, which is sending notifications does that, "provides a way of a client to interact with some functionality in the application".
If you have better definition etc. I would gladly see it because I really want to name it correctly. :)
A helper for me would be e.g. a set of functions generating CustomEvent
s but not dispatching it to the DOM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, a service is meant to manage data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sunpietro , can you provide some reference?
.catch(handleUpdateError); | ||
}; | ||
|
||
for (const checkbox of visibilityCheckboxes) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not old fashion forEach
? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's all about expressing ideas through code. forEach
exists probably because there was no for ( . of . )
construct before in language and I think that for ( . of . )
was added partly because it is more clear. It also works with any iterable.
Moreover, it think it is good practice to avoid functions with side effects like forEach
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed in private. There is no point to avoid side effects in this case and with forEach
the code would be simpler.
|
||
[...doc.querySelectorAll('.ez-checkbox-icon__checkbox')].forEach(checkbox => checkbox.addEventListener('change', updateVisibility, false)); | ||
fetch(changeVisibilityLink, fetchInit) | ||
.then(handleUpdateResponse(event)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just handleUpdateResponse.bind(null, event)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this: handleUpdateResponse(event)
is more clear than this: handleUpdateResponse.bind(null, event)
.
In my opinion bind
don't express well intention of applying one argument to the function. Higher order functions are better IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But bind
is for applying arguments to function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a common approach in our codebase to use HoF. I'd stick with current approach and use .bind()
function instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this is some argument.
I accept it, consistency is important, but do you have any other arguments against using HoF in such cases? I am just asking from pure curiosity about good practices. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no point to create some workarounds if something is native in JS.
This is about using .bind()
vs using imitation of bind
.
@@ -479,6 +479,16 @@ ezplatform.location.update_visibility: | |||
defaults: | |||
_controller: 'EzPlatformAdminUiBundle:Location:updateVisibility' | |||
|
|||
ezplatform.location.hide: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think hide
is not an accurate name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would stick with visibility
considering action is named updateVisibility
}; | ||
const handleUpdateError = global.eZ.services.notification.showErrorNotification; | ||
const handleUpdateResponse = (event) => (response) => { | ||
if (response.status === 200) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would do it in opposite way in the if
checked if there is an error and did return
.
return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_UNAUTHORIZED); | ||
} | ||
|
||
try { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why two try/catch
blocks if you catch the same Exception in both?
@@ -479,6 +479,16 @@ ezplatform.location.update_visibility: | |||
defaults: | |||
_controller: 'EzPlatformAdminUiBundle:Location:updateVisibility' | |||
|
|||
ezplatform.location.hide: | |||
path: /location/hide/{locationId}/{hidden} | |||
methods: ['GET'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you used GET
instead of POST
for an update?
* | ||
* @return \Symfony\Component\HttpFoundation\JsonResponse | ||
*/ | ||
public function updateVisibilityAjaxAction(int $locationId, int $hidden): JsonResponse |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove Ajax
part of name.
@@ -479,6 +479,16 @@ ezplatform.location.update_visibility: | |||
defaults: | |||
_controller: 'EzPlatformAdminUiBundle:Location:updateVisibility' | |||
|
|||
ezplatform.location.hide: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would stick with visibility
considering action is named updateVisibility
@@ -485,6 +483,9 @@ public function updateVisibilityAction(Request $request): Response | |||
if ($form->isSubmitted() && $form->isValid()) { | |||
$result = $this->submitHandler->handle($form, function (LocationUpdateVisibilityData $data) use ($contentInfo) { | |||
$location = $data->getLocation(); | |||
|
|||
$this->canUserHideLocation($location); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use "breakpoint"-like methods (no return value with exception in case of failure). There is need to wrap this simple if
here.
EZP-29179: Implement permissions for "Content/Hide"
|
||
doc.querySelector('#location_update_visibility_data_location').value = event.target.value; | ||
doc.querySelector('#location_update_visibility_data_hidden').checked = !event.target.checked; | ||
const onVisibilityUpdated = (event) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, remove empty line above the const onVisibilityUpdated
target.closest('.ez-checkbox-icon').classList.toggle('is-checked', isHidden); | ||
}; | ||
const handleUpdateError = global.eZ.services.notification.showErrorNotification; | ||
const handleUpdateResponse = (event) => (response) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, use binding instead.
|
||
throw new Error(response.statusText); | ||
}; | ||
const updateVisibility = (event) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const updateVisibility = ({ value: locationId, checked: isVisible }) => {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The event
is passed to handleUpdateResponse
so it is rather impossible to write it like that.
|
||
[...doc.querySelectorAll('.ez-checkbox-icon__checkbox')].forEach(checkbox => checkbox.addEventListener('change', updateVisibility, false)); | ||
fetch(changeVisibilityLink, fetchInit) | ||
.then(handleUpdateResponse(event)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not a common approach in our codebase to use HoF. I'd stick with current approach and use .bind()
function instead.
*/ | ||
const showNotification = (detail) => { | ||
const event = new CustomEvent('ez-notify', { | ||
detail, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be in the same line with the event
variable declaration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's more, you don't even need this variable and make the whole const showNotification
function declaration in one line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const showNotification = (detail) => document.body.dispatchEvent(new CustomEvent('ez-notify', { detail }));
vs
const showNotification = (detail) => {
const event = new CustomEvent('ez-notify', { detail });
document.body.dispatchEvent(event);
};
showDangerNotification(error.message); | ||
}; | ||
|
||
eZ.services = eZ.services || {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, a service is meant to manage data.
@tischsoic @sunpietro please reach the conclusion on the implementation details in private. We MUST merge this PR today! |
'locationId' => $contentInfo->mainLocationId, | ||
'_fragment' => LocationsTab::URI_FRAGMENT, | ||
])); | ||
return new JsonResponse(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should return some success
and ideally some text for the success
notification.
@@ -110,6 +110,7 @@ | |||
|
|||
{% block javascripts %} | |||
{% javascripts | |||
'@EzPlatformAdminUiBundle/Resources/public/js/scripts/services/notification.service.js' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be in layout.html.twig
to make this global not only for content structure
.
doc.querySelector('#location_update_visibility_data_location').value = event.target.value; | ||
doc.querySelector('#location_update_visibility_data_hidden').checked = !event.target.checked; | ||
|
||
doc.querySelector('form[name="location_update_visibility_data"]').submit(); | ||
} | ||
$.ajax({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?!
@dew326 @sunpietro can we merge it now? |
Not with |
I removed jQuery in last commit. @dew326 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, there is a couple of things to improve.
} | ||
|
||
onVisibilityUpdated(event); | ||
response.json().then(handleUpdateSuccess); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should return response.json()
and then in the next then()
you should proccess this data.
throw new Error(response.statusText); | ||
} | ||
|
||
onVisibilityUpdated(event); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you handle response here, then you should call this method in the next then()
onVisibilityUpdated(event); | ||
response.json().then(handleUpdateSuccess); | ||
}; | ||
const updateVisibility = (event) => { | ||
doc.querySelector('#location_update_visibility_data_location').value = event.target.value; | ||
doc.querySelector('#location_update_visibility_data_hidden').checked = !event.target.checked; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be changed to not search in whole doc
but only on form
.
Co-authored-by: Michał Grabowski <michal.grabowski@ibexa.co>
In addition we added ajax request to change location visibility
Checklist:
$ composer fix-cs
)