Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion lms/djangoapps/instructor/views/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@
import analytics.distributions
import analytics.csvs

from bulk_email.models import CourseEmail
from html_to_text import html_to_text
from bulk_email import tasks

from pudb import set_trace

log = logging.getLogger(__name__)


Expand Down Expand Up @@ -664,6 +670,45 @@ def extract_user_info(user):
}
return JsonResponse(response_payload)

@cache_control(no_cache=True, no_store=True, must_revalidate=True)
# todo check if staff is the desired access level
# todo do html and plaintext messages
@require_level('staff')
@require_query_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:
- 'send_to' specifies what group the email should be sent to
- 'subject' specifies email's subject
- 'message' specifies email's content
"""
set_trace()
course = get_course_by_id(course_id)
has_instructor_access = has_access(request.user, course, 'instructor')
send_to = request.GET.get("send_to")
subject = request.GET.get("subject")
message = request.GET.get("message")
text_message = html_to_text(message)
if subject == "":
return HttpResponseBadRequest("Operation requires instructor access.")
email = CourseEmail(
course_id = course_id,
sender=request.user,
to_option=send_to,
subject=subject,
html_message=message,
text_message=text_message
)
email.save()
tasks.delegate_email_batches.delay(
email.id,
request.user.id
)
response_payload = {
'course_id': course_id,
}
return JsonResponse(response_payload)

@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
Expand Down Expand Up @@ -728,7 +773,6 @@ def update_forum_role_membership(request, course_id):
}
return JsonResponse(response_payload)


@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
Expand Down
3 changes: 2 additions & 1 deletion lms/djangoapps/instructor/views/api_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
Instructor API endpoint urls.
"""


from django.conf.urls import patterns, url

urlpatterns = patterns('', # nopep8
Expand Down Expand Up @@ -32,4 +31,6 @@
'instructor.views.api.update_forum_role_membership', name="update_forum_role_membership"),
url(r'^proxy_legacy_analytics$',
'instructor.views.api.proxy_legacy_analytics', name="proxy_legacy_analytics"),
url(r'^send_email$',
'instructor.views.api.send_email', name="send_email")
)
20 changes: 18 additions & 2 deletions lms/djangoapps/instructor/views/instructor_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@
from django.utils.html import escape
from django.http import Http404

from xmodule_modifiers import wrap_xmodule
from xmodule.html_module import HtmlDescriptor
from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds
from courseware.access import has_access
from courseware.courses import get_course_by_id
from django_comment_client.utils import has_forum_access
from django_comment_common.models import FORUM_ROLE_ADMINISTRATOR
from xmodule.modulestore.django import modulestore
from student.models import CourseEnrollment


@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def instructor_dashboard_2(request, course_id):
Expand All @@ -42,7 +45,8 @@ def instructor_dashboard_2(request, course_id):
_section_membership(course_id, access),
_section_student_admin(course_id, access),
_section_data_download(course_id),
_section_analytics(course_id),
_section_send_email(course_id, access,course),
_section_analytics(course_id)
]

context = {
Expand Down Expand Up @@ -136,6 +140,18 @@ def _section_data_download(course_id):
}
return section_data

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))
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')()
}
return section_data


def _section_analytics(course_id):
""" Provide data for the corresponding dashboard section """
Expand Down
2 changes: 0 additions & 2 deletions lms/djangoapps/instructor/views/legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,10 @@
from xblock.field_data import DictFieldData
from xblock.fields import ScopeIds


from bulk_email.models import CourseEmail
from html_to_text import html_to_text
from bulk_email import tasks


log = logging.getLogger(__name__)

# internal commands for managing forum roles:
Expand Down
2 changes: 1 addition & 1 deletion lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
# analytics experiments
'ENABLE_INSTRUCTOR_ANALYTICS': False,

'ENABLE_INSTRUCTOR_EMAIL': False,
'ENABLE_INSTRUCTOR_EMAIL': True,

# enable analytics server.
# WARNING: THIS SHOULD ALWAYS BE SET TO FALSE UNDER NORMAL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ setup_instructor_dashboard_sections = (idash_content) ->
,
constructor: window.InstructorDashboard.sections.StudentAdmin
$element: idash_content.find ".#{CSS_IDASH_SECTION}#student_admin"
,
constructor: window.InstructorDashboard.sections.Email
$element: idash_content.find ".#{CSS_IDASH_SECTION}#send_email"
,
constructor: window.InstructorDashboard.sections.Analytics
$element: idash_content.find ".#{CSS_IDASH_SECTION}#analytics"
Expand Down
73 changes: 73 additions & 0 deletions lms/static/coffee/src/instructor_dashboard/send_email.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Email Section

# imports from other modules.
# wrap in (-> ... apply) to defer evaluation
# such that the value can be defined later than this assignment (file load order).
plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, arguments
std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments

class SendEmail
constructor: (@$container) ->
# gather elements
@$emailEditor = XModule.loadModule($('.xmodule_edit'));
@$send_to = @$container.find("select[name='send_to']'")
@$subject = @$container.find("input[name='subject']'")
#message = emailEditor.save()['data']
@$btn_send = @$container.find("input[name='send']'")
@$task_response = @$container.find(".request-response")
@$request_response_error = @$container.find(".request-response-error")

# attach click handlers

@$btn_send.click =>

send_data =
action: 'send'
send_to: @$send_to.val()
subject: @$subject.val()
message: @$emailEditor.save()['data']
#message: @$message.val()

$.ajax
dataType: 'json'
url: @$btn_send.data 'endpoint'
data: send_data
success: (data) => @display_response "Your email was successfully queued for sending."
error: std_ajax_err => @fail_with_error "Error sending email."

fail_with_error: (msg) ->
console.warn msg
@$task_response.empty()
@$request_response_error.empty()
@$request_response_error.text msg

display_response: (data_from_server) ->
@$task_response.empty()
@$request_response_error.empty()
@$task_response.text("Your email was successfully queued for sending.")


# Email Section
class Email
# enable subsections.
constructor: (@$section) ->
# attach self to html
# so that instructor_dashboard.coffee can find this object
# to call event handlers like 'onClickTitle'
@$section.data 'wrapper', @

# isolate # initialize SendEmail subsection
plantTimeout 0, => new SendEmail @$section.find '.send-email'

# handler for when the section title is clicked.
onClickTitle: ->


# export for use
# create parent namespaces if they do not already exist.
# abort if underscore can not be found.
if _?
_.defaults window, InstructorDashboard: {}
_.defaults window.InstructorDashboard, sections: {}
_.defaults window.InstructorDashboard.sections,
Email: Email
4 changes: 4 additions & 0 deletions lms/static/sass/course/instructor/_instructor_2.scss
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ section.instructor-dashboard-content-2 {
}
}

.instructor-dashboard-wrapper-2 section.idash-section#email {
// todo
}


.instructor-dashboard-wrapper-2 section.idash-section#course_info {
.course-errors-wrapper {
Expand Down
65 changes: 65 additions & 0 deletions lms/templates/instructor/instructor_dashboard_2/email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<%! from django.utils.translation import ugettext as _ %>
<%page args="section_data"/>

<h2>Email</h2>

<form>
<ul class="list-fields">
<li class="field">
<label for="id_to">${_("Send to:")}</label>
<select id="id_to" name="to_option">
<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>
</li>
<li class="field">
<label for="id_subject">${_("Subject: ")}</label>
%if subject:
<input type="text" id="id_subject" name="subject" maxlength="100" size="75" value="${subject}">
%else:
<input type="text" id="id_subject" name="subject" maxlength="100" size="75">
%endif
</li>

<li class="field">
<label>Message:</label>
<div class="email-editor">

<!--todo make this render the real way-->

<section class="xmodule_edit xmodule_HtmlDescriptor" data-type="HTMLEditingDescriptor">


<section class="html-editor editor">
<div class="row">
<textarea class="tiny-mce">&lt;p&gt;hihihi&lt;/p&gt;</textarea>
</div>
</section>

</section>


</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. Important things to consider before sending:")}
<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>
<input type="submit" name="send-email" value="Send email" data-endpoint="${ section_data[get_send_email_url']}">
</div>

</form>
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@
<script type="text/javascript" src="${static.url('js/vendor/slick.grid.js')}"></script>
<link rel="stylesheet" href="${static.url('css/vendor/slickgrid/smoothness/jquery-ui-1.8.16.custom.css')}">
<link rel="stylesheet" href="${static.url('css/vendor/slickgrid/slick.grid.css')}">
<script type="text/javascript" src="${static.url('js/vendor/CodeMirror/htmlmixed.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/CodeMirror/css.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/codemirror-compressed.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/tiny_mce/tiny_mce.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/tiny_mce/jquery.tinymce.js')}"></script>
<%static:js group='module-descriptor-js'/>
</%block>

## NOTE that instructor is set as the active page so that the instructor button lights up, even though this is the instructor_2 page.
Expand Down
35 changes: 35 additions & 0 deletions lms/templates/instructor/instructor_dashboard_2/send_email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<%! from django.utils.translation import ugettext as _ %>
<%page args="section_data"/>

<script language="JavaScript" type="text/javascript">
</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:
<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="">
<br/>
<input type="button" name="send" value="${_("Send")}" data-endpoint="${ section_data['send_email'] }" >
<div class="request-response"></div>
<div class="request-response-error"></div>
</div>