diff --git a/netbox/activity/__init__.py b/netbox/activity/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/netbox/activity/apps.py b/netbox/activity/apps.py new file mode 100644 index 000000000..34cfcc035 --- /dev/null +++ b/netbox/activity/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ActivityConfig(AppConfig): + name = 'activity' diff --git a/netbox/activity/forms.py b/netbox/activity/forms.py new file mode 100644 index 000000000..5a675d352 --- /dev/null +++ b/netbox/activity/forms.py @@ -0,0 +1,15 @@ +from utilities.forms import BootstrapMixin, CommentField +from .models import LogItem + +from django import forms + + +class CommentForm(BootstrapMixin, forms.ModelForm): + + class Meta: + fields = [ + 'body', + 'for_device', + 'created_by', + ] + model = LogItem diff --git a/netbox/activity/migrations/0001_initial.py b/netbox/activity/migrations/0001_initial.py new file mode 100644 index 000000000..158baf27b --- /dev/null +++ b/netbox/activity/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.13 on 2018-06-30 15:19 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('dcim', '0055_virtualchassis_ordering'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='LogItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('body', models.TextField(default='')), + ('created_at', models.DateTimeField(default=django.utils.timezone.now)), + ('created_by', models.ForeignKey(default='1', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ('for_device', models.ForeignKey(default='1', on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='dcim.Device')), + ], + options={ + 'verbose_name': 'comment', + 'verbose_name_plural': 'comments', + 'ordering': ['-created_at'], + }, + ), + ] diff --git a/netbox/activity/migrations/__init__.py b/netbox/activity/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/netbox/activity/models.py b/netbox/activity/models.py new file mode 100644 index 000000000..af10caa98 --- /dev/null +++ b/netbox/activity/models.py @@ -0,0 +1,26 @@ +from django.db import models +from django.utils import timezone +from django.contrib.auth.models import User + + +class LogItem(models.Model): + + for_device = models.ForeignKey( + 'dcim.Device', + on_delete=models.CASCADE, + related_name='logs', + default='1', + ) + body = models.TextField(default='') + created_at = models.DateTimeField(default=timezone.now) + created_by = models.ForeignKey( + User, + on_delete=models.SET_NULL, + null=True, + default='1' + ) + + class Meta: + verbose_name = 'comment' + verbose_name_plural = 'comments' + ordering = ['-created_at'] diff --git a/netbox/activity/urls.py b/netbox/activity/urls.py new file mode 100644 index 000000000..56f26f8a5 --- /dev/null +++ b/netbox/activity/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls import url +from . import views + + +urlpatterns = [ + url(r'^(?P\d+)/$', views.display_activity, name='display'), + url(r'^(\d+)/delete/(?P\d+)$', views.DeleteComment.as_view(), name='delete_comment'), + url(r'^(\d+)/add', views.AddComment.as_view(), name='add_comment') +] diff --git a/netbox/activity/views.py b/netbox/activity/views.py new file mode 100644 index 000000000..1bbe547e7 --- /dev/null +++ b/netbox/activity/views.py @@ -0,0 +1,61 @@ +from django.shortcuts import render, get_object_or_404 +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.utils.safestring import mark_safe +from django.shortcuts import reverse + +from dcim.models import Device +from utilities.forms import ConfirmationForm +from utilities.views import ObjectDeleteView, ObjectEditView +from . import models +from .models import LogItem +from . import forms + + +def display_activity(request, pk): + + device = get_object_or_404(Device, pk=pk) + logItems = device.logs.all() + return render(request, 'activity/displayActivity.html', { + 'device': device, + 'logItems': logItems, + }) + +class DeleteComment(PermissionRequiredMixin, ObjectDeleteView): + + permission_required = 'activity.delete_logitem' + model = LogItem + template_name = 'activity/deleteComment.html' + + def get_return_url(self, request, obj): + return reverse('activity:display', kwargs={'pk': obj.for_device.pk}) + +class AddComment(PermissionRequiredMixin, ObjectEditView): + + permission_required = 'activity.add_logitem' + model = LogItem + model_form = forms.CommentForm + template_name = 'activity/addComment.html' + + def get_return_url(self, request, obj): + return reverse('activity:display', kwargs={'pk': obj.for_device.pk}) + + def get(self, request, *args, **kwargs): + + obj = self.get_object(kwargs) + obj = self.alter_obj(obj, request, args, kwargs) + # Parse initial data manually to avoid setting field values as lists + initial_data = {k: request.GET[k] for k in request.GET} + form = self.model_form(instance=obj, initial=initial_data) + + # Prefilled fields for comments + created_by = request.user + for_device = request.build_absolute_uri().replace('http://', '').replace('https://', '').split('/')[2].split('/')[0] + + return render(request, self.template_name, { + 'obj': obj, + 'obj_type': self.model._meta.verbose_name, + 'form': form, + 'return_url': self.get_return_url(request, obj), + 'created_by': created_by, + 'for_device': for_device, + }) diff --git a/netbox/netbox/settings.py b/netbox/netbox/settings.py index 71c41063c..df9f39b37 100644 --- a/netbox/netbox/settings.py +++ b/netbox/netbox/settings.py @@ -120,6 +120,7 @@ EMAIL_SUBJECT_PREFIX = '[NetBox] ' # Installed applications INSTALLED_APPS = ( + 'activity', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', diff --git a/netbox/netbox/urls.py b/netbox/netbox/urls.py index 4907555ed..bab8aca90 100644 --- a/netbox/netbox/urls.py +++ b/netbox/netbox/urls.py @@ -25,6 +25,8 @@ schema_view = get_schema_view( _patterns = [ + url(r'^activity/', include('activity.urls', namespace='activity'), name='activity'), + # Base views url(r'^$', HomeView.as_view(), name='home'), url(r'^search/$', SearchView.as_view(), name='search'), diff --git a/netbox/templates/activity/addComment.html b/netbox/templates/activity/addComment.html new file mode 100644 index 000000000..3868d0109 --- /dev/null +++ b/netbox/templates/activity/addComment.html @@ -0,0 +1,83 @@ +{% extends '_base.html' %} +{% load form_helpers %} + +{% block title %}Add a new comment{% endblock %} + +{% block content %} +
+ {% csrf_token %} + {% for field in form.hidden_fields %} + {{ field }} + {% endfor %} +
+
+

Add a new comment

+ {% block tabs %}{% endblock %} + {% if form.non_field_errors %} +
+
Errors
+
+ {{ form.non_field_errors }} +
+
+ {% endif %} + {% block form %} +
+
Comment
+
+ + + + + {% render_form form %} + + + +
+ +
+ +
+
+ + +
+ +
+ +
+
+ + + +
+
+ {% endblock %} +
+
+
+
+ {% block buttons %} + {% if obj.pk %} + + {% else %} + + {% endif %} + Cancel + {% endblock %} +
+
+
+{% endblock %} diff --git a/netbox/templates/activity/deleteComment.html b/netbox/templates/activity/deleteComment.html new file mode 100644 index 000000000..105ced9b7 --- /dev/null +++ b/netbox/templates/activity/deleteComment.html @@ -0,0 +1,6 @@ +{% extends 'utilities/obj_delete.html' %} + +{% block message %} +

Are you sure you want to delete the comment below?

+
{{ obj.body }}
+{% endblock %} diff --git a/netbox/templates/activity/displayActivity.html b/netbox/templates/activity/displayActivity.html new file mode 100644 index 000000000..bcb18d8ed --- /dev/null +++ b/netbox/templates/activity/displayActivity.html @@ -0,0 +1,34 @@ +{% extends '_base.html' %} +{% load helpers %} + +{% block title %}{{ device }} - Activity{% endblock %} + +{% block content %} +{% include 'dcim/inc/device_header.html' with active_tab='activity' %} + +
+
+ + {% if logItems.count == 0 %} +
No activity yet.
+ {% endif %} + + {% for logItem in logItems %} +
+ {% if perms.activity.delete_logitem %} + + {% endif %} +

{% if logItem.created_by is not None %}{{ logItem.created_by }}{% endif %}{% if logItem.created_by == None %}A deleted user{% endif %} posted a comment at {{ logItem.created_at }}:

{{ logItem.body|gfm }}

+
+
+ {% endfor %} + +
+
+ {% if perms.activity.add_logitem %} + Add a comment + {% endif %} +
+
+ +{% endblock %} diff --git a/netbox/templates/dcim/inc/device_header.html b/netbox/templates/dcim/inc/device_header.html index 92acd297d..e880e0933 100644 --- a/netbox/templates/dcim/inc/device_header.html +++ b/netbox/templates/dcim/inc/device_header.html @@ -46,6 +46,9 @@ + diff --git a/netbox/templates/inc/nav_menu.html b/netbox/templates/inc/nav_menu.html index a85647993..778a55115 100644 --- a/netbox/templates/inc/nav_menu.html +++ b/netbox/templates/inc/nav_menu.html @@ -104,7 +104,7 @@ -