Skip to content

Commit

Permalink
Merge pull request #18 from jokiefer/16_improvements_for_first_release
Browse files Browse the repository at this point in the history
16 improvements for first release
  • Loading branch information
jokiefer authored Dec 22, 2020
2 parents e3d1793 + 4428dfb commit d2bd018
Show file tree
Hide file tree
Showing 71 changed files with 434 additions and 518 deletions.
375 changes: 235 additions & 140 deletions django_bootstrap_swt/components.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions django_bootstrap_swt/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,4 @@ class DataToggleEnum(Enum):
COLLAPSE = "collapse"
MODAL = "modal"
DROPDOWN = "dropdown"
TOOLTIP = "tooltip"

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1 +1 @@
<{{tag}} {% if additional_classes %}class="{% for cls in additional_classes %}{{cls}} {% endfor %}"{% endif %}>{% if content %}{{content|safe}}{% endif %}</{{tag}}>
<{{tag}}{% for attr, values in attrs.items %} {{ attr }}{% if values %}="{% for value in values%}{{value}}{% if not forloop.last %} {% endif %}{% endfor %}"{% endif %}{% endfor %}>{% if content %}{{content|safe}}{% endif %}</{{tag}}>

This file was deleted.

20 changes: 12 additions & 8 deletions django_bootstrap_swt/utils.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
from urllib import parse
from django_bootstrap_swt.components import BootstrapComponent
from django_bootstrap_swt.components import BootstrapComponent, Tag


class RenderHelper:
"""
This class provides some functions for permission checked rendering.
"""
def __init__(self, user_permissions: [str] = None, update_url_qs: dict = None):
def __init__(self, user_permissions: [str] = None, update_url_qs: dict = None, update_attrs: dict = None):
"""
:param user_permissions: a list which holds all user permission codenames
:param update_url_qs: a dict which contains the key:value pairs to update the query of url items
:param update_attrs: Optional: the dict with the update key value pairs. Value shall be a list
"""
self.user_permissions = user_permissions if user_permissions else []
self.update_url_qs = update_url_qs
self.update_attrs = update_attrs

def _check_render_permission(self, item: BootstrapComponent) -> bool:
if not item.needs_perm or self.user_permissions and item.needs_perm in self.user_permissions:
Expand All @@ -26,15 +28,15 @@ def update_queryparams(self, item: BootstrapComponent):
:param item: the BootstrapComponent to update
:return: the updated item
"""
if hasattr(item, 'url'):
url_parts = list(parse.urlparse(item.url))
if hasattr(item, 'attrs') and 'href' in item.attrs:
url_parts = list(parse.urlparse(item.attrs['href'][0]))
query = dict(parse.parse_qsl(url_parts[4]))
query.update(self.update_url_qs)
url_parts[4] = parse.urlencode(query)
item.url = parse.urlunparse(url_parts)
item.attrs['href'] = [parse.urlunparse(url_parts)]
return item

def render_item(self, item: BootstrapComponent, safe: bool = False) -> str:
def render_item(self, item, safe: bool = False) -> str:
"""
Use this function to render one item based on the self.user_permissions list. The item.needs_perm attribute
value will be checked against the self.user_permissions list. If the needs_perm attribute value is not
Expand All @@ -49,17 +51,19 @@ def render_item(self, item: BootstrapComponent, safe: bool = False) -> str:
if self._check_render_permission(item):
if self.update_url_qs:
self.update_queryparams(item=item)
if self.update_attrs:
item.update_attributes(update_attrs=self.update_attrs)
rendered_string = item.render(safe=safe)
return rendered_string

def render_list_coherent(self, items: [BootstrapComponent], safe: bool = False) -> str:
def render_list_coherent(self, items: [], safe: bool = False) -> str:
"""
Use this function to render a list of items based on the self.user_permissions list. All items which needs
permission will be checked against the self.user_permissions list. If the needs_perm attribute value is not
in the self.user_permissions list, the item will not be rendered and concatenated.
:param items: the list of BootstrapComponent which shall be rendered
:param safe: switches if the rendered component is returned as SafeString or str
:param safe: Optional: switches if the rendered component is returned as SafeString or str
:return: empty string if the user does not have the right permissions for any item |
the concatenated string with all rendered items for that the user has permissions
"""
Expand Down
61 changes: 57 additions & 4 deletions docs/pages/examples.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
********
Examples
~~~~~~~~
********

This shows how you can use the django-bootstrap-swt app by some examples.

Expand All @@ -13,6 +14,9 @@ For our examples we use the following models::
name = models.CharField(max_length=100, verbose_name="product name")
order = models.ForeignKey(Order, related_name="items")


Use-Case: Provide action buttons for models.
###########################################
So what if you like to provide the action buttons for all you can do with this models?

For this use case you can write a function under the model classes which returns the action buttons::
Expand All @@ -22,18 +26,21 @@ For this use case you can write a function under the model classes which returns
...
def get_action_buttons(self):
actions[LinkButton(url=self.edit_view_uri,
value='<i class="fas fa-edit"></i>',
content='<i class="fas fa-edit"></i>',
color=ButtonColorEnum.WARNING,
tooltip=_l(f"Edit <strong>{self.name} [{self.id}]</strong> Order."),
needs_perm=PermissionEnum.CAN_EDIT_ORDER.value)),
LinkButton(url=self.delete_view_uri,
value='<i class="fas fa-trash-alt"></i>',
content='<i class="fas fa-trash-alt"></i>',
color=ButtonColorEnum.DANGER,
tooltip=_l(f"Delete <strong>{self.name} [{self.id}]</strong> Order."),
needs_perm=PermissionEnum.CAN_DELETE_ORDER.value)),
]

On any view you then can call the `order.get_action_buttons()` function to get all possible actions.

Use-Case: Only renders buttons for that the user has permissions.
#################################################################
The next thing is, how can i only display the actions the user has permissions for?

For this use case you can use the `RenderHelper` class::
Expand All @@ -49,7 +56,7 @@ For this use case you can use the `RenderHelper` class::
for permission in permissions:
user_permissions.append(permission.codename)

render_helper = RenderHelper(request=self.request, user_permissions=user_permissions)
render_helper = RenderHelper(user_permissions=user_permissions)

for order in context['object_list']:
order.actions = render_helper.render_list_coherent(items=order.get_action_buttons())
Expand All @@ -62,3 +69,49 @@ Now the rendered buttons are stored in your context and you can use them in your
{{object.actions|safe}}
{% endfor %}

Use-Case: Update some attributes of the html component before rendering.
########################################################################

Sometimes you don't want to specify some attributes of a component at constructing time.

For this use case you can use again the `RenderHelper` class:

1. Update url query parameters.
If you want to add or update some query parameters of your Action buttons you can do it with the following::

class OrderListView(ListView):
model = Order
...
def get_context_data(self, **kwargs):
...

update_url_qs_dict = {'key-1': 'value-1', 'key-2': 'value-2'}

render_helper = RenderHelper(user_permissions=user_permissions, update_url_qs=update_url_qs_dict)

for order in context['object_list']:
order.actions = render_helper.render_list_coherent(items=order.get_action_buttons())

return context


All elements with an `href` attribute are updated like `http://example.com?key-1=value-1&key-2=value-2`.

2. Update the html attributes.
You can also update any html attribute with the `RenderHelper`. For example you want to change the button size on every view::

class OrderListView(ListView):
model = Order
...
def get_context_data(self, **kwargs):
...

update_attrs = {'class': ['btn-sm']}

render_helper = RenderHelper(user_permissions=user_permissions, update_attrs=update_attrs)

for order in context['object_list']:
order.actions = render_helper.render_list_coherent(items=order.get_action_buttons())

return context

Original file line number Diff line number Diff line change
@@ -1,14 +1 @@
<div class="accordion" id="{{accordion_id}}" >
<div class="card">
<div id="{{card_header_id}}" class="card-header">
<div class="row "><div class="col-sm text-left "><button id="{{button_id}}" class="btn collapsed accordion text-left" type="button" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}">
<i class="fa" aria-hidden="true"></i> nice button
</button></div></div>
</div>
<div id="{{card_body_id}}" class="card-body bg-success collapse" data-parent="#{{accordion_id}}" aria-labelledby="{{card_header_id}}">
{% if True %}{% endif %}
</div>
{% if True %}{% endif %}
</div>

</div>
<div id="{{accordion_id}}" class="accordion"><div class="card"><div id="{{card_header_id}}" class="card-header"><div class="row"><div class="col-sm text-left"><button id="{{button_id}}" type="button" class="btn collapsed accordion text-left" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}"><i class="fa" aria-hidden="true"></i> nice button</button></div></div></div><div id="{{card_body_id}}" class="card-body bg-success collapse" data-parent="#{{accordion_id}}" aria-labelledby="{{card_header_id}}"></div></div></div>
Original file line number Diff line number Diff line change
@@ -1,14 +1 @@
<div class="accordion" id="{{accordion_id}}" >
<div class="card">
<div id="{{card_header_id}}" class="card-header">
<div class="row "><div class="col-sm text-left "><button id="{{button_id}}" class="btn collapsed accordion text-left" type="button" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}">
<i class="fa" aria-hidden="true"></i> nice button
</button></div></div>
</div>
<div id="{{card_body_id}}" class="card-body border-success collapse" data-parent="#{{accordion_id}}" aria-labelledby="{{card_header_id}}">
{% if True %}{% endif %}
</div>
{% if True %}{% endif %}
</div>

</div>
<div id="{{accordion_id}}" class="accordion"><div class="card"><div id="{{card_header_id}}" class="card-header"><div class="row"><div class="col-sm text-left"><button id="{{button_id}}" type="button" class="btn collapsed accordion text-left" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}"><i class="fa" aria-hidden="true"></i> nice button</button></div></div></div><div id="{{card_body_id}}" class="card-body border-success collapse" data-parent="#{{accordion_id}}" aria-labelledby="{{card_header_id}}"></div></div></div>
Original file line number Diff line number Diff line change
@@ -1,14 +1 @@
<div class="accordion" id="{{accordion_id}}" >
<div class="card">
<div id="{{card_header_id}}" class="card-header">
<div class="row "><div class="col-sm text-left "><button id="{{button_id}}" class="btn collapsed accordion text-left" type="button" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}">
<i class="fa" aria-hidden="true"></i> nice button
</button></div></div>
</div>
<div id="{{card_body_id}}" class="card-body text-success collapse" data-parent="#{{accordion_id}}" aria-labelledby="{{card_header_id}}">
{% if True %}{% endif %}
</div>
{% if True %}{% endif %}
</div>

</div>
<div id="{{accordion_id}}" class="accordion"><div class="card"><div id="{{card_header_id}}" class="card-header"><div class="row"><div class="col-sm text-left"><button id="{{button_id}}" type="button" class="btn collapsed accordion text-left" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}"><i class="fa" aria-hidden="true"></i> nice button</button></div></div></div><div id="{{card_body_id}}" class="card-body text-success collapse" data-parent="#{{accordion_id}}" aria-labelledby="{{card_header_id}}"></div></div></div>
Original file line number Diff line number Diff line change
@@ -1,14 +1 @@
<div class="accordion" id="{{accordion_id}}" >
<div class="card">
<div id="{{card_header_id}}" class="card-header">
<div class="row "><div class="col-sm text-left "><button id="{{button_id}}" class="btn collapsed accordion text-left" type="button" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}">
<i class="fa" aria-hidden="true"></i> nice button
</button></div></div>
</div>
<div id="{{card_body_id}}" class="card-body collapse" data-parent="#{{accordion_id}}" data-url="http://example.com" aria-labelledby="{{card_header_id}}">
{% if True %}{% endif %}
</div>
{% if True %}{% endif %}
</div>

</div>
<div id="{{accordion_id}}" class="accordion"><div class="card"><div id="{{card_header_id}}" class="card-header"><div class="row"><div class="col-sm text-left"><button id="{{button_id}}" type="button" class="btn collapsed accordion text-left" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}"><i class="fa" aria-hidden="true"></i> nice button</button></div></div></div><div id="{{card_body_id}}" class="card-body collapse" data-url="http://example.com" data-parent="#{{accordion_id}}" aria-labelledby="{{card_header_id}}"></div></div></div>
Original file line number Diff line number Diff line change
@@ -1,14 +1 @@
<div class="accordion" id="{{accordion_id}}" >
<div class="card">
<div id="{{card_header_id}}" class="card-header bg-success">
<div class="row "><div class="col-sm text-left "><button id="{{button_id}}" class="btn collapsed accordion text-left" type="button" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}">
<i class="fa" aria-hidden="true"></i> nice button
</button></div></div>
</div>
<div id="{{card_body_id}}" class="card-body collapse" data-parent="#{{accordion_id}}" aria-labelledby="{{card_header_id}}">
{% if True %}{% endif %}
</div>
{% if True %}{% endif %}
</div>

</div>
<div id="{{accordion_id}}" class="accordion"><div class="card"><div id="{{card_header_id}}" class="card-header bg-success"><div class="row"><div class="col-sm text-left"><button id="{{button_id}}" type="button" class="btn collapsed accordion text-left" data-toggle="collapse" data-target="#{{card_body_id}}" aria-expanded="false" aria-controls="{{card_body_id}}"><i class="fa" aria-hidden="true"></i> nice button</button></div></div></div><div id="{{card_body_id}}" class="card-body collapse" data-parent="#{{accordion_id}}" aria-labelledby="{{card_header_id}}"></div></div></div>
Loading

0 comments on commit d2bd018

Please sign in to comment.