mirror of
https://github.com/django/django.git
synced 2025-10-26 07:06:08 +00:00
Fixed #21113 -- Made LogEntry.change_message language independent
Thanks Tim Graham for the review.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.admin.utils import quote
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
@@ -7,6 +9,7 @@ from django.db import models
|
||||
from django.urls import NoReverseMatch, reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import python_2_unicode_compatible, smart_text
|
||||
from django.utils.text import get_text_list
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
|
||||
ADDITION = 1
|
||||
@@ -18,6 +21,8 @@ class LogEntryManager(models.Manager):
|
||||
use_in_migrations = True
|
||||
|
||||
def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
|
||||
if isinstance(change_message, list):
|
||||
change_message = json.dumps(change_message)
|
||||
self.model.objects.create(
|
||||
user_id=user_id,
|
||||
content_type_id=content_type_id,
|
||||
@@ -50,6 +55,7 @@ class LogEntry(models.Model):
|
||||
# Translators: 'repr' means representation (https://docs.python.org/3/library/functions.html#repr)
|
||||
object_repr = models.CharField(_('object repr'), max_length=200)
|
||||
action_flag = models.PositiveSmallIntegerField(_('action flag'))
|
||||
# change_message is either a string or a JSON structure
|
||||
change_message = models.TextField(_('change message'), blank=True)
|
||||
|
||||
objects = LogEntryManager()
|
||||
@@ -69,7 +75,7 @@ class LogEntry(models.Model):
|
||||
elif self.is_change():
|
||||
return ugettext('Changed "%(object)s" - %(changes)s') % {
|
||||
'object': self.object_repr,
|
||||
'changes': self.change_message,
|
||||
'changes': self.get_change_message(),
|
||||
}
|
||||
elif self.is_deletion():
|
||||
return ugettext('Deleted "%(object)s."') % {'object': self.object_repr}
|
||||
@@ -85,6 +91,46 @@ class LogEntry(models.Model):
|
||||
def is_deletion(self):
|
||||
return self.action_flag == DELETION
|
||||
|
||||
def get_change_message(self):
|
||||
"""
|
||||
If self.change_message is a JSON structure, interpret it as a change
|
||||
string, properly translated.
|
||||
"""
|
||||
if self.change_message and self.change_message[0] == '[':
|
||||
try:
|
||||
change_message = json.loads(self.change_message)
|
||||
except ValueError:
|
||||
return self.change_message
|
||||
messages = []
|
||||
for sub_message in change_message:
|
||||
if 'added' in sub_message:
|
||||
if sub_message['added']:
|
||||
sub_message['added']['name'] = ugettext(sub_message['added']['name'])
|
||||
messages.append(ugettext('Added {name} "{object}".').format(**sub_message['added']))
|
||||
else:
|
||||
messages.append(ugettext('Added.'))
|
||||
|
||||
elif 'changed' in sub_message:
|
||||
sub_message['changed']['fields'] = get_text_list(
|
||||
sub_message['changed']['fields'], ugettext('and')
|
||||
)
|
||||
if 'name' in sub_message['changed']:
|
||||
sub_message['changed']['name'] = ugettext(sub_message['changed']['name'])
|
||||
messages.append(ugettext('Changed {fields} for {name} "{object}".').format(
|
||||
**sub_message['changed']
|
||||
))
|
||||
else:
|
||||
messages.append(ugettext('Changed {fields}.').format(**sub_message['changed']))
|
||||
|
||||
elif 'deleted' in sub_message:
|
||||
sub_message['deleted']['name'] = ugettext(sub_message['deleted']['name'])
|
||||
messages.append(ugettext('Deleted {name} "{object}".').format(**sub_message['deleted']))
|
||||
|
||||
change_message = ' '.join(msg[0].upper() + msg[1:] for msg in messages)
|
||||
return change_message or ugettext('No fields changed.')
|
||||
else:
|
||||
return self.change_message
|
||||
|
||||
def get_edited_object(self):
|
||||
"Returns the edited object represented by this log entry"
|
||||
return self.content_type.get_object_for_this_type(pk=self.object_id)
|
||||
|
||||
@@ -44,7 +44,9 @@ from django.utils.html import escape, format_html
|
||||
from django.utils.http import urlencode, urlquote
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.text import capfirst, get_text_list
|
||||
from django.utils.translation import string_concat, ugettext as _, ungettext
|
||||
from django.utils.translation import (
|
||||
override as translation_override, string_concat, ugettext as _, ungettext,
|
||||
)
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
@@ -924,33 +926,44 @@ class ModelAdmin(BaseModelAdmin):
|
||||
return urlencode({'_changelist_filters': preserved_filters})
|
||||
return ''
|
||||
|
||||
@translation_override(None)
|
||||
def construct_change_message(self, request, form, formsets, add=False):
|
||||
"""
|
||||
Construct a change message from a changed object.
|
||||
Construct a JSON structure describing changes from a changed object.
|
||||
Translations are deactivated so that strings are stored untranslated.
|
||||
Translation happens later on LogEntry access.
|
||||
"""
|
||||
change_message = []
|
||||
if add:
|
||||
change_message.append(_('Added.'))
|
||||
change_message.append({'added': {}})
|
||||
elif form.changed_data:
|
||||
change_message.append(_('Changed %s.') % get_text_list(form.changed_data, _('and')))
|
||||
change_message.append({'changed': {'fields': form.changed_data}})
|
||||
|
||||
if formsets:
|
||||
for formset in formsets:
|
||||
for added_object in formset.new_objects:
|
||||
change_message.append(_('Added %(name)s "%(object)s".')
|
||||
% {'name': force_text(added_object._meta.verbose_name),
|
||||
'object': force_text(added_object)})
|
||||
change_message.append({
|
||||
'added': {
|
||||
'name': force_text(added_object._meta.verbose_name),
|
||||
'object': force_text(added_object),
|
||||
}
|
||||
})
|
||||
for changed_object, changed_fields in formset.changed_objects:
|
||||
change_message.append(_('Changed %(list)s for %(name)s "%(object)s".')
|
||||
% {'list': get_text_list(changed_fields, _('and')),
|
||||
'name': force_text(changed_object._meta.verbose_name),
|
||||
'object': force_text(changed_object)})
|
||||
change_message.append({
|
||||
'changed': {
|
||||
'name': force_text(changed_object._meta.verbose_name),
|
||||
'object': force_text(changed_object),
|
||||
'fields': changed_fields,
|
||||
}
|
||||
})
|
||||
for deleted_object in formset.deleted_objects:
|
||||
change_message.append(_('Deleted %(name)s "%(object)s".')
|
||||
% {'name': force_text(deleted_object._meta.verbose_name),
|
||||
'object': force_text(deleted_object)})
|
||||
change_message = ' '.join(change_message)
|
||||
return change_message or _('No fields changed.')
|
||||
change_message.append({
|
||||
'deleted': {
|
||||
'name': force_text(deleted_object._meta.verbose_name),
|
||||
'object': force_text(deleted_object),
|
||||
}
|
||||
})
|
||||
return change_message
|
||||
|
||||
def message_user(self, request, message, level=messages.INFO, extra_tags='',
|
||||
fail_silently=False):
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<tr>
|
||||
<th scope="row">{{ action.action_time|date:"DATETIME_FORMAT" }}</th>
|
||||
<td>{{ action.user.get_username }}{% if action.user.get_full_name %} ({{ action.user.get_full_name }}){% endif %}</td>
|
||||
<td>{{ action.change_message }}</td>
|
||||
<td>{{ action.get_change_message }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user