import json
+from django.core import serializers
from django.core.exceptions import MultipleObjectsReturned
from django.core.exceptions import ObjectDoesNotExist
from karmaworld.apps.courses.models import School
from karmaworld.apps.notes.models import Note
from karmaworld.apps.users.models import CourseKarmaEvent
-from karmaworld.utils import ajax_increment, format_session_increment_field
+from karmaworld.utils.ajax_increment import *
FLAG_FIELD = 'flags'
return kwargs
-
class AboutView(TemplateView):
""" Display the About page with the Schools leaderboard """
template_name = "about.html"
"""Record that somebody has flagged a note."""
return ajax_increment(Course, request, pk, FLAG_FIELD, process_course_flag_events)
+def edit_course(request, pk):
+ """
+ Saves the edited course content
+ """
+ course = Course.objects.get(pk=pk)
+ course_form = CourseForm(request.POST or None, instance=course)
+
+ if request.method == "POST" and request.is_ajax():
+ if course_form.is_valid():
+
+ # This will fail if name and school match one that already exists
+ # Same happens for add, should add specialized validation?
+ course_form.save()
+
+ # Make a helper for this? Kinda Ugly...
+ course_json = serializers.serialize('json', [course,])
+ return HttpResponse(json.dumps(json.loads(course_json)[0]),
+ mimetype="application/json")
+ else:
+ print course_form.errors
+ return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Validation error',
+ 'errors': course_form.errors}),
+ mimetype="application/json")
+ else:
+ return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Invalid request'}),
+ mimetype="application/json")
border-radius: 0;
}
+.hidden {
+ display:none;
+}
+
.half-padding
{
#add-note-btn,
#add-course-btn,
#existing-course-btn,
-#save-btn
+#save-btn,
+#edit-save-btn
{
border: none;
background-color: white;
}
#existing-course-btn,
-#save-btn {
+#save-btn,
+#edit-save-btn {
margin-top: 13px;
}
#existing-course-btn.disabled,
-#save-btn.disabled
+#save-btn.disabled,
+#edit-save-btn.disabled
{
color: #afafaf;
cursor: auto;
}
-#add-note-form, #add-course-form
+.validation_error {
+ color:red;
+}
+
+#add-note-form, #add-course-form, #edit-course-form
{
display: none;
}
$('#save-btn').removeClass('disabled');
$('#existing-course-msg').hide();
}
- }
+ };
addCourse = function() {
// Show the add a course form
minLength: 3
});
- $("#id_name").autocomplete({
- source: function(request, response){
- var school_id = $('#id_school').val();
- $.ajax({
- url: json_school_course_list,
- data: {q: request.term, school_id: school_id},
- success: function(data) {
- if (data['status'] === 'success') {
- response($.map(data['courses'], function(item) {
- return {
- value: item.name,
- label: item.name,
- };
- }));
- }
- },
- dataType: "json",
- type: 'POST'
- });
- },
+ KARMAWORLD.Course.initCourseNameAutocomplete({
select: function(event, ui) {
courseNameSelected = true;
fieldEdited();
courseNameSelected = false;
fieldEdited();
}
- },
- minLength: 3
+ }
});
- $("#id_instructor_name").autocomplete({
- source: function(request, response) {
- var school_id = $('#id_school').val();
- var course_name = $('#id_name').val();
- $.ajax({
- url: json_school_course_instructor_list,
- data: {q: request.term, school_id: school_id, course_name: course_name},
- success: function(data) {
- if (data['status'] === 'success') {
- // Fill in the autocomplete entries
- response($.map(data['instructors'], function(item) {
- return {
- value: item.name,
- label: item.name,
- url: item.url
- };
- }));
- }
- },
- dataType: "json",
- type: 'POST'
- });
- },
+ KARMAWORLD.Course.initInstructorNameAutocomplete({
select: function(event, ui) {
instructorSelected = true;
$('#existing-course-btn').attr('href', ui.item.url);
$('#existing-course-btn').attr('href', '');
fieldEdited();
}
- },
- minLength: 3
+ }
});
-
});
-
$(function() {
- $("#flag-button").click(function(event) {
+ $('#flag-button').click(function(event) {
event.preventDefault();
if (confirm('Do you wish to flag this course for deletion?')) {
// this note
$.ajax({
url: course_flag_url,
- dataType: "json",
+ dataType: 'json',
type: 'POST'
});
}
});
+
+ $('#edit-button').click(function(event) {
+ $('#edit-course-form').slideToggle();
+ });
+
+ $('#edit-save-btn').click(function(event) {
+ $.ajax({
+ url: course_edit_url,
+ dataType: 'json',
+ data: $('#edit-course-form').children().serialize(),
+ type: 'POST',
+ success: function(data) {
+ // We might want to use a template here instead of rehashing logic
+ // on both the client and server side
+ $('#edit-course-form').slideUp();
+ $('.validation_error').remove()
+ $('#course_form_errors').text('');
+ $('#course_name').text(data.fields.name);
+ $('#course_instructor_name').text(data.fields.instructor_name);
+
+ var $externalLinkSquare = $('<i>', {'class': 'fa fa-external-link-square'});
+ $('#course_url').text(data.fields.url.slice(0, 50) + ' ');
+ $('#course_url').append($externalLinkSquare);
+ $('#course_url').attr('href', data.fields.url);
+ if (data.fields.url === '') {
+ $('#course_link').parent().hide();
+ } else {
+ $('#course_link').parent().show();
+ }
+ },
+ error: function(resp) {
+ var json = JSON.parse(resp.responseText);
+ var errors = json.errors;
+
+ // Delete all errors that currently exist
+ $('.validation_error').remove()
+
+ // Failed responses with no errors -> display message
+ if (!errors) {
+ $('#course_form_errors').text(json.message);
+ } else {
+ if (errors.instructor_email) {
+ $.each(errors.instructor_email, function(index, value) {
+ $('#id_instructor_email').parent().children('legend').append($('<span>', { class: 'validation_error', text: value }));
+ });
+ }
+
+ if (errors.url) {
+ $.each(errors.url, function(index, value) {
+ $('#id_url').parent().children('legend').append($('<span>', { class: 'validation_error' , text: value }));
+ });
+ }
+ }
+ }
+ });
+ });
+
+ KARMAWORLD.Course.initCourseNameAutocomplete({});
+ KARMAWORLD.Course.initInstructorNameAutocomplete({});
+
});
--- /dev/null
+window.KARMAWORLD = window.KARMAWORLD || {};
+window.KARMAWORLD.Course = {
+ initCourseNameAutocomplete: function(autocompleteOpts) {
+ var opts = $.extend( {}, autocompleteOpts, {
+ source: function(request, response){
+ var school_id = $('#id_school').val();
+ $.ajax({
+ url: json_school_course_list,
+ data: {q: request.term, school_id: school_id},
+ success: function(data) {
+ if (data['status'] === 'success') {
+ response($.map(data['courses'], function(item) {
+ return {
+ value: item.name,
+ label: item.name,
+ };
+ }));
+ }
+ },
+ dataType: "json",
+ type: 'POST'
+ });
+ },
+ minLength: 3
+ });
+ $("#id_name").autocomplete(opts);
+ },
+
+ initInstructorNameAutocomplete: function(autocompleteOpts) {
+ var opts = $.extend( {}, autocompleteOpts, {
+ source: function(request, response) {
+ var school_id = $('#id_school').val();
+ var course_name = $('#id_name').val();
+ $.ajax({
+ url: json_school_course_instructor_list,
+ data: {q: request.term, school_id: school_id, course_name: course_name},
+ success: function(data) {
+ if (data['status'] === 'success') {
+ // Fill in the autocomplete entries
+ response($.map(data['instructors'], function(item) {
+ return {
+ value: item.name,
+ label: item.name,
+ url: item.url
+ };
+ }));
+ }
+ },
+ dataType: "json",
+ type: 'POST'
+ });
+ },
+ minLength: 3
+ });
+
+ $("#id_instructor_name").autocomplete(opts);
+ }
+};
{% block pagescripts %}
<script src="{{ STATIC_URL }}js/bootstrap-modal.js" ></script>
<script src="{{ STATIC_URL }}js/setup-ajax.js"></script>
+ <script src="{{ STATIC_URL }}js/course.js"></script>
<script src="{{ STATIC_URL }}js/course-detail.js" ></script>
<script>
+ var json_school_course_list = "{% url 'json_school_course_list' %}";
+ var json_school_course_instructor_list = "{% url 'json_school_course_instructor_list' %}";
var csrf_token = "{{ csrf_token }}";
- var course_flag_url = "{% url 'flag_course' course.id %}"
+ var course_flag_url = "{% url 'flag_course' course.id %}";
+ var course_edit_url = "{% url 'edit_course' course.id %}";
</script>
{% endblock %}
-
{% block pagestyle %}
<link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/note_course_pages.css">
{% endblock %}
<div id="course_header" class="hero_gradient_bar">
<div class="row">
- <div class="small-12 columns header_title">
+ <div id="course_name" class="small-12 columns header_title">
{{ course.name }}
- </div><!-- /note_name -->
+ </div><!-- /course_name -->
</div>
<div class="row">
<div id="course_meta" class="twelve columns">
<div class="activity_details_context">
- {{ course.instructor_name }} {% if course.department %}// {{ course.department.name }}{% endif %}
+ <span id="course_instructor_name">{{ course.instructor_name }}</span>
+ <span id="course_department">{% if course.department %}// {{ course.department.name }}{% endif %}</span>
</div><!-- /activity_details_context -->
</div><!-- /course_meta -->
</div>
</div><!-- /course_meta -->
</div>
- {% if course.url %}
- <div class="row">
- <div id="course_link" class="twelve columns">
- <div class="activity_details_context">
- <a rel="nofollow" target="_blank" href="{{ course.url }}">
- {{ course.url|slice:":50" }}
- <i class="fa fa-external-link-square"></i>
- </a>
- </div><!-- /activity_details_context -->
- </div><!-- /course_meta -->
- </div>
- {% endif %}
+ <div class="row{% if not course.url %} hidden{% endif %}">
+ <div id="course_link" class="twelve columns">
+ <div class="activity_details_context">
+ <a id="course_url" rel="nofollow" target="_blank" href="{{ course.url }}">
+ {{ course.url|slice:":50" }}
+ <i class="fa fa-external-link-square"></i>
+ </a>
+ </div><!-- /activity_details_context -->
+ </div><!-- /course_meta -->
+ </div>
- <div class="row">
+ <div class="row">
<div id="course_actions" class="large-3 medium-6 small-12 columns small-centered">
<div class="row">
<div class="small-12 column center">
+ <a href="#" id="edit-button"><img src="{{ STATIC_URL }}img/note_thank.png" alt="edit_flag" width="25" height="35"/></a>
<a href="#" id="flag-button" {% if already_flagged %} class="hide" {% endif %}><img src="{{ STATIC_URL }}img/note_flag.png" alt="note_flag" width="25" height="35"/></a>
<a href="#" id="flag-button-disabled" {% if not already_flagged %} class="hide" {% endif %}><img src="{{ STATIC_URL }}img/note_flag_disabled.png" alt="note_flag" width="25" height="35"/></a>
</div>
</div>
- </div><!-- /note_actions -->
+ </div><!-- /course_actions -->
</div>
</div><!-- /course_header -->
+ <section id="edit-course-form">
+ <form>
+ {% csrf_token %}
+
+ <div class="row">
+ <div id="course_form_errors" class="small-12 columns">
+ {{ course_form.non_field_errors }}
+ </div>
+ </div>
+ <div class="row hidden">
+ <div class="small-12 columns">
+ <legend>School</legend>
+ <div>
+ <input id="id_school" value="{{ course.school.id }}" name="school" type='hidden'/>
+ </div>
+ </div>
+ </div> <!-- .row -->
+
+ <div class="row">
+ <div class="small-12 columns">
+ <legend>Course Name:</legend>
+ <input id="id_name" class="" type="text" name="name" maxlength="255" value="{{ course.name }}"/>
+ </div>
+ </div> <!-- .row -->
+
+ <div class="row">
+ <div class="small-12 columns large-6">
+ <legend>Instructor Name:</legend>
+ <input id="id_instructor_name" class="" type="text" name="instructor_name" maxlength="75" value="{{ course.instructor_name }}"/>
+ </div>
+
+ <div class="small-12 columns large-6">
+ <legend>Instructor Email:</legend>
+ <input id="id_instructor_email" class="" type="text" name="instructor_email" maxlength="75" value="{{ course.instructor_email }}"/>
+ </div>
+ </div> <!-- .row -->
+
+ <div class="row">
+ <div class="small-12 columns">
+ <legend>Course url:</legend>
+ <input id="id_url" class="" type="text" name="url" maxlength="255" value="{{ course.url }}"/>
+ </div>
+ </div> <!-- .row -->
+
+ <div class="row">
+ <div class="small-4 large-8 columns small-centered text-center">
+ <button id="edit-save-btn" type=button><i class="fa fa-save"></i> Save</button>
+ </div>
+ </div>
+ </form>
+ </section>
<div class="row">
<div class="small-10 columns small-offset-1"> <hr/> </div>
</div>
</div>
{% endcomment %}
- </section><!--/note_content-->
+ </section><!--/course_content-->
+
{% endblock %}
var json_school_course_instructor_list = "{% url 'json_school_course_instructor_list' %}"
var csrf_token = "{{ csrf_token }}";
</script>
- <script src="{{ STATIC_URL }}js/setup-ajax.js"></script>
+<script src="{{ STATIC_URL }}js/setup-ajax.js"></script>
+<script src="{{ STATIC_URL }}js/course.js"></script>
<script src="{{ STATIC_URL }}js/add-course.js"></script>
<section id="add-course-form">
<div class="row">
<div class="small-12 columns">
- <legend>Course Name:
+ <legend>Course Name:
{% if course_form.name.errors %}
<span style="color:red">
* there was an error with this field
from django.views.generic.base import TemplateView
from karmaworld.apps.courses.models import Course
-from karmaworld.apps.courses.views import AboutView, flag_course
+from karmaworld.apps.courses.views import AboutView, flag_course, edit_course
from karmaworld.apps.courses.views import CourseDetailView
from karmaworld.apps.courses.views import CourseListView
from karmaworld.apps.courses.views import school_list
url(r'^ajax/note/downloaded/(?P<pk>[\d]+)/$', downloaded_note, name='downloaded_note'),
# Ajax endpoint to flag a course
url(r'^ajax/course/flag/(?P<pk>[\d]+)/$', flag_course, name='flag_course'),
+ # Ajax endpoint to edit a course
+ url(r'^ajax/course/edit/(?P<pk>[\d]+)/$', edit_course, name='edit_course'),
# Valid url cases to the Note page
# a: school/course/id
request.session[format_session_increment_field(cls, pk, field)] = True
return ajax_base(cls, request, pk, ajax_increment_work)
-