mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-22 20:12:00 -06:00
Improved UserAction display
This commit is contained in:
parent
b614a1ae67
commit
651f97af81
22
netbox/extras/migrations/0005_auto_20160524_1324.py
Normal file
22
netbox/extras/migrations/0005_auto_20160524_1324.py
Normal file
@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.5 on 2016-05-24 13:24
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('extras', '0004_useraction'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='useraction',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='actions', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
@ -3,6 +3,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.http import HttpResponse
|
||||
from django.template import Template, Context
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from dcim.models import Site
|
||||
|
||||
@ -156,7 +157,7 @@ class UserAction(models.Model):
|
||||
A record of an action (add, edit, or delete) performed on an object by a User.
|
||||
"""
|
||||
time = models.DateTimeField(auto_now_add=True, editable=False)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
user = models.ForeignKey(User, related_name='actions', on_delete=models.CASCADE)
|
||||
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
|
||||
object_id = models.PositiveIntegerField(blank=True, null=True)
|
||||
action = models.PositiveSmallIntegerField(choices=ACTION_CHOICES)
|
||||
@ -166,3 +167,18 @@ class UserAction(models.Model):
|
||||
|
||||
class Meta:
|
||||
ordering = ['-time']
|
||||
|
||||
def __unicode__(self):
|
||||
if self.message:
|
||||
return ' '.join([self.user, self.message])
|
||||
return ' '.join([self.user, self.get_action_display(), self.content_type])
|
||||
|
||||
def icon(self):
|
||||
if self.action in [ACTION_CREATE, ACTION_IMPORT]:
|
||||
return mark_safe('<i class="glyphicon glyphicon-plus text-success"></i>')
|
||||
elif self.action in [ACTION_EDIT, ACTION_BULK_EDIT]:
|
||||
return mark_safe('<i class="glyphicon glyphicon-pencil text-warning"></i>')
|
||||
elif self.action in [ACTION_DELETE, ACTION_BULK_DELETE]:
|
||||
return mark_safe('<i class="glyphicon glyphicon-remove text-danger"></i>')
|
||||
else:
|
||||
return ''
|
||||
|
@ -36,7 +36,7 @@ def home(request):
|
||||
|
||||
return render(request, 'home.html', {
|
||||
'stats': stats,
|
||||
'recent_activity': UserAction.objects.all()[:20]
|
||||
'recent_activity': UserAction.objects.all()[:15]
|
||||
})
|
||||
|
||||
|
||||
|
@ -47,100 +47,104 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>DCIM</strong>
|
||||
<div class="col-md-7">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>DCIM</strong>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.site_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'dcim:site_list' %}">Sites</a></h4>
|
||||
<p class="list-group-item-text text-muted">Geographic locations</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.rack_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'dcim:rack_list' %}">Racks</a></h4>
|
||||
<p class="list-group-item-text text-muted">Equipment racks, optionally organized by group</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.device_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'dcim:device_list' %}">Devices</a></h4>
|
||||
<p class="list-group-item-text text-muted">Rack-mounted network equipment, servers, and other devices</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<h4 class="list-group-item-heading">Connections</h4>
|
||||
<span class="badge pull-right">{{ stats.interface_connections_count }}</span>
|
||||
<p style="padding-left: 20px;"><a href="{% url 'dcim:interface_connections_list' %}">Interfaces</a></p>
|
||||
<span class="badge pull-right">{{ stats.console_connections_count }}</span>
|
||||
<p style="padding-left: 20px;"><a href="{% url 'dcim:console_connections_list' %}">Console</a></p>
|
||||
<span class="badge pull-right">{{ stats.power_connections_count }}</span>
|
||||
<p class="list-group-item-text" style="padding-left: 20px;"><a href="{% url 'dcim:power_connections_list' %}">Power</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if perms.secrets %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Secrets</strong>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.secret_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'secrets:secret_list' %}">Secrets</a></h4>
|
||||
<p class="list-group-item-text text-muted">Sensitive data (such as passwords) which has been stored securely</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.site_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'dcim:site_list' %}">Sites</a></h4>
|
||||
<p class="list-group-item-text text-muted">Geographic locations</p>
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>IPAM</strong>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.aggregate_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'ipam:aggregate_list' %}">Aggregates</a></h4>
|
||||
<p class="list-group-item-text text-muted">Top-level IP allocations</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.prefix_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'ipam:prefix_list' %}">Prefixes</a></h4>
|
||||
<p class="list-group-item-text text-muted">IPv4 and IPv6 network assignments</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.ipaddress_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'ipam:ipaddress_list' %}">IP Addresses</a></h4>
|
||||
<p class="list-group-item-text text-muted">Individual IPv4 and IPv6 addresses</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.vlan_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'ipam:vlan_list' %}">VLANs</a></h4>
|
||||
<p class="list-group-item-text text-muted">Layer two domains, identified by VLAN ID</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.rack_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'dcim:rack_list' %}">Racks</a></h4>
|
||||
<p class="list-group-item-text text-muted">Equipment racks, optionally organized by group</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.device_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'dcim:device_list' %}">Devices</a></h4>
|
||||
<p class="list-group-item-text text-muted">Rack-mounted network equipment, servers, and other devices</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<h4 class="list-group-item-heading">Connections</h4>
|
||||
<span class="badge pull-right">{{ stats.interface_connections_count }}</span>
|
||||
<p style="padding-left: 20px;"><a href="{% url 'dcim:interface_connections_list' %}">Interfaces</a></p>
|
||||
<span class="badge pull-right">{{ stats.console_connections_count }}</span>
|
||||
<p style="padding-left: 20px;"><a href="{% url 'dcim:console_connections_list' %}">Console</a></p>
|
||||
<span class="badge pull-right">{{ stats.power_connections_count }}</span>
|
||||
<p class="list-group-item-text" style="padding-left: 20px;"><a href="{% url 'dcim:power_connections_list' %}">Power</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if perms.secrets %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Secrets</strong>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.secret_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'secrets:secret_list' %}">Secrets</a></h4>
|
||||
<p class="list-group-item-text text-muted">Sensitive data (such as passwords) which has been stored securely</p>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Circuits</strong>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.provider_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'circuits:provider_list' %}">Providers</a></h4>
|
||||
<p class="list-group-item-text text-muted">Organizations which provide circuit connectivity</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.circuit_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'circuits:circuit_list' %}">Circuits</a></h4>
|
||||
<p class="list-group-item-text text-muted">Communication links for Internet transit, peering, and other services</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>IPAM</strong>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.aggregate_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'ipam:aggregate_list' %}">Aggregates</a></h4>
|
||||
<p class="list-group-item-text text-muted">Top-level IP allocations</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.prefix_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'ipam:prefix_list' %}">Prefixes</a></h4>
|
||||
<p class="list-group-item-text text-muted">IPv4 and IPv6 network assignments</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.ipaddress_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'ipam:ipaddress_list' %}">IP Addresses</a></h4>
|
||||
<p class="list-group-item-text text-muted">Individual IPv4 and IPv6 addresses</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.vlan_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'ipam:vlan_list' %}">VLANs</a></h4>
|
||||
<p class="list-group-item-text text-muted">Layer two domains, identified by VLAN ID</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Circuits</strong>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.provider_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'circuits:provider_list' %}">Providers</a></h4>
|
||||
<p class="list-group-item-text text-muted">Organizations which provide circuit connectivity</p>
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<span class="badge pull-right">{{ stats.circuit_count }}</span>
|
||||
<h4 class="list-group-item-heading"><a href="{% url 'circuits:circuit_list' %}">Circuits</a></h4>
|
||||
<p class="list-group-item-text text-muted">Communication links for Internet transit, peering, and other services</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-5">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<strong>Recent Activity</strong>
|
||||
@ -150,7 +154,7 @@
|
||||
<tr>
|
||||
<td>{{ a.time|date:"Y-m-d H:i" }}</td>
|
||||
<td>{{ a.user }}</td>
|
||||
<td>{{ a.message|safe }}</td>
|
||||
<td>{{ a.icon }} {{ a.message|safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
@ -2,4 +2,5 @@
|
||||
<li{% ifequal active_tab "profile" %} class="active"{% endifequal %}><a href="{% url 'users:profile' %}">Profile</a></li>
|
||||
<li{% ifequal active_tab "change_password" %} class="active"{% endifequal %}><a href="{% url 'users:change_password' %}">Change Password</a></li>
|
||||
<li{% ifequal active_tab "userkey" %} class="active"{% endifequal %}><a href="{% url 'users:userkey' %}">User Key</a></li>
|
||||
<li{% ifequal active_tab "recent_activity" %} class="active"{% endifequal %}><a href="{% url 'users:recent_activity' %}">Recent Activity</a></li>
|
||||
</ul>
|
||||
|
35
netbox/templates/users/recent_activity.html
Normal file
35
netbox/templates/users/recent_activity.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends '_base.html' %}
|
||||
{% load form_helpers %}
|
||||
|
||||
{% block title %}Recent Activity{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2">
|
||||
<h1>Recent Activity</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-2 col-md-offset-2">
|
||||
{% include 'users/inc/profile_nav.html' with active_tab="recent_activity" %}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for action in recent_activity %}
|
||||
<tr>
|
||||
<td>{{ action.time|date:"Y-m-d H:i" }}</td>
|
||||
<td>{{ action.icon }} {{ action.message|safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -10,5 +10,6 @@ urlpatterns = [
|
||||
url(r'^profile/password/$', views.change_password, name='change_password'),
|
||||
url(r'^profile/user-key/$', views.userkey, name='userkey'),
|
||||
url(r'^profile/user-key/edit/$', views.userkey_edit, name='userkey_edit'),
|
||||
url(r'^profile/recent-activity/$', views.recent_activity, name='recent_activity'),
|
||||
|
||||
]
|
||||
|
@ -116,3 +116,11 @@ def userkey_edit(request):
|
||||
'userkey': userkey,
|
||||
'form': form,
|
||||
})
|
||||
|
||||
|
||||
@login_required()
|
||||
def recent_activity(request):
|
||||
|
||||
return render(request, 'users/recent_activity.html', {
|
||||
'recent_activity': request.user.actions.all()[:50]
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user