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

EZP-29179: Implement permissions for "Content/Hide" #538

Merged
merged 5 commits into from Jun 28, 2018

Conversation

mikadamczyk
Copy link
Contributor

@mikadamczyk mikadamczyk commented Jun 26, 2018

Question Answer
Tickets https://jira.ez.no/browse/EZP-29179
Bug fix? no
New feature? yes
BC breaks? no
Tests pass? yes
Doc needed? no
License GPL-2.0

In addition we added ajax request to change location visibility

screen shot 2018-06-26 at 1 18 11 pm

Checklist:

  • Coding standards ($ composer fix-cs)
  • Ready for Code Review

@lserwatka
Copy link
Member

@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;
Copy link
Contributor

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?

Copy link
Contributor

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) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

({ target }) => {

Copy link
Contributor

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) => {
Copy link
Contributor

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.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, it's intentional.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please, use binding instead.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why?

Copy link
Contributor

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 || {};
Copy link
Member

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?

Copy link
Contributor

@tischsoic tischsoic Jun 27, 2018

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 CustomEvents but not dispatching it to the DOM.

Copy link
Contributor

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.

Copy link
Contributor

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) {
Copy link
Member

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? :)

Copy link
Contributor

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.

Copy link
Member

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))
Copy link
Member

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)?

Copy link
Contributor

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.

Copy link
Member

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.

Copy link
Contributor

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.

Copy link
Contributor

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. :)

Copy link
Member

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:
Copy link
Member

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.

Copy link
Member

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) {
Copy link
Member

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 {
Copy link
Member

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']
Copy link
Member

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
Copy link
Member

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:
Copy link
Member

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);
Copy link
Member

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.


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) => {
Copy link
Contributor

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) => {
Copy link
Contributor

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) => {
Copy link
Contributor

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 }) => {

Copy link
Contributor

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))
Copy link
Contributor

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,
Copy link
Contributor

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

Copy link
Contributor

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.

Copy link
Contributor

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 || {};
Copy link
Contributor

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.

@lserwatka
Copy link
Member

@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();
Copy link
Member

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'
Copy link
Member

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({
Copy link
Member

Choose a reason for hiding this comment

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

?!

@lserwatka
Copy link
Member

@dew326 @sunpietro can we merge it now?

@dew326
Copy link
Member

dew326 commented Jun 27, 2018

Not with jQuery used for ajax request.

@tischsoic
Copy link
Contributor

I removed jQuery in last commit. @dew326

Copy link
Contributor

@sunpietro sunpietro left a 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);
Copy link
Contributor

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);
Copy link
Contributor

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;
Copy link
Member

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.

@lserwatka lserwatka merged commit 144397d into ezsystems:master Jun 28, 2018
mateuszdebinski pushed a commit that referenced this pull request Oct 25, 2022
Co-authored-by: Michał Grabowski <michal.grabowski@ibexa.co>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
7 participants