potiuk commented on PR #70243:
URL: https://github.com/apache/airflow/pull/70243#issuecomment-5151796788

   The feature makes sense — having to bounce back to Show User just to reset a 
password is a genuine annoyance — and I checked the part that matters most 
here: there is no permission bypass. `lib.render_action_links` applies its own 
filter as the first thing it does:
   
   ```jinja
   {% macro render_action_links(actions, pk, modelview_name) %}
       {% set actions = actions | get_actions_on_show(modelview_name) %}
   ```
   
   so injecting the action into `template_args` cannot surface a button the 
user is not entitled to, and the action endpoint is separately protected. Your 
tests cover both directions, which is the right shape for this.
   
   I'd like to suggest a different approach to the template, though, because 
copying `form_vertical.html` brings a problem with it.
   
   `render_action_links` emits its own `<form id="action_form" ...>` (see 
`appbuilder/general/lib.html`). In the copied template the call sits inside the 
model `<form class="form-vertical">`, so the rendered page has a form nested 
inside another form. That is invalid HTML — browsers drop the inner element — 
so the button may work only incidentally, and could stop working on a browser 
or FAB version that handles it differently. FAB's own `show.html` does not hit 
this because the Show view has no surrounding form, so the pattern does not 
carry over as directly as it looks.
   
   You are right that `form_vertical.html` cannot be extended — it defines no 
blocks, which I assume is exactly why you copied it. But the *page* template 
can be: `appbuilder/general/model/edit.html` wraps the widget in `{% block 
edit_form %}`. Overriding that block puts the actions after the form rather 
than inside it:
   
   ```jinja
   {% extends "appbuilder/general/model/edit.html" %}
   {% import 'appbuilder/general/lib.html' as lib %}
   
   {% block edit_form %}
     {{ super() }}
     {% if actions %}
     <div class="well well-sm">
         {{ lib.render_action_links(actions, pk, modelview_name) }}
     </div>
     {% endif %}
   {% endblock %}
   ```
   
   and the view can pass the values straight to `render_template` rather than 
reaching into widget internals:
   
   ```python
   edit_template = "appbuilder/general/model/user_edit.html"
   
   @expose("/edit/<pk>", methods=["GET", "POST"])
   @has_access
   def edit(self, pk):
       pk = self._deserialize_pk_if_composite(pk)
       widgets = self._edit(pk)
       if not widgets:
           return self.post_edit_redirect()
       return self.render_template(
           self.edit_template,
           title=self.edit_title,
           widgets=widgets,
           related_views=self._related_views,
           actions={"resetpasswords": self.actions.get("resetpasswords")},
           pk=pk,
           modelview_name=self.__class__.__name__,
       )
   ```
   
   That drops the ~45 lines of duplicated form markup, removes the need for 
`UserEditFormWidget` altogether, and means any future FAB change to 
`form_vertical.html` is picked up automatically instead of silently drifting 
from our copy. The permission filtering is unchanged, since it is the same 
macro doing the work.
   
   Two smaller notes:
   
   The description says the new template "extends FAB's built-in 
`form_vertical.html`" — it is a copy rather than an `extends`. Worth correcting 
before merge, since the body becomes the commit message and the maintenance 
implications of the two are quite different.
   
   "Mirroring the existing `show()` override pattern" is also a slightly 
generous reading: `show()` *removes* entries FAB has already populated, whereas 
this *adds* one FAB never populates. The result is safe because of the 
macro-level filtering, but they aren't the same manoeuvre, and it's worth 
knowing the safety comes from the macro rather than from symmetry with `show()`.
   
   ---
   Drafted-by: Claude Code (Opus 5); reviewed by @potiuk before posting
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to