Skip to content

Commit

Permalink
Merge branch 'main' of github.com:basxsoftwareassociation/bread
Browse files Browse the repository at this point in the history
  • Loading branch information
saemideluxe committed Aug 17, 2021
2 parents 53609d6 + 77b9f7e commit 5f9abef
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 89 deletions.
34 changes: 34 additions & 0 deletions .github/workflows/automated_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Automated Release

on:
push:
tags: [ "v*" ]

workflow_dispatch:

jobs:
tagged-release:
if: github.event.base_ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine check dist/*
twine upload dist/*
20 changes: 4 additions & 16 deletions bread/contrib/reports/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,25 +167,13 @@ def exceldownload(request, pk: int):
),
],
rowactions=[
menu.Action(
js=hg.BaseElement(
"document.location = '",
hg.F(lambda c: _layout.objectaction(c["row"], "edit")),
"'",
),
Link(
href=ModelHref(Report, "edit", kwargs={"pk": hg.C("row.pk")}),
iconname="edit",
label=_("Edit"),
),
menu.Action(
js=hg.BaseElement(
"document.location = '",
hg.F(
lambda c: urls.reverse_model(
Report, "excel", kwargs={"pk": c["row"].pk}
)
),
"'",
),
Link(
href=ModelHref(Report, "excel", kwargs={"pk": hg.C("row.pk")}),
iconname="download",
label=_("Excel"),
),
Expand Down
42 changes: 20 additions & 22 deletions bread/layout/components/datatable.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from bread.menu import Action
from bread.utils import filter_fieldlist, pretty_modelname, resolve_modellookup
from bread.utils.links import Link
from bread.utils.urls import link_with_urlparameters, reverse_model
Expand Down Expand Up @@ -110,7 +109,7 @@ def with_toolbar(
primary_button=None,
searchurl=None,
query_urlparameter=None,
bulkactions=(),
bulkactions: List[Link] = (),
pagination_options=(),
paginator=None,
page_urlparameter="page",
Expand All @@ -125,7 +124,7 @@ def with_toolbar(
primary_button: bread.layout.button.Button instance
searchurl: url to which values entered in the searchfield should be submitted
query_urlparameter: name of the query field for the searchurl which contains the entered text
bulkactions: List of bread.menu.Action or bread.menu.Link instances bread.menu.Link will send a post or a get (depending on its "method" attribute) to the target url the sent data will be a form with the selected checkboxes as fields if the head-checkbox has been selected only that field will be selected
bulkactions: List of bread.utils.links.Link instances. Will send a post or a get (depending on its "method" attribute) to the target url the sent data will be a form with the selected checkboxes as fields if the head-checkbox has been selected only that field will be selected
"""
checkboxallid = f"datatable-check-{hg.html_id(self)}"
header = [hg.H4(title, _class="bx--data-table-header__title")]
Expand All @@ -135,23 +134,18 @@ def with_toolbar(
)
resultcontainerid = f"datatable-search-{hg.html_id(self)}"
bulkactionlist = []
for action in bulkactions:
if isinstance(action, Link):
action = Action(
js=hg.BaseElement(
for link in bulkactions:
bulkactionlist.append(
Button(
link.label,
iconname=link.iconname,
onclick=hg.BaseElement(
"submitbulkaction(this.closest('[data-table]'), '",
action.href,
f"', method='{getattr(action, 'method', 'GET')}')",
link.href,
"', method='GET')",
),
label=action.label,
iconname=action.iconname,
permissions=action.permissions,
)
bulkactionlist.append(Button.fromaction(action))
elif isinstance(action, Action):
bulkactionlist.append(Button.fromaction(action))
else:
RuntimeError(f"bulkaction needs to be {Action} or {Link}")
),
)

if bulkactions:
self.head.insert(
Expand Down Expand Up @@ -303,9 +297,9 @@ def from_model(
model,
queryset=None,
columns: Union[List[str], List[DataTableColumn]] = None,
rowactions=None,
rowactions: List[Link] = (),
rowactions_dropdown=False,
bulkactions=(),
bulkactions: List[Link] = (),
title=None,
addurl=None,
backurl: Union[hg.Lazy, str] = None,
Expand Down Expand Up @@ -375,7 +369,7 @@ def from_model(
rowactions,
"action",
hg.F(
lambda c: Button.fromaction(
lambda c: Button.fromlink(
c["action"],
notext=True,
small=True,
Expand Down Expand Up @@ -421,7 +415,11 @@ def from_model(
"",
objectactions_menu,
td_attributes=hg.F(
lambda c: {"_class": "bx--table-column-menu"}
lambda c: {
"_class": "bx--table-column-menu"
if rowactions_dropdown
else ""
}
),
th_attributes=hg.F(
lambda c: {"_class": "bx--table-column-menu"}
Expand Down
40 changes: 0 additions & 40 deletions bread/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,43 +117,3 @@ def registeritem(item):

# global main menu
main = Menu()

# TODO: I think we should rethink this concept of Action
# Thoughts:
# Partly we replaced the original Link class (inheriting from ACtion)
# with the new bread.utils.links.Link class in order to have real
# support for html a href links. (The old menu.Link class here was using
# javascript with "document.location = ..." which is a really unclean
# solution for "real" links)
# What we still need is a concept of UI-actions that cannot be represented with
# a href link but require a javascript action.
# This Action class fullfills that purpose somewhat but it should at least go
# into another module and we need to add support to e.g. buttons to "attach"
# an action or create a button out of an action...
# Maybe this class is even completely obsolete. Everything here can be done with
# bread.layout.button.Button(label, icon=iconname, onclick=js)
# Be aware: This class is used in quite a few places, including datatables
# Refactoring this (out) is not trivial. But I think worthwhile, because having an easy
# and consisten way of adding custom "actions" or behaviour to the UI is a very frequent
# task.


class Action:
"""Represents a user-clickable action
js, label, icon and permissions can be str, lazy string or a callable function.
The function takes the current request as the only argument.
"""

def __init__(self, js, label="", iconname=None, permissions=[]):
self.js = js
self.label = label
self.iconname = iconname
self._permissions = permissions

def has_permission(self, request, obj=None):
return all(
[
request.user.has_perm(perm, obj) or request.user.has_perm(perm)
for perm in try_call(self._permissions, request)
]
)
22 changes: 16 additions & 6 deletions bread/utils/links.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import NamedTuple, Union
from typing import List, NamedTuple, Union

import htmlgenerator as hg
from django.db import models
Expand All @@ -23,10 +23,18 @@ def __init__(self, *args, **kwargs):
self.kwargs = kwargs

def resolve(self, context: dict):
return urlreverse(
*[hg.resolve_lazy(a, context) for a in self.args],
**{k: hg.resolve_lazy(v, context) for k, v in self.kwargs.items()}
)
kwargs = {k: hg.resolve_lazy(v, context) for k, v in self.kwargs.items()}
# the django reverse function requires url-keyword arguments to be pass in a parameter named
# "kwarg". This is a bit confusing since kwargs normally referse to the python keyword arguments
# and not to URL keyword arguments. However, we also want to support lazy URL keywords, so we do the
# resolving of the actualy URL-kwargs as well
if "kwargs" in kwargs:
kwargs["kwargs"] = {
k: hg.resolve_lazy(v, context) for k, v in kwargs["kwargs"].items()
}
if "args" in kwargs:
kwargs["args"] = [hg.resolve_lazy(arg, context) for arg in kwargs["args"]]
return urlreverse(*[hg.resolve_lazy(a, context) for a in self.args], **kwargs)


class ModelHref(LazyHref):
Expand All @@ -45,6 +53,8 @@ class ModelHref(LazyHref):
"""

def __init__(self, model, name, *args, **kwargs):
# if this is an instance of a model, we can extract the pk URL argument directly
# TODO: instance-specific routes which don't use the pk argument will fail
if isinstance(model, models.Model) and "pk" not in kwargs.get("kwargs", {}):
if "kwargs" not in kwargs:
kwargs["kwargs"] = {}
Expand All @@ -60,7 +70,7 @@ class Link(NamedTuple):
href: Union[str, LazyHref]
label: str
iconname: str = "fade"
permissions: list[str] = []
permissions: List[str] = []

def has_permission(self, request, obj=None):
return all(
Expand Down
4 changes: 2 additions & 2 deletions bread/views/browse.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Callable, NamedTuple, Optional
from typing import Callable, List, NamedTuple, Optional

import htmlgenerator as hg
from django.conf import settings
Expand Down Expand Up @@ -30,7 +30,7 @@ class BulkAction(NamedTuple):
label: str
action: Callable[[HttpRequest, models.query.QuerySet], Optional[HttpResponse]]
iconname: str = "fade"
permissions: list[str] = []
permissions: List[str] = []

def has_permission(self, request, obj=None):
return all(
Expand Down
9 changes: 6 additions & 3 deletions bread/views/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@ def layoutasreadonly(layout):
[isinstance(a, _layout.form.Form) for a in ancestors]
)
and (
isinstance(
element,
hg.BUTTON,
(
isinstance(
element,
hg.BUTTON,
)
and "bx--tag" not in element.attributes.get("_class", "")
)
or getattr(element, "attributes", {}).get("type") == "submit"
)
Expand Down

0 comments on commit 5f9abef

Please sign in to comment.