Parcourir la source

Improved UserAction display

Jeremy Stretch il y a 9 ans
Parent
commit
651f97af81

+ 22 - 0
netbox/extras/migrations/0005_auto_20160524_1324.py

@@ -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),
+        ),
+    ]

+ 17 - 1
netbox/extras/models.py

@@ -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 ''

+ 1 - 1
netbox/netbox/views.py

@@ -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]
     })
 
 

+ 91 - 87
netbox/templates/home.html

@@ -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>
-            <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 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>
-        {% 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 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.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 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>
     </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>

+ 1 - 0
netbox/templates/users/inc/profile_nav.html

@@ -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 - 0
netbox/templates/users/recent_activity.html

@@ -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 %}

+ 1 - 0
netbox/users/urls.py

@@ -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'),
 
 ]

+ 8 - 0
netbox/users/views.py

@@ -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]
+    })