Skip to content
This repository was archived by the owner on Jul 29, 2022. It is now read-only.

Commit ca783ea

Browse files
committed
refactor button/url - removes href
1 parent 10b90db commit ca783ea

File tree

22 files changed

+415
-403
lines changed

22 files changed

+415
-403
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
help:
2+
echo
3+
4+
demo:
5+
cd tests && ./manage.py migrate
6+
cd tests && ./manage.py runserver
17

28
develop:
39
python3 -m venv .venv

src/admin_extra_urls/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
NAME = "django-admin-extra-urls"
2-
VERSION = __version__ = "3.5.1"
1+
NAME = 'django-admin-extra-urls'
2+
VERSION = __version__ = '3.5.1'
33
__author__ = 'sax'

src/admin_extra_urls/api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
# from .config import ButtonAction # noqa: F401
2-
from .decorators import action, button, href, try_catch # noqa: F401
3-
from .mixins import ExtraUrlMixin # noqa: F401
1+
from .button import UrlButton # noqa: F401
2+
from .decorators import button, href, link, url # noqa: F401
3+
from .mixins import ExtraUrlMixin, confirm_action # noqa: F401

src/admin_extra_urls/button.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import logging
2+
from django.urls import NoReverseMatch, reverse
3+
4+
from admin_extra_urls.utils import (check_permission,
5+
get_preserved_filters, labelize,)
6+
7+
logger = logging.getLogger(__name__)
8+
9+
10+
class UrlHandler:
11+
def __init__(self):
12+
pass
13+
14+
15+
class BaseExtraButton:
16+
default_css_class = 'btn disable-on-click auto-disable'
17+
18+
def __init__(self, **options):
19+
self.options = options
20+
# self.options.setdefault('html_attrs', {})
21+
# self.options.setdefault('display', lambda context: True)
22+
self.options.setdefault('icon', None)
23+
self.options.setdefault('change_list', False)
24+
self.options.setdefault('change_form', False)
25+
self.options.setdefault('url_name', None)
26+
self.options.setdefault('permission', None)
27+
self.options.setdefault('url', None)
28+
# self.options.setdefault('label', self.options['url'])
29+
# self.options.setdefault('name', None)
30+
self.options.setdefault('visible', True)
31+
# self.order = order
32+
# if display := options.get('display', empty) != empty:
33+
# self.display = (lambda context: True) if options.get('display') is None else options.get('display')
34+
# self.change_form = change_form
35+
# self.change_list = change_list
36+
# self.url_name = url_name
37+
# self._url = url
38+
self.context = None
39+
self.html_attrs = self.options.get('html_attrs', {})
40+
41+
def __repr__(self):
42+
return f'<ExtraButton {self.options}>'
43+
44+
def __html__(self):
45+
return f'[ExtraButton {self.options}]'
46+
47+
def __copy__(self):
48+
return type(self)(**self.options)
49+
50+
def __getattr__(self, item):
51+
if item in self.options:
52+
return self.options.get(item)
53+
raise ValueError(f'{item} is not a valid attribute for {self}')
54+
55+
def visible(self):
56+
# if self.details and not self.original:
57+
# return False
58+
if callable(self.options['visible']):
59+
return self.options['visible'](self.context)
60+
return self.options['visible']
61+
62+
def authorized(self):
63+
if self.permission:
64+
return check_permission(self.permission, self.request, self.original)
65+
return True
66+
67+
@property
68+
def request(self):
69+
if not self.context:
70+
raise ValueError(f"You need to call bind() to access 'request' on {self}")
71+
return self.context['request']
72+
73+
@property
74+
def original(self):
75+
if not self.context:
76+
raise ValueError(f"You need to call bind() to access 'original' on {self}")
77+
return self.context.get('original', None)
78+
79+
def bind(self, context):
80+
self.context = context
81+
# this is only for backward compatibility
82+
if self.name and 'id' not in self.html_attrs:
83+
self.html_attrs['id'] = f'btn-{self.name}'
84+
return self
85+
86+
87+
class UrlButton(BaseExtraButton):
88+
def __init__(self, **options):
89+
super().__init__(**options)
90+
91+
def label(self):
92+
return self.options.get('label', labelize(self.options.get('name', '')))
93+
94+
@property
95+
def url(self):
96+
if self.options.get('url'):
97+
return self.options.get('url')
98+
try:
99+
if self.original:
100+
url = reverse(f'admin:{self.url_name}', args=[self.original.pk])
101+
else:
102+
url = reverse(f'admin:{self.url_name}')
103+
filters = get_preserved_filters(self.request)
104+
return f'{url}?{filters}'
105+
except NoReverseMatch as e:
106+
logger.exception(e)
107+
return f'javascript:alert("{e}")'
108+
109+
110+
class Button(BaseExtraButton):
111+
def __init__(self, **options):
112+
super().__init__(**options)
113+
self._params = {}
114+
115+
def label(self):
116+
return self.options.get('label', str(self.url))
117+
118+
@property
119+
def url(self):
120+
if isinstance(self._params, dict):
121+
return self.options.get('url').format(**self._params)
122+
else:
123+
return self._params
124+
125+
def bind(self, context):
126+
self.context = context
127+
if 'func' in self.options:
128+
self._params = (self.func(self) or {})

src/admin_extra_urls/checks.py

Whitespace-only changes.

src/admin_extra_urls/config.py

Lines changed: 10 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,15 @@
1-
from django.contrib.admin.templatetags.admin_urls import admin_urlname
2-
from django.urls import reverse
31

4-
from admin_extra_urls.utils import get_preserved_filters, safe, Display
5-
6-
empty = object()
7-
8-
9-
class Button:
10-
def __init__(self, path, *, label=None, icon='', permission=None,
11-
css_class="btn btn-success disable-on-click", order=999, visible=empty,
12-
modeladmin=None, display=Display.NOT_SET, group=None,
13-
details=True, urls=None):
2+
class UrlConfig:
3+
def __init__(self, *, func=None, path=None, button=None,
4+
details=False, permission=None, object_id_arg_name='object_id', **extra):
5+
self.func = func
6+
self.method = func.__name__
147
self.path = path
15-
self.label = label or path
16-
17-
self.icon = icon
18-
self.display = display
19-
self._perm = permission
20-
self.order = order
21-
self.css_class = css_class
22-
self.group = group
23-
self._visible = visible
24-
self._bound = False
8+
self.permission = permission
259
self.details = details
26-
self.urls = urls
27-
self.modeladmin = modeladmin
10+
self.button = button
11+
self.object_id_arg_name = object_id_arg_name
12+
self.extra = extra
2813

2914
def __repr__(self):
30-
return f"<{self.__class__.__name__} '{self.label}' object at {id(self)}>"
31-
def bind(self, context):
32-
self.context = context
33-
obj = context.get('original', None)
34-
try:
35-
request = context['request']
36-
except KeyError:
37-
raise KeyError("Cannot find 'request' in context. "
38-
"Be sure you have 'django.template.context_processors.request' in"
39-
"your templates context_processors")
40-
user = request.user
41-
groups = context.get('aeu_groups', ['None'])
42-
self.querystring = get_preserved_filters(request)
43-
if callable(self._visible):
44-
self.visible = safe(self._visible, obj, request)
45-
else:
46-
self.visible = self._visible
47-
if self.visible and str(self.group) not in groups:
48-
self.visible = False
49-
50-
if self._perm is None:
51-
self.authorized = True
52-
elif callable(self._perm):
53-
self.authorized = self._perm(request, obj)
54-
else:
55-
self.authorized = user.has_perm(self._perm)
56-
57-
58-
class ButtonHREF(Button):
59-
def __init__(self, func, *, path=None, label=None, icon='', permission=None,
60-
css_class="btn btn-success", order=999, visible=empty, group=None,
61-
modeladmin=None, details=True, html_attrs=None, display=Display.NOT_SET):
62-
self.func = func
63-
self.html_attrs = html_attrs
64-
self.callback_parameter = None
65-
super().__init__(path=path, label=label, icon=icon, permission=permission,
66-
css_class=css_class, order=order, visible=visible, group=group,
67-
modeladmin=modeladmin, details=details, display=display)
68-
69-
def __repr__(self):
70-
return f"<ButtonHREF {self.label} {self.func.__name__}>"
71-
72-
def bind(self, context):
73-
super().bind(context)
74-
self.callback_parameter = self.func(self)
75-
76-
def url(self):
77-
if isinstance(self.callback_parameter, dict):
78-
return self.path.format(**self.callback_parameter)
79-
else:
80-
return self.callback_parameter
81-
82-
83-
class ButtonAction(Button):
84-
def __init__(self, func, **kwargs):
85-
self.func = func
86-
super().__init__(**kwargs)
87-
self.path = self.path or func.__name__
88-
self.method = func.__name__
89-
90-
def url(self):
91-
opts = self.context['opts']
92-
if self.details:
93-
base_url = reverse(admin_urlname(opts, self.method),
94-
args=[self.context['original'].pk])
95-
else:
96-
base_url = reverse(admin_urlname(opts, self.method))
97-
return "%s?%s" % (base_url, self.querystring)
15+
return f'<UrlConfig func:"{self.func}" path:"{self.path}" button:"{self.button}">'

0 commit comments

Comments
 (0)