From e325317bde00dc1189d88c4579cc651f7094d2ce Mon Sep 17 00:00:00 2001
From: Julia Hansbrough <julia@edx.org>
Date: Fri, 4 Oct 2013 15:33:11 +0000
Subject: [PATCH] Changed GET to POST and xmodule HTML editor call, section CSS

---
 AUTHORS                                       |  1 +
 CHANGELOG.rst                                 |  2 +
 lms/djangoapps/instructor/tests/test_email.py |  1 -
 lms/djangoapps/instructor/views/api.py        | 47 +++++++++++++--
 .../instructor/views/instructor_dashboard.py  |  6 +-
 lms/envs/dev.py                               |  1 +
 .../instructor_dashboard/send_email.coffee    |  5 +-
 .../sass/course/instructor/_instructor_2.scss | 58 +++++++++++++++++++
 .../instructor_dashboard_2/send_email.html    | 56 ++++++++++--------
 9 files changed, 144 insertions(+), 33 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 94963e46304..2f4d7efeadd 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -89,3 +89,4 @@ Akshay Jagadeesh <akjags@gmail.com>
 Nick Parlante <nick.parlante@cs.stanford.edu>
 Marko Seric <marko.seric@math.uzh.ch>
 Felipe Montoya <felipe.montoya@edunext.co>
+Julia Hansbrough <julia@edx.org>
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 952a0dfd9bc..f7891ae817b 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -7,6 +7,8 @@ the top.  Include a label indicating the component affected.
 
 LMS: Disable data download buttons on the instructor dashboard for large courses
 
+LMS: Ported bulk emailing to the beta instructor dashboard.
+
 LMS: Refactor and clean student dashboard templates.
 
 LMS: Fix issue with CourseMode expiration dates
diff --git a/lms/djangoapps/instructor/tests/test_email.py b/lms/djangoapps/instructor/tests/test_email.py
index 5f664bc0e5f..1150c575fed 100644
--- a/lms/djangoapps/instructor/tests/test_email.py
+++ b/lms/djangoapps/instructor/tests/test_email.py
@@ -53,7 +53,6 @@ class TestInstructorDashboardEmailView(ModuleStoreTestCase):
 
     @patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
     def test_email_flag_true(self):
-        from nose.tools import set_trace; set_trace()
         # Assert that the URL for the email view is in the response
         response = self.client.get(self.url)
         self.assertTrue(self.email_link in response.content)
diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py
index 25e070d01a2..e7f394cea15 100644
--- a/lms/djangoapps/instructor/views/api.py
+++ b/lms/djangoapps/instructor/views/api.py
@@ -106,6 +106,43 @@ def require_query_params(*args, **kwargs):
         return wrapped
     return decorator
 
+def require_post_params(*args, **kwargs):
+    """
+    Checks for required paremters or renders a 400 error.
+    (decorator with arguments)
+
+    `args` is a *list of required GET parameter names.
+    `kwargs` is a **dict of required GET parameter names
+        to string explanations of the parameter
+    """
+    required_params = []
+    required_params += [(arg, None) for arg in args]
+    required_params += [(key, kwargs[key]) for key in kwargs]
+    # required_params = e.g. [('action', 'enroll or unenroll'), ['emails', None]]
+
+    def decorator(func):  # pylint: disable=C0111
+        def wrapped(*args, **kwargs):  # pylint: disable=C0111
+            request = args[0]
+
+            error_response_data = {
+                'error': 'Missing required query parameter(s)',
+                'parameters': [],
+                'info': {},
+            }
+
+            for (param, extra) in required_params:
+                default = object()
+                if request.POST.get(param, default) == default:
+                    error_response_data['parameters'] += [param]
+                    error_response_data['info'][param] = extra
+
+            if len(error_response_data['parameters']) > 0:
+                return JsonResponse(error_response_data, status=400)
+            else:
+                return func(*args, **kwargs)
+        return wrapped
+    return decorator
+
 
 def require_level(level):
     """
@@ -749,19 +786,19 @@ def send_email(request, course_id):
 @ensure_csrf_cookie
 @cache_control(no_cache=True, no_store=True, must_revalidate=True)
 @require_level('staff')
-@require_query_params(send_to="sending to whom", subject="subject line", message="message text")
+@require_post_params(send_to="sending to whom", subject="subject line", message="message text")
 def send_email(request, course_id):
     """
     Send an email to self, staff, or everyone involved in a course.
-    Query Paramaters:
+    Query Parameters:
     - 'send_to' specifies what group the email should be sent to
     - 'subject' specifies email's subject
     - 'message' specifies email's content
     """
     course = get_course_by_id(course_id)
-    send_to = request.GET.get("send_to")
-    subject = request.GET.get("subject")
-    message = request.GET.get("message")
+    send_to = request.POST.get("send_to")
+    subject = request.POST.get("subject")
+    message = request.POST.get("message")
     text_message = html_to_text(message)
     email = CourseEmail(
         course_id=course_id,
diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py
index 4c24e0e4286..4bdce87f4e0 100644
--- a/lms/djangoapps/instructor/views/instructor_dashboard.py
+++ b/lms/djangoapps/instructor/views/instructor_dashboard.py
@@ -138,6 +138,7 @@ def _section_student_admin(course_id, access):
         'section_display_name': _('Student Admin'),
         'access': access,
         'get_student_progress_url_url': reverse('get_student_progress_url', kwargs={'course_id': course_id}),
+        'enrollment_url': reverse('students_update_enrollment', kwargs={'course_id': course_id}),
         'reset_student_attempts_url': reverse('reset_student_attempts', kwargs={'course_id': course_id}),
         'rescore_problem_url': reverse('rescore_problem', kwargs={'course_id': course_id}),
         'list_instructor_tasks_url': reverse('list_instructor_tasks', kwargs={'course_id': course_id}),
@@ -160,12 +161,15 @@ def _section_data_download(course_id):
 def _section_send_email(course_id, access, course):
     """ Provide data for the corresponding bulk email section """
     html_module = HtmlDescriptor(course.system, DictFieldData({'data': ''}), ScopeIds(None, None, None, None))
+    fragment = course.system.render(html_module, None, 'studio_view')
+    fragment = wrap_xmodule('xmodule_edit.html', html_module, 'studio_view', fragment, None)
+    email_editor = fragment.content
     section_data = {
         'section_key': 'send_email',
         'section_display_name': _('Email'),
         'access': access, 
         'send_email': reverse('send_email',kwargs={'course_id': course_id}),
-        'editor': wrap_xmodule(html_module.get_html, html_module, 'xmodule_edit.html')()
+        'editor': email_editor
     }
     return section_data
 
diff --git a/lms/envs/dev.py b/lms/envs/dev.py
index 4d46dc52e2d..e873861196d 100644
--- a/lms/envs/dev.py
+++ b/lms/envs/dev.py
@@ -29,6 +29,7 @@ MITX_FEATURES['ENABLE_MANUAL_GIT_RELOAD'] = True
 MITX_FEATURES['ENABLE_PSYCHOMETRICS'] = False    # real-time psychometrics (eg item response theory analysis in instructor dashboard)
 MITX_FEATURES['ENABLE_INSTRUCTOR_ANALYTICS'] = True
 MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True
+MITX_FEATURES['ENABLE_INSTRUCTOR_EMAIL'] = True
 MITX_FEATURES['ENABLE_HINTER_INSTRUCTOR_VIEW'] = True
 MITX_FEATURES['ENABLE_INSTRUCTOR_BETA_DASHBOARD'] = True
 MITX_FEATURES['MULTIPLE_ENROLLMENT_ROLES'] = True
diff --git a/lms/static/coffee/src/instructor_dashboard/send_email.coffee b/lms/static/coffee/src/instructor_dashboard/send_email.coffee
index af509a7d525..c8b0588b5d9 100644
--- a/lms/static/coffee/src/instructor_dashboard/send_email.coffee
+++ b/lms/static/coffee/src/instructor_dashboard/send_email.coffee
@@ -22,6 +22,8 @@ class SendEmail
     # attach click handlers
 
     @$btn_send.click =>
+
+      success_message = gettext('Your email was successfully queued for sending.')
       
       send_data =
         action: 'send'
@@ -30,10 +32,11 @@ class SendEmail
         message: @$emailEditor.save()['data']
 
       $.ajax
+        type: 'POST'
         dataType: 'json'
         url: @$btn_send.data 'endpoint'
         data: send_data
-        success: (data) => @display_response gettext('Your email was successfully queued for sending.')
+        success: (data) => @display_response ("<div class=\"msg msg-confirm\"><p class=\"copy\">" + success_message + "</p></div>")
         error: std_ajax_err => @fail_with_error gettext('Error sending email.')
 
   fail_with_error: (msg) ->
diff --git a/lms/static/sass/course/instructor/_instructor_2.scss b/lms/static/sass/course/instructor/_instructor_2.scss
index 19f6abf5ed7..c9a4c79aa8f 100644
--- a/lms/static/sass/course/instructor/_instructor_2.scss
+++ b/lms/static/sass/course/instructor/_instructor_2.scss
@@ -241,6 +241,60 @@ section.instructor-dashboard-content-2 {
   }
 }
 
+.instructor-dashboard-wrapper-2 section.idash-section#send_email {
+  // form fields
+  .list-fields {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+
+    .field {
+      margin-bottom: 20px;
+      padding: 0;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+
+  // system feedback - messages
+  .msg {
+
+
+    .copy {
+      font-weight: 600;
+    }
+  }
+
+  .msg-confirm {
+    background: tint(green,90%);
+
+    .copy {
+      color: green;
+    }
+  }
+
+  .list-advice {
+    list-style: none;
+    padding: 0;
+    margin: 20px 0;
+
+    .item {
+      font-weight: 600;
+      margin-bottom: 10px;
+
+      &:last-child {
+        margin-bottom: 0;
+      }
+    }
+  }
+  .msg .copy {
+      font-weight: 600; }
+  .msg-confirm {
+    background: #e5f2e5; }
+}
+
 
 .instructor-dashboard-wrapper-2 section.idash-section#membership {
   $half_width: $baseline * 20;
@@ -538,3 +592,7 @@ section.instructor-dashboard-content-2 {
     right: $baseline;
   }
 }
+
+input[name="subject"] {
+  width:600px;
+}
diff --git a/lms/templates/instructor/instructor_dashboard_2/send_email.html b/lms/templates/instructor/instructor_dashboard_2/send_email.html
index 68fd0938a17..5a11bcf2073 100644
--- a/lms/templates/instructor/instructor_dashboard_2/send_email.html
+++ b/lms/templates/instructor/instructor_dashboard_2/send_email.html
@@ -6,38 +6,44 @@
 <script type="text/javascript" src="jsi18n/"></script>
 <div class="vert-left send-email">
   <h2> ${_("Send Email")} </h2>
-  <label for="id_to">${_("Send to:")}</label>
-  <select id="id_to" name="send_to">
-    <option value="myself">${_("Myself")}</option>
-    %if to_option == "staff":
-      <option value="staff" selected="selected">${_("Staff and instructors")}</option>
-    %else:
-      <option value="staff">${_("Staff and instructors")}</option>
-    %endif
-    %if to_option == "all":
-      <option value="all" selected="selected">${_("All (students, staff and instructors)")}</option>
-    %else:
+  <div class="request-response msg msg-confirm"></div>
+  <ul class="list-fields">
+    <li class="field">
+      <label for="id_to">${_("Send to:")}</label><br/>
+      <select id="id_to" name="send_to">
+        <option value="myself">${_("Myself")}</option>
+        %if to_option == "staff":
+        <option value="staff" selected="selected">${_("Staff and instructors")}</option>
+        %else:
+        <option value="staff">${_("Staff and instructors")}</option>
+          %endif
+          %if to_option == "all":
+            <option value="all" selected="selected">${_("All (students, staff and instructors)")}</option>
+          %else:
       <option value="all">${_("All (students, staff and instructors)")}</option>
-    %endif
-  </select>
-  <br/>
-  <label for="id_subject">${_("Subject: ")}</label>
-  <input type="text" id="id_subject" name="subject">
-  <br/>
-  <label>Message:</label>
-  <div class="email-editor"> 
-  ${ section_data['editor'] } 
-  </div>
-  <input type="hidden" name="message" value="">
+      %endif
+      </select>
+    </li>
   <br/>
+  <li class="field">
+    <label for="id_subject">${_("Subject: ")}</label><br/>
+    <input type="text" id="id_subject" name="subject">
+  </li>
+  <li class="field">
+    <label>Message:</label>
+      <div class="email-editor"> 
+      ${ section_data['editor'] } 
+      </div>
+    <input type="hidden" name="message" value="">
+  </li>
+  </ul>
  <div class="submit-email-action">
   ${_("Please try not to email students more than once a day. Before sending your email, consider:")}
-  <ul>
+  <ul class="list-advice">
    <li class="item">${_("Have you read over the email to make sure it says everything you want to say?")}</li>
    <li class="item">${_("Have you sent the email to yourself first to make sure you're happy with how it's displayed?")}</li>
   </ul>
  </div>
-  <input type="button" name="send" value="${_("Send")}" data-endpoint="${ section_data['send_email'] }" >
-  <div class="request-response"></div>
+  <input type="button" name="send" value="${_("Send Email")}" data-endpoint="${ section_data['send_email'] }" >
   <div class="request-response-error"></div>
 </div>
\ No newline at end of file
-- 
GitLab