Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/history #349

Merged
merged 30 commits into from
Jul 21, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5ed5123
started on history
kennethlove Jul 18, 2013
fb0e73a
PEP8
chrisjones-brack3t Jul 18, 2013
4cfe10e
actual data in history template
kennethlove Jul 18, 2013
ef77c82
Merge branch 'fix-history' of github.com:brack3t/django-admin2 into f…
kennethlove Jul 18, 2013
76ab676
more html
kennethlove Jul 18, 2013
6273e68
Stealing the logger from the old admin.
chrisjones-brack3t Jul 18, 2013
14d2a20
Merge branch 'fix-history' of github.com:brack3t/django-admin2 into f…
chrisjones-brack3t Jul 18, 2013
598b0a7
basics for recording history
kennethlove Jul 18, 2013
ae39a26
Use model properties for action ints.
chrisjones-brack3t Jul 18, 2013
555588f
PEP8
chrisjones-brack3t Jul 18, 2013
4089ad4
PEP8 all the things. Add in new history url.
chrisjones-brack3t Jul 18, 2013
82d9e7a
PEP8 love.
chrisjones-brack3t Jul 18, 2013
f7ef575
Should be a detail view.
chrisjones-brack3t Jul 18, 2013
d62b612
nevermind.
chrisjones-brack3t Jul 18, 2013
7dbf504
model_history template
kennethlove Jul 18, 2013
a795135
Merge branch 'fix-history' of github.com:brack3t/django-admin2 into f…
kennethlove Jul 18, 2013
ee1c90c
Working model history view.
chrisjones-brack3t Jul 18, 2013
ee2ca9f
template updates
kennethlove Jul 18, 2013
802265b
Merge branch 'fix-history' of github.com:brack3t/django-admin2 into f…
kennethlove Jul 18, 2013
99ae00a
show user that performed action
kennethlove Jul 18, 2013
acae0a2
icons
kennethlove Jul 19, 2013
52c337a
force_text for object_repr and condition on history link
kennethlove Jul 19, 2013
69f2a35
Why don't you PEP your 8! Add in change messages.
chrisjones-brack3t Jul 19, 2013
57ef2a4
Nicer to look at.
chrisjones-brack3t Jul 19, 2013
e5f1940
action_type property
kennethlove Jul 19, 2013
af11cb0
Merge branch 'fix-history' of github.com:brack3t/django-admin2 into f…
kennethlove Jul 19, 2013
e7f8170
Ooooooooo pretty.
chrisjones-brack3t Jul 19, 2013
738e3a9
template changes
kennethlove Jul 19, 2013
3844a95
merge
kennethlove Jul 19, 2013
4959a09
fix merge error.
chrisjones-brack3t Jul 19, 2013
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions djadmin2/core.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-:
"""
WARNING: This file about to undergo major refactoring by @pydanny per Issue #99.
WARNING: This file about to undergo major refactoring by @pydanny per
Issue #99.
"""
from __future__ import division, absolute_import, unicode_literals

Expand All @@ -22,7 +23,8 @@ class Admin2(object):
It keeps a registry of all registered Models and collects the urls of their
related ModelAdmin2 instances.

It also provides an index view that serves as an entry point to the admin site.
It also provides an index view that serves as an entry point to the
admin site.
"""
index_view = views.IndexView
app_index_view = views.AppIndexView
Expand All @@ -46,7 +48,8 @@ def register(self, model, model_admin=None, **kwargs):
If a model is already registered, this will raise ImproperlyConfigured.
"""
if model in self.registry:
raise ImproperlyConfigured('%s is already registered in django-admin2' % model)
raise ImproperlyConfigured(
'%s is already registered in django-admin2' % model)
if not model_admin:
model_admin = types.ModelAdmin2
self.registry[model] = model_admin(model, admin=self, **kwargs)
Expand All @@ -62,12 +65,14 @@ def deregister(self, model):
"""
Deregisters the given model. Remove the model from the self.app as well

If the model is not already registered, this will raise ImproperlyConfigured.
If the model is not already registered, this will raise
ImproperlyConfigured.
"""
try:
del self.registry[model]
except KeyError:
raise ImproperlyConfigured('%s was never registered in django-admin2' % model)
raise ImproperlyConfigured(
'%s was never registered in django-admin2' % model)

# Remove the model from the apps registry
# Get the app label
Expand Down Expand Up @@ -101,7 +106,8 @@ def get_admin_by_name(self, name):
for object_admin in self.registry.values():
if object_admin.name == name:
return object_admin
raise ValueError(u'No object admin found with name {}'.format(repr(name)))
raise ValueError(
u'No object admin found with name {}'.format(repr(name)))

def get_index_kwargs(self):
return {
Expand All @@ -122,7 +128,8 @@ def get_api_index_kwargs(self):
}

def get_urls(self):
urlpatterns = patterns('',
urlpatterns = patterns(
'',
url(regex=r'^$',
view=self.index_view.as_view(**self.get_index_kwargs()),
name='dashboard'
Expand All @@ -140,18 +147,21 @@ def get_urls(self):
name='logout'
),
url(regex=r'^(?P<app_label>\w+)/$',
view=self.app_index_view.as_view(**self.get_app_index_kwargs()),
view=self.app_index_view.as_view(
**self.get_app_index_kwargs()),
name='app_index'
),
url(regex=r'^api/v0/$',
view=self.api_index_view.as_view(**self.get_api_index_kwargs()),
view=self.api_index_view.as_view(
**self.get_api_index_kwargs()),
name='api_index'
),
)

for model, model_admin in self.registry.iteritems():
model_options = utils.model_options(model)
urlpatterns += patterns('',
urlpatterns += patterns(
'',
url('^{}/{}/'.format(
model_options.app_label,
model_options.object_name.lower()),
Expand Down
96 changes: 95 additions & 1 deletion djadmin2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,107 @@
""" Boilerplate for now, will serve a purpose soon! """
from __future__ import division, absolute_import, unicode_literals

from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.util import quote
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate to be a jerk, but this single line of code means I can't accept this pull request.

Please read https://django-admin2.readthedocs.org/en/latest/design.html, especially the constraints.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

God, you jerk 👯

We'll port that function over soon.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean jerk or krej? Krej, as you know, is the name of the sauce that Pac Rim Kaiju put on their giant hamburgers.

👊

from django.db import models
from django.db.models import signals
from django.utils.encoding import force_text
from django.utils.encoding import smart_text
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext, ugettext_lazy as _

from . import permissions


class LogEntryManager(models.Manager):
def log_action(self, user_id, obj, action_flag, change_message=''):
content_type_id = ContentType.objects.get_for_model(obj).id
e = self.model(None, None, user_id, content_type_id,
smart_text(obj.id), force_text(obj)[:200],
action_flag, change_message)
e.save()


@python_2_unicode_compatible
class LogEntry(models.Model):
ADDITION = 1
CHANGE = 2
DELETION = 3

action_time = models.DateTimeField(_('action time'), auto_now=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
related_name='log_entries')
content_type = models.ForeignKey(ContentType, blank=True, null=True,
related_name='log_entries')
object_id = models.TextField(_('object id'), blank=True, null=True)
object_repr = models.CharField(_('object repr'), max_length=200)
action_flag = models.PositiveSmallIntegerField(_('action flag'))
change_message = models.TextField(_('change message'), blank=True)

objects = LogEntryManager()

class Meta:
verbose_name = _('log entry')
verbose_name_plural = _('log entries')
ordering = ('-action_time',)

def __repr__(self):
return smart_text(self.action_time)

def __str__(self):
if self.action_flag == self.ADDITION:
return ugettext('Added "%(object)s".') % {
'object': self.object_repr}
elif self.action_flag == self.CHANGE:
return ugettext('Changed "%(object)s" - %(changes)s') % {
'object': self.object_repr,
'changes': self.change_message,
}
elif self.action_flag == self.DELETION:
return ugettext('Deleted "%(object)s."') % {
'object': self.object_repr}

return ugettext('LogEntry Object')

def is_addition(self):
return self.action_flag == self.ADDITION

def is_change(self):
return self.action_flag == self.CHANGE

def is_deletion(self):
return self.action_flag == self.DELETION

@property
def action_type(self):
if self.is_addition():
return _('added')
if self.is_change():
return _('changed')
if self.is_deletion():
return _('deleted')
return ''

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)

def get_admin_url(self):
"""
Returns the admin URL to edit the object represented by this log entry.
This is relative to the Django admin index page.
"""
if self.content_type and self.object_id:
return '{0.app_label}/{0.model}/{1}'.format(
self.content_type,
quote(self.object_id)
)
return None

# setup signal handlers here, since ``models.py`` will be imported by django
# for sure if ``djadmin2`` is listed in the ``INSTALLED_APPS``.

signals.post_syncdb.connect(permissions.create_view_permissions,
signals.post_syncdb.connect(
permissions.create_view_permissions,
dispatch_uid="django-admin2.djadmin2.permissions.create_view_permissions")
9 changes: 8 additions & 1 deletion djadmin2/templatetags/admin2_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django import template
from django.db.models.fields import FieldDoesNotExist

from .. import utils, renderers
from .. import utils, renderers, models


register = template.Library()
Expand Down Expand Up @@ -136,3 +136,10 @@ def render(context, model_instance, attribute_name):
# It must be a method instead.
field = None
return renderer(value, field)


@register.inclusion_tag('djadmin2theme_default/includes/history.html',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A hard-coded link to the default theme. We'll patch this later but we're trying to keep these out. 🔢

This is where settings.ADMIN2_THEME_DIRECTORY is normally used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Should have a conventions area in the contrib docs about template paths.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. I'll open a ticket.

takes_context=True)
def action_history(context):
actions = models.LogEntry.objects.filter(user__pk=context['user'].pk)
return {'actions': actions}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{% if actions %}
<ol class="unstyled">
{% for action in actions %}
<li>
{% if action.is_addition %}
<i class="added icon-plus"></i>
{% elif action.is_change %}
<i class="changed icon-pencil"></i>
{% else %}
<i class="deleted icon-minus"></i>
{% endif %}
{{ action }}
<span class="muted">{{ action.content_type.model }}</span>
</li>
{% endfor %}
</ol>
{% else %}
<p>None available</p>
{% endif %}

Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@

{% block content %}

<div class="row">
<div class="span7">
<div class="row-fluid">
<div class="span8">
{% for app_label, registry in apps.items %}
{% include 'djadmin2theme_default/includes/app_model_list.html' %}
{% endfor %}
</div>
<div class="span5">
<div class="span4 well pull-right">
<h4>{% trans "Recent Actions" %}</h4>
<h5>{% trans "My Actions" %}</h5>
TODO
{% action_history %}
</div>
</div>
{% endblock content %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{% extends "djadmin2theme_default/base.html" %}
{% load admin2_tags i18n %}

{% block title %}{% trans "History for" %} {{ object }}{% endblock title %}

{% block page_title %}{% trans "History for" %} {{ object }}{% endblock page_title %}

{% block breadcrumbs %}
<li>
<a href="{% url "admin2:dashboard" %}">{% trans "Home" %}</a>
<span class="divider">/</span>
</li>
<li>
<a href="{% url "admin2:app_index" app_label=app_label %}">{{ app_label|title }}</a>
<span class="divider">/</span>
</li>
<li>
<a href="{% url view|admin2_urlname:"index" %}">{{ model_name_pluralized|title }}</a>
<span class="divider">/</span>
</li>
<li>
<a href="{% url view|admin2_urlname:"detail" pk=object.pk %}">{{ object }}</a>
<span class="divider">/</span>
</li>
<li class="active">{% trans "History" %}</li>
{% endblock breadcrumbs %}

{% block content %}

<p>
{% blocktrans with object=object %}
History for {{ object }}
{% endblocktrans %}

{% if object_list %}
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>{% trans "Date/Time" %}</th>
<th>{% trans "User" %}</th>
<th>{% trans "Action" %}</th>
<th>{% trans "Message" %}</th>
</tr>
</thead>
<tbody>
{% for log in object_list %}
<tr>
<td>{{ log.action_time }}</td>
<td>{{ log.user }}</td>
<td>{{ log.action_type|capfirst }}</td>
<td>{{ log.change_message }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No history for this object.</p>
{% endif %}
{% endblock content %}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
{% block page_title %}{% blocktrans with action=action model_name=model_name %}{{ action_name }} {{ model_name }}{% endblocktrans %}{% endblock page_title %}

{% block page_title_link %}
<a href="#" class="btn btn-info">History</a>
{% if object.pk %}
<a href="{% url view|admin2_urlname:"history" pk=object.pk %}" class="btn btn-info pull-right">History</a>
{% endif %}
{% endblock page_title_link %}

{% block breadcrumbs %}
Expand Down Expand Up @@ -41,14 +43,14 @@
{% block content %}

<form method="post">

{% if view.model_admin.save_on_top %}
{% include "djadmin2theme_default/includes/save_buttons.html" %}
{% endif %}

<div class="row-fluid"><!-- begin main form row -->
<div class="span12">

<div class="change_form">
{% csrf_token %}
{{ form|crispy }}
Expand All @@ -63,11 +65,11 @@ <h4>{{ formset.model|model_verbose_name_plural|capfirst }}</h4>

</div>
</div><!-- end main form row -->

{% if view.model_admin.save_on_bottom %}
{% include "djadmin2theme_default/includes/save_buttons.html" %}
{% endif %}

</form>


Expand Down
Loading