PK!inline_actions/__init__.pyPK!3KKinline_actions/__init__.py.orig<<<<<<< HEAD __version__ = '2.1.0' ======= >>>>>>> build: switch to poetry PK!:  inline_actions/actions.pyfrom django.contrib import messages from django.shortcuts import redirect from django.urls import reverse from django.utils.translation import ugettext_lazy as _ class ViewAction: inline_actions = ['view_action'] def view_action(self, request, obj, parent_obj=None): """Redirect to changeform of selcted inline instance""" url = reverse( 'admin:{}_{}_change'.format( obj._meta.app_label, obj._meta.model_name, ), args=(obj.pk,) ) return redirect(url) view_action.short_description = _("View") class DeleteAction: def get_inline_actions(self, request, obj=None): actions = super().get_inline_actions(request, obj) if self.has_delete_permission(request, obj): actions.append('delete_action') return actions def delete_action(self, request, obj, parent_obj=None): """Remove selected inline instance if permission is sufficient""" if self.has_delete_permission(request): obj.delete() messages.info(request, "`{}` deleted.".format(obj)) delete_action.short_description = _("Delete") class DefaultActionsMixin(ViewAction, DeleteAction): inline_actions = [] PK!;-((inline_actions/admin.pyfrom django.apps import apps from django.contrib import admin from django.http import HttpResponse from django.shortcuts import redirect from django.urls import reverse from django.utils.safestring import mark_safe from django.utils.text import capfirst from django.utils.translation import ugettext_lazy as _ class InlineActionException(Exception): pass class ActionNotCallable(InlineActionException): def __init__(self, model_admin, action, *args, **kwargs): super().__init__(*args, **kwargs) self.model_admin = model_admin self.action = action class BaseInlineActionsMixin: INLINE_MODEL_ADMIN = 'inline' MODEL_ADMIN = 'admin' inline_actions = [] def get_inline_actions(self, request, obj=None): """ Returns a list of all actions for this Admin. """ # If self.actions is explicitly set to None that means that we don't # want *any* actions enabled on this page. if self.inline_actions is None: return [] actions = [] # Gather actions from the inline admin and all parent classes, # starting with self and working back up. for klass in self.__class__.mro()[::-1]: class_actions = getattr(klass, 'inline_actions', []) # Avoid trying to iterate over None if not class_actions: continue for action in class_actions: if action not in actions: actions.append(action) return actions def get_readonly_fields(self, request, obj=None): fields = super().get_readonly_fields(request, obj) fields = list(fields) if 'render_inline_actions' not in fields: fields.append('render_inline_actions') return fields def _get_admin_type(self, model_admin=None): """ Returns wether this is an InlineAdmin or not. """ model_admin = model_admin or self if isinstance(model_admin, admin.options.InlineModelAdmin): return self.INLINE_MODEL_ADMIN return self.MODEL_ADMIN def render_inline_actions(self, obj=None): # NOQA: C901 """ Renders all defined inline actions as html. """ if not (obj and obj.pk): return '' buttons = [] for action_name in self.get_inline_actions(self._request, obj): action_func = getattr(self, action_name, None) if not action_func: raise RuntimeError( "Could not find action `{}`".format(action_name)) # Add per-object label support action_name = action_func.__name__ label_handler = getattr( self, 'get_{}_label'.format(action_name), None) if callable(label_handler): description = label_handler(obj=obj) else: try: description = action_func.short_description except AttributeError: description = capfirst(action_name.replace('_', ' ')) # Add per-object css classes support css_handler = getattr( self, 'get_{}_css'.format(action_name), None) if callable(css_handler): css_classes = css_handler(obj=obj) else: try: css_classes = action_func.css_classes except AttributeError: css_classes = '' # If the form is submitted, we have no information about the # requested action. # Hence we need all data to be encoded using the action name. action_data = [ # required to distinguish between multiple inlines for the same model self.__class__.__name__.lower(), self._get_admin_type(), action_name, obj._meta.app_label, obj._meta.model_name, str(obj.pk), ] buttons.append( ''.format( '_action__{}'.format('__'.join(action_data)), description, css_classes, ) ) return mark_safe('
{}
'.format( ''.join(buttons) )) render_inline_actions.short_description = _("Actions") render_inline_actions.allow_tags = True class InlineActionsMixin(BaseInlineActionsMixin): def render_inline_actions(self, obj=None): html = super().render_inline_actions(obj=obj) # we have to add

tags as a workaround for invalid html return mark_safe('

{}

'.format(html)) render_inline_actions.short_description = _("Actions") render_inline_actions.allow_tags = True def get_fields(self, request, obj=None): # store `request` for `get_inline_actions` self._request = request fields = super().get_fields(request, obj) if self.inline_actions is not None: # is it explicitly disabled? fields = list(fields) if 'render_inline_actions' not in fields: fields.append('render_inline_actions') return fields class InlineActionsModelAdminMixin(BaseInlineActionsMixin): class Media: css = { "all": ( "inline_actions/css/inline_actions.css", ) } def get_list_display(self, request): # store `request` for `get_inline_actions` self._request = request fields = super().get_list_display(request) if self.inline_actions is not None: # is it explicitly disabled? fields = list(fields) if 'render_inline_actions' not in fields: fields.append('render_inline_actions') return fields def get_fields(self, request, obj=None): # store `request` for `get_inline_actions` self._request = request fields = super().get_fields(request, obj=obj) if not self.fields: # django adds all readonly fields by default # if `self.fields` is not defined we don't want to include # `render_inline_actions fields.remove('render_inline_actions') return fields def _execute_action(self, request, model_admin, action, obj, parent_obj=None): """ Tries to execute the requested action and returns a `HttpResponse`. raises ActionNotCallable - When action is not a function """ # execute action func = getattr(model_admin, action, None) try: response = func(request, obj, parent_obj=parent_obj) except TypeError as e: raise ActionNotCallable(model_admin, action) from e # we should receive an HttpResponse if isinstance(response, HttpResponse): return response # otherwise redirect back if parent_obj is None: # InlineActionsMixin.MODEL_ADMIN: # redirect to `changelist` url = reverse( 'admin:{}_{}_changelist'.format( obj._meta.app_label, obj._meta.model_name, ), ) else: # redirect to `changeform` url = reverse( 'admin:{}_{}_change'.format( parent_obj._meta.app_label, parent_obj._meta.model_name, ), args=(parent_obj.pk,) ) # readd query string query = request.META['QUERY_STRING'] or request.GET.urlencode() if query: url = '{}?{}'.format(url, query) return redirect(url) def _handle_action(self, request, object_id=None): """ Resolve and executes the action issued by the current request. If no action was triggered, it does nothing. Returns `HttpResponse` or `None` """ all_actions = [key for key in list(request.POST.keys()) if key.startswith('_action__')] if request.method == 'POST' and all_actions: assert len(all_actions) == 1 raw_action_name = all_actions[0].replace('_action__', '', 1) # resolve action and target models raw_action_parts = raw_action_name.split('__') admin_class_name, admin_type = raw_action_parts[:2] action, app_label, model_name, object_pk = raw_action_parts[2:] model = apps.get_model(app_label=app_label, model_name=model_name) parent_obj = self.get_object(request, object_id) # find action and execute if admin_type == self.MODEL_ADMIN: model_admin = self obj = model_admin.get_queryset(request).get(pk=object_pk) # parent_obj is None because `object_id` is None else: for inline in self.get_inline_instances(request): inline_class_name = inline.__class__.__name__.lower() matches_inline_class = inline_class_name == admin_class_name matches_model = inline.model == model if not matches_model or not matches_inline_class: continue model_admin = inline obj = model_admin.get_queryset(request).get(pk=object_pk) if model_admin: return self._execute_action( request, model_admin, action, obj, parent_obj) return None def changeform_view(self, request, object_id=None, form_url='', extra_context=None): # handle requested action if required response = self._handle_action(request, object_id=object_id) if response: return response # continue normally return super().changeform_view( request, object_id, form_url, extra_context) def changelist_view(self, request, extra_context=None): # handle requested action if required response = self._handle_action(request) if response: return response # continue normally return super().changelist_view( request, extra_context) PK!0=;inline_actions/static/inline_actions/css/inline_actions.css.field-render_inline_actions > p, .field-render_inline_actions label + p { display: none; } #changelist table .submit_row.inline_actions input, .submit_row.inline_actions input { padding: 0px 5px; margin: 0 10px 0 0; cursor: pointer; } PK!nN-django_inline_actions-2.2.0.dist-info/LICENSECopyright (c) 2016, Alexander Frenzel All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Alexander Frenzel nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK!HڽTU+django_inline_actions-2.2.0.dist-info/WHEEL A н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK!H{*r (.django_inline_actions-2.2.0.dist-info/METADATAZms6_K>XHT4nS_7yi{Ɉ QH(VԦ7i:,gx? e^3{Eyg&2^łjƣs_ Ιʖ,b.,(ZȔeݘ͘b̋"a,y9l1:么%}%Cj|6<|x 6’qM`?Nl:\pI _^_{gZZ߻´9}<uv K-S{#x1_S[.b_ϥH"lHû.>nVmҭ }V$r0)zJb<!p~iu99|NX IEF3[(;edgRa 2lo|[kzQ/}QZOR Y98Dl*Yy0r=͐y[oJ֗ιIK ZkY&Ւ,;9y*+B *@Ĝ 2df66P_+0|r3g(b񀤀D7oƷW&wƁ3)XF`<<D_eb̀ɒ#)¨dVXtู:u|bk,W&5d4(f h?ývdQ$. KfYdKʏZ)|V ߚ!3$E"B%%tgsEZL,Tl1aX+ϰ}y{}NJĭp?IXE&e8r.a!Rv)c`6FSA,[4Y1- L<2 Q̧ _$ 몃ڈ2Ԃt΂ S)lej` E'f&e{0>"DuHj*Ͳ rv#S)eMvɌ|6kQMF+E4 T;lbe1v\ҏsZQU,Yjv'qޚ~ !86 ͉b+"'58aHUuu#MĢ#cc8"@t-X§"1잏f.HYjX!2 "L@ZM,"+'Fg(lS9s#.Ŋv >K+:1x։A B*'uxs ܝͦȇn<6&Cn0cX"gI#j呙ZlTQrJ,SdCsXGMҔ;ʺnroЫU#U0}f/JLr=]~kIKٌ ʹR!-M;:v\[}%^$,ƯMl,@͡,ZV[<z,dBTZXB9~[QW5?JYvQڸ.& -d 6.8.6fǝQew\ӒPjme.ROfu7#r=Q]K}~v'5^W \LgY>8)zxS}ȳL=Nuii l,l[m:j?fŒ* O@r* H9ENx2vEAnRdGqѣG{c"6 =}6ͬ4(s-v(z(D ٽ}a#ymܿ+6`6K%-wD 2QjtHCWYLWWc$mML<4no6Y#Ru5nol[a {!I} hTU-ؾ`F"ʢGsOľ/ lK(ҙJ?,1hh;4dn(l4i]uTV圑| Hۋ.YQ]9C:GZuneU:U\vMz]믽y 6Azv=ޜ.PGp5P*(S[`V9*;7Ue)k[Q~;D޾P*/Gl׵>ZOj*x;{61e\!Db#TUQױ_&YQCl0ռvz _j7ۛw(sH@m.,ŴZKJsJ ٔ}$lew}>=O>ϣӧg