4
4
from django .contrib import admin
5
5
from django .contrib .admin import helpers
6
6
from django .contrib .admin .utils import unquote
7
+ from django .contrib .auth import get_permission_codename
7
8
from django .core .exceptions import PermissionDenied
8
9
from django .shortcuts import get_object_or_404 , render
9
10
from django .urls import re_path , reverse
12
13
from django .utils .text import capfirst
13
14
from django .utils .translation import gettext as _
14
15
15
- from . import utils
16
+ from .utils import get_history_manager_for_model , get_history_model_for_model
16
17
17
18
USER_NATURAL_KEY = tuple (key .lower () for key in settings .AUTH_USER_MODEL .split ("." , 1 ))
18
19
@@ -61,7 +62,7 @@ def history_view(self, request, object_id, extra_context=None):
61
62
except action_list .model .DoesNotExist :
62
63
raise http .Http404
63
64
64
- if not self .has_change_permission (request , obj ):
65
+ if not self .has_view_history_or_change_history_permission (request , obj ):
65
66
raise PermissionDenied
66
67
67
68
# Set attribute on each action_list entry from admin methods
@@ -79,7 +80,7 @@ def history_view(self, request, object_id, extra_context=None):
79
80
content_type .model ,
80
81
)
81
82
context = {
82
- "title" : self .history_view_title (obj ),
83
+ "title" : self .history_view_title (request , obj ),
83
84
"action_list" : action_list ,
84
85
"module_name" : capfirst (force_str (opts .verbose_name_plural )),
85
86
"object" : obj ,
@@ -88,7 +89,7 @@ def history_view(self, request, object_id, extra_context=None):
88
89
"opts" : opts ,
89
90
"admin_user_view" : admin_user_view ,
90
91
"history_list_display" : history_list_display ,
91
- "revert_disabled" : self .revert_disabled ,
92
+ "revert_disabled" : self .revert_disabled ( request , obj ) ,
92
93
}
93
94
context .update (self .admin_site .each_context (request ))
94
95
context .update (extra_context or {})
@@ -97,8 +98,8 @@ def history_view(self, request, object_id, extra_context=None):
97
98
request , self .object_history_template , context , ** extra_kwargs
98
99
)
99
100
100
- def history_view_title (self , obj ):
101
- if self .revert_disabled and not SIMPLE_HISTORY_EDIT :
101
+ def history_view_title (self , request , obj ):
102
+ if self .revert_disabled ( request , obj ) and not SIMPLE_HISTORY_EDIT :
102
103
return _ ("View history: %s" ) % force_str (obj )
103
104
else :
104
105
return _ ("Change history: %s" ) % force_str (obj )
@@ -131,7 +132,7 @@ def history_form_view(self, request, object_id, version_id, extra_context=None):
131
132
).instance
132
133
obj ._state .adding = False
133
134
134
- if not self .has_change_permission (request , obj ):
135
+ if not self .has_view_history_or_change_history_permission (request , obj ):
135
136
raise PermissionDenied
136
137
137
138
if SIMPLE_HISTORY_EDIT :
@@ -140,7 +141,7 @@ def history_form_view(self, request, object_id, version_id, extra_context=None):
140
141
change_history = False
141
142
142
143
if "_change_history" in request .POST and SIMPLE_HISTORY_EDIT :
143
- history = utils . get_history_manager_for_model (obj )
144
+ history = get_history_manager_for_model (obj )
144
145
obj = history .get (pk = version_id ).instance
145
146
146
147
formsets = []
@@ -173,7 +174,7 @@ def history_form_view(self, request, object_id, version_id, extra_context=None):
173
174
model_name = original_opts .model_name
174
175
url_triplet = self .admin_site .name , original_opts .app_label , model_name
175
176
context = {
176
- "title" : self .history_form_view_title (obj ),
177
+ "title" : self .history_form_view_title (request , obj ),
177
178
"adminform" : admin_form ,
178
179
"object_id" : object_id ,
179
180
"original" : obj ,
@@ -186,12 +187,13 @@ def history_form_view(self, request, object_id, version_id, extra_context=None):
186
187
"change_url" : reverse ("%s:%s_%s_change" % url_triplet , args = (obj .pk ,)),
187
188
"history_url" : reverse ("%s:%s_%s_history" % url_triplet , args = (obj .pk ,)),
188
189
"change_history" : change_history ,
189
- "revert_disabled" : self .revert_disabled ,
190
+ "revert_disabled" : self .revert_disabled ( request , obj ) ,
190
191
# Context variables copied from render_change_form
191
192
"add" : False ,
192
193
"change" : True ,
193
194
"has_add_permission" : self .has_add_permission (request ),
194
- "has_change_permission" : self .has_change_permission (request , obj ),
195
+ "has_view_permission" : self .has_view_history_permission (request , obj ),
196
+ "has_change_permission" : self .has_change_history_permission (request , obj ),
195
197
"has_delete_permission" : self .has_delete_permission (request , obj ),
196
198
"has_file_field" : True ,
197
199
"has_absolute_url" : False ,
@@ -211,8 +213,8 @@ def history_form_view(self, request, object_id, version_id, extra_context=None):
211
213
request , self .object_history_form_template , context , ** extra_kwargs
212
214
)
213
215
214
- def history_form_view_title (self , obj ):
215
- if self .revert_disabled :
216
+ def history_form_view_title (self , request , obj ):
217
+ if self .revert_disabled ( request , obj ) :
216
218
return _ ("View %s" ) % force_str (obj )
217
219
else :
218
220
return _ ("Revert %s" ) % force_str (obj )
@@ -231,6 +233,54 @@ def content_type_model_cls(self):
231
233
"""Returns the ContentType model class."""
232
234
return django_apps .get_model ("contenttypes.contenttype" )
233
235
236
+ def revert_disabled (self , request , obj = None ):
237
+ """If `True`, hides the "Revert" button in the `submit_line.html` template."""
238
+ if getattr (settings , "SIMPLE_HISTORY_REVERT_DISABLED" , False ):
239
+ return True
240
+ elif self .has_view_history_permission (
241
+ request , obj
242
+ ) and not self .has_change_history_permission (request , obj ):
243
+ return True
244
+ return False
245
+
246
+ def has_view_permission (self , request , obj = None ):
247
+ return super ().has_view_permission (request , obj )
248
+
249
+ def has_change_permission (self , request , obj = None ):
250
+ return super ().has_change_permission (request , obj )
251
+
252
+ def has_view_or_change_permission (self , request , obj = None ):
253
+ return self .has_view_permission (request , obj ) or self .has_change_permission (
254
+ request , obj
255
+ )
256
+
257
+ def has_view_history_or_change_history_permission (self , request , obj = None ):
258
+ if self .enforce_history_permissions :
259
+ return self .has_view_history_permission (
260
+ request , obj
261
+ ) or self .has_change_history_permission (request , obj )
262
+ return self .has_view_or_change_permission (request , obj )
263
+
264
+ def has_view_history_permission (self , request , obj = None ):
265
+ if self .enforce_history_permissions :
266
+ opts_history = get_history_model_for_model (self .model )._meta
267
+ codename_view_history = get_permission_codename ("view" , opts_history )
268
+ return request .user .has_perm (
269
+ f"{ opts_history .app_label } .{ codename_view_history } "
270
+ )
271
+ return self .has_view_permission (request , obj )
272
+
273
+ def has_change_history_permission (self , request , obj = None ):
274
+ if self .enforce_history_permissions :
275
+ opts_history = get_history_model_for_model (self .model )._meta
276
+ codename_change_history = get_permission_codename ("change" , opts_history )
277
+ return request .user .has_perm (
278
+ f"{ opts_history .app_label } .{ codename_change_history } "
279
+ )
280
+ return self .has_change_permission (request , obj )
281
+
234
282
@property
235
- def revert_disabled (self ):
236
- return getattr (settings , "SIMPLE_HISTORY_REVERT_DISABLED" , False )
283
+ def enforce_history_permissions (self ):
284
+ return getattr (
285
+ settings , "SIMPLE_HISTORY_ENFORCE_HISTORY_MODEL_PERMISSIONS" , False
286
+ )
0 commit comments