From 9cf542902d72deba57bf5a8ce08bd984dcdac07e Mon Sep 17 00:00:00 2001
From: Kálmán Viktor <kviktor@cloud.bme.hu>
Date: Fri, 20 Mar 2015 13:22:55 +0100
Subject: [PATCH] request: add reason for decline

---
 circle/dashboard/static/dashboard/dashboard.less              | 11 +++++++++++
 circle/dashboard/templates/dashboard/vm-detail/resources.html |  4 ++--
 circle/request/forms.py                                       |  8 ++++----
 circle/request/migrations/0001_initial.py                     |  8 +++++---
 circle/request/migrations/0002_auto_20150317_1211.py          | 28 ----------------------------
 circle/request/migrations/0003_auto_20150318_1023.py          | 20 --------------------
 circle/request/models.py                                      | 14 +++++++++-----
 circle/request/templates/request/_request-lease-form.html     |  2 +-
 circle/request/templates/request/_request-template-form.html  |  2 +-
 circle/request/templates/request/detail.html                  | 44 +++++++++++++++++++++++++++-----------------
 circle/request/templates/request/request-resource.html        |  2 +-
 circle/request/views.py                                       | 31 +++++++++++++++++++------------
 12 files changed, 80 insertions(+), 94 deletions(-)
 delete mode 100644 circle/request/migrations/0002_auto_20150317_1211.py
 delete mode 100644 circle/request/migrations/0003_auto_20150318_1023.py

diff --git a/circle/dashboard/static/dashboard/dashboard.less b/circle/dashboard/static/dashboard/dashboard.less
index f845de0..e7275c8 100644
--- a/circle/dashboard/static/dashboard/dashboard.less
+++ b/circle/dashboard/static/dashboard/dashboard.less
@@ -1296,3 +1296,14 @@ textarea[name="new_members"] {
     text-align: center;
   }
 }
+
+#request-buttons {
+ form {
+    display: inline;
+ }
+
+ textarea {
+    resize: none;
+    min-height: 80px;
+ }
+}
diff --git a/circle/dashboard/templates/dashboard/vm-detail/resources.html b/circle/dashboard/templates/dashboard/vm-detail/resources.html
index 25e28cc..025118f 100644
--- a/circle/dashboard/templates/dashboard/vm-detail/resources.html
+++ b/circle/dashboard/templates/dashboard/vm-detail/resources.html
@@ -26,8 +26,8 @@
       {% trans "Changing resources is only possible on virtual machines with STOPPED state. We suggest to turn off the VM after submitting the request otherwise it will be automatically stopped in the future when the request is accepted." %}
     </div>
     <div class="form-group">
-      <label>{% trans "Reason" %}*</label>
-      <textarea class="form-control" name="reason">{% include "request/initials/resources.html" %}</textarea>
+      <label>{% trans "Message" %}*</label>
+      <textarea class="form-control" name="message">{% include "request/initials/resources.html" %}</textarea>
     </div>
     <input type="submit" class="btn btn-success btn-sm"/>
   </div>
diff --git a/circle/request/forms.py b/circle/request/forms.py
index 077469b..0cc6aea 100644
--- a/circle/request/forms.py
+++ b/circle/request/forms.py
@@ -63,7 +63,7 @@ class InitialFromFileMixin(object):
         request = kwargs.pop("request", None)
         super(InitialFromFileMixin, self).__init__(*args, **kwargs)
 
-        self.initial['reason'] = render_to_string(
+        self.initial['message'] = render_to_string(
             self.initial_template,
             RequestContext(request, {}),
         )
@@ -74,17 +74,17 @@ class TemplateRequestForm(InitialFromFileMixin, Form):
                                 label=_("Template share"))
     level = ChoiceField(TemplateAccessAction.LEVELS, widget=RadioSelect,
                         initial=TemplateAccessAction.LEVELS.user)
-    reason = CharField(widget=forms.Textarea, label=_("Reason"))
+    message = CharField(widget=forms.Textarea, label=_("Message"))
 
     initial_template = "request/initials/template.html"
 
 
 class LeaseRequestForm(InitialFromFileMixin, Form):
     lease = ModelChoiceField(LeaseType.objects.all(), label=_("Lease"))
-    reason = CharField(widget=forms.Textarea)
+    message = CharField(widget=forms.Textarea)
 
     initial_template = "request/initials/lease.html"
 
 
 class ResourceRequestForm(VmResourcesForm):
-    reason = CharField(widget=forms.Textarea)
+    message = CharField(widget=forms.Textarea)
diff --git a/circle/request/migrations/0001_initial.py b/circle/request/migrations/0001_initial.py
index 999ed69..dd10a0f 100644
--- a/circle/request/migrations/0001_initial.py
+++ b/circle/request/migrations/0001_initial.py
@@ -46,12 +46,14 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)),
                 ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)),
-                ('status', models.CharField(default=b'UNSEEN', max_length=10, choices=[(b'UNSEEN', 'unseen'), (b'PENDING', 'pending'), (b'ACCEPTED', 'accepted'), (b'DECLINED', 'declined')])),
+                ('status', models.CharField(default=b'PENDING', max_length=10, choices=[(b'PENDING', 'pending'), (b'ACCEPTED', 'accepted'), (b'DECLINED', 'declined')])),
                 ('type', models.CharField(max_length=10, choices=[(b'resource', 'resource request'), (b'lease', 'lease request'), (b'template', 'template access')])),
-                ('reason', models.TextField(help_text=b'szia')),
+                ('message', models.TextField(verbose_name='Message')),
+                ('reason', models.TextField(verbose_name='Reason')),
                 ('object_id', models.IntegerField()),
+                ('closed_by', models.ForeignKey(related_name='closed_by', to=settings.AUTH_USER_MODEL, null=True)),
                 ('content_type', models.ForeignKey(to='contenttypes.ContentType')),
-                ('user', models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL)),
+                ('user', models.ForeignKey(related_name='user', to=settings.AUTH_USER_MODEL)),
             ],
             options={
                 'abstract': False,
diff --git a/circle/request/migrations/0002_auto_20150317_1211.py b/circle/request/migrations/0002_auto_20150317_1211.py
deleted file mode 100644
index 5cbd042..0000000
--- a/circle/request/migrations/0002_auto_20150317_1211.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import models, migrations
-from django.conf import settings
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
-        ('request', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='request',
-            name='closed_by',
-            field=models.ForeignKey(related_name='closed_by', to=settings.AUTH_USER_MODEL, null=True),
-            preserve_default=True,
-        ),
-        migrations.AlterField(
-            model_name='request',
-            name='user',
-            field=models.ForeignKey(related_name='user', to=settings.AUTH_USER_MODEL),
-            preserve_default=True,
-        ),
-    ]
diff --git a/circle/request/migrations/0003_auto_20150318_1023.py b/circle/request/migrations/0003_auto_20150318_1023.py
deleted file mode 100644
index 1ca5daf..0000000
--- a/circle/request/migrations/0003_auto_20150318_1023.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from django.db import models, migrations
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('request', '0002_auto_20150317_1211'),
-    ]
-
-    operations = [
-        migrations.AlterField(
-            model_name='request',
-            name='reason',
-            field=models.TextField(verbose_name='Reason'),
-            preserve_default=True,
-        ),
-    ]
diff --git a/circle/request/models.py b/circle/request/models.py
index 7701965..4be8f18 100644
--- a/circle/request/models.py
+++ b/circle/request/models.py
@@ -52,12 +52,11 @@ class RequestType(Model):
 
 class Request(TimeStampedModel):
     STATUSES = Choices(
-        ('UNSEEN', _("unseen")),
         ('PENDING', _('pending')),
         ('ACCEPTED', _('accepted')),
         ('DECLINED', _('declined')),
     )
-    status = CharField(choices=STATUSES, default=STATUSES.UNSEEN,
+    status = CharField(choices=STATUSES, default=STATUSES.PENDING,
                        max_length=10)
     user = ForeignKey(User, related_name="user")
     closed_by = ForeignKey(User, related_name="closed_by", null=True)
@@ -67,6 +66,7 @@ class Request(TimeStampedModel):
         ('template', _("template access")),
     )
     type = CharField(choices=TYPES, max_length=10)
+    message = TextField(verbose_name=_("Message"))
     reason = TextField(verbose_name=_("Reason"))
 
     content_type = ForeignKey(ContentType)
@@ -91,7 +91,6 @@ class Request(TimeStampedModel):
 
     def get_effect(self):
         return {
-            "UNSEEN": "primary",
             "PENDING": "warning",
             "ACCEPTED": "success",
             "DECLINED": "danger",
@@ -99,7 +98,6 @@ class Request(TimeStampedModel):
 
     def get_status_icon(self):
         return {
-            "UNSEEN": "eye-slash",
             "PENDING": "exclamation-triangle",
             "ACCEPTED": "check",
             "DECLINED": "times",
@@ -111,9 +109,10 @@ class Request(TimeStampedModel):
         self.closed_by = user
         self.save()
 
-    def decline(self, user):
+    def decline(self, user, reason):
         self.status = "DECLINED"
         self.closed_by = user
+        self.reason = reason
         self.save()
 
 
@@ -202,5 +201,10 @@ def send_notification_to_superusers(sender, instance, created, **kwargs):
             ugettext_noop("New %(request_type)s"), notification_msg, context
         )
 
+    instance.user.profile.notify(
+        ugettext_noop("Request submitted"),
+        ugettext_noop('You can view the request\'s status at this '
+                      '<a href="%(request_url)s">link</a>.'), context
+    )
 
 post_save.connect(send_notification_to_superusers, sender=Request)
diff --git a/circle/request/templates/request/_request-lease-form.html b/circle/request/templates/request/_request-lease-form.html
index 1c7eecc..2eac032 100644
--- a/circle/request/templates/request/_request-lease-form.html
+++ b/circle/request/templates/request/_request-lease-form.html
@@ -5,6 +5,6 @@
   {% include "display-form-errors.html" %}
   {% csrf_token %}
   {{ form.lease|as_crispy_field }}
-  {{ form.reason|as_crispy_field }}
+  {{ form.message|as_crispy_field }}
   <input type="submit" class="btn btn-primary"/>
 </form>
diff --git a/circle/request/templates/request/_request-template-form.html b/circle/request/templates/request/_request-template-form.html
index 18476e3..a7cc90e 100644
--- a/circle/request/templates/request/_request-template-form.html
+++ b/circle/request/templates/request/_request-template-form.html
@@ -21,6 +21,6 @@
     </label>
   </div>
   {% endfor %}
-  {{ form.reason|as_crispy_field }}
+  {{ form.message|as_crispy_field }}
   <input type="submit" class="btn btn-primary"/>
 </form>
diff --git a/circle/request/templates/request/detail.html b/circle/request/templates/request/detail.html
index e19008b..cb5ca6c 100644
--- a/circle/request/templates/request/detail.html
+++ b/circle/request/templates/request/detail.html
@@ -12,9 +12,11 @@
   <div class="col-md-12">
     <div class="panel panel-default">
       <div class="panel-heading">
-        <a href="{% url "request.views.request-list" %}" class="btn btn-default btn-xs pull-right">
-          {% trans "Back" %}
-        </a>
+        {% if request.user.is_superuser %}
+          <a href="{% url "request.views.request-list" %}" class="btn btn-default btn-xs pull-right">
+            {% trans "Back" %}
+          </a>
+        {% endif %}
         <h3 class="no-margin">
           <i class="fa fa-{{ object.get_request_icon }}"></i>
           {{ object.get_readable_type|capfirst }}
@@ -32,7 +34,7 @@
           </a>
         </p>
         <p>
-          <pre>{{ object.reason }}</pre>
+          <pre>{{ object.message }}</pre>
         </p>
         <hr />
         {% if object.type == "lease" %}
@@ -87,36 +89,44 @@
           hacks!!!
         {% endif %}
 
-        {% if object.status == "PENDING" %}
+        {% if object.status == "PENDING" and request.user.is_superuser %}
           <hr />
 
-          <div class="pull-right">
-            <form method="POST" style="display: inline;">
+          <div class="pull-right" id="request-buttons">
+            <form method="POST">
               {% csrf_token %}
+              <p>
+              <textarea class="form-control" placeholder="{% trans "Reason (sent to the user if the request is declined)" %}" name="reason"></textarea>
+              </p>
               <button class="btn btn-danger" type="submit">
                 <i class="fa fa-thumbs-down"></i>
                 {% trans "Decline" %}
               </button>
             </form>
-            {{ acceptable_statuses }}
             {% if object.type == "resource" and action.instance.status not in accept_states %}
               {% trans "You can't accept this request because of the VM's state." %}
             {% else %}
-            <form method="POST" style="display: inline;">
-              {% csrf_token %}
-              <input type="hidden" name="accept" value="1"/>
-              <button class="btn btn-success">
-                <i class="fa fa-thumbs-up"></i>
-                {% trans "Accept" %}
-              </button>
-            </form>
+              <form method="POST">
+                {% csrf_token %}
+                <input type="hidden" name="accept" value="1"/>
+                <button class="btn btn-success">
+                  <i class="fa fa-thumbs-up"></i>
+                  {% trans "Accept" %}
+                </button>
+              </form>
             {% endif %}
           </div>
-        {% else %}
+        {% endif %}
+        {% if object.status != "PENDING" %}
           <div class="text-right">
             {% blocktrans with closed=object.modified|arrowfilter:LANGUAGE_CODE user=object.closed_by.profile.get_display_name %}
             Closed {{ closed }} by <a href="{{ user.profile.get_absolute_url }}">{{ user }}</a>
             {% endblocktrans %}
+            {% if object.status == "DECLINED" %}
+              <p>
+                <strong>{% trans "Reason" %}:</strong> {{ object.reason }}
+              </p>
+            {% endif %}
           </div>
         {% endif %}
       </div><!-- .panel-body -->
diff --git a/circle/request/templates/request/request-resource.html b/circle/request/templates/request/request-resource.html
index 616df19..f3dbdd5 100644
--- a/circle/request/templates/request/request-resource.html
+++ b/circle/request/templates/request/request-resource.html
@@ -24,7 +24,7 @@
           </div>
           {% include "display-form-errors.html" %}
           {% include "dashboard/_resources-sliders.html" with field_priority=form.priority field_num_cores=form.num_cores field_ram_size=form.ram_size %}
-          {{ form.reason|as_crispy_field }}
+          {{ form.message|as_crispy_field }}
           <button type="submit" class="btn btn-success">
             {% trans "Request new resources" %}
           </button>
diff --git a/circle/request/views.py b/circle/request/views.py
index 14b43ef..b9154f7 100644
--- a/circle/request/views.py
+++ b/circle/request/views.py
@@ -20,7 +20,7 @@ from django.views.generic import (
     UpdateView, TemplateView, DetailView, CreateView, FormView,
 )
 from django.shortcuts import redirect, get_object_or_404
-from django.core.exceptions import PermissionDenied
+from django.core.exceptions import PermissionDenied, SuspiciousOperation
 
 from braces.views import SuperuserRequiredMixin, LoginRequiredMixin
 from django_tables2 import SingleTableView
@@ -59,32 +59,39 @@ class RequestList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
         return data
 
 
-class RequestDetail(LoginRequiredMixin, SuperuserRequiredMixin, DetailView):
+class RequestDetail(LoginRequiredMixin, DetailView):
     model = Request
     template_name = "request/detail.html"
 
     def post(self, *args, **kwargs):
-        if self.get_object().status in ["PENDING", "UNSEEN"]:
-            user = self.request.user
+        user = self.request.user
+        request = self.get_object()  # not self.request!
+
+        if not user.is_superuser:
+            raise SuspiciousOperation
+
+        if self.get_object().status == "PENDING":
             accept = self.request.POST.get("accept")
-            request = self.get_object()  # not self.request!
+            reason = self.request.POST.get("reason")
             if accept:
                 request.accept(user)
             else:
-                request.decline(user)
+                request.decline(user, reason)
 
         return redirect(request.get_absolute_url())
 
     def get_context_data(self, **kwargs):
         request = self.object
+        user = self.request.user
+
+        if not user.is_superuser and request.user != user:
+            raise SuspiciousOperation
+
         context = super(RequestDetail, self).get_context_data(**kwargs)
 
         context['action'] = request.action
         context['accept_states'] = ResourcesRequestOperation.accept_states
 
-        if request.status == Request.STATUSES.UNSEEN:
-            request.status = Request.STATUSES.PENDING
-            request.save()
         return context
 
 
@@ -151,7 +158,7 @@ class TemplateRequestView(FormView):
 
         req = Request(
             user=user,
-            reason=data['reason'],
+            message=data['message'],
             type=Request.TYPES.template,
             action=ta
         )
@@ -203,7 +210,7 @@ class LeaseRequestView(VmRequestMixin, FormView):
 
         req = Request(
             user=user,
-            reason=data['reason'],
+            message=data['message'],
             type=Request.TYPES.lease,
             action=el
         )
@@ -245,7 +252,7 @@ class ResourceRequestView(VmRequestMixin, FormView):
 
         req = Request(
             user=user,
-            reason=data['reason'],
+            message=data['message'],
             type=Request.TYPES.resource,
             action=rc
         )
--
libgit2 0.26.0