Edit and delete notes
authorCharles Connell <charles@connells.org>
Mon, 28 Apr 2014 21:14:34 +0000 (17:14 -0400)
committerCharles Connell <charles@connells.org>
Mon, 28 Apr 2014 21:14:34 +0000 (17:14 -0400)
karmaworld/apps/notes/forms.py
karmaworld/apps/notes/views.py
karmaworld/assets/css/global.css
karmaworld/assets/js/note-detail.js
karmaworld/templates/notes/note_detail.html
karmaworld/urls.py

index 0759ffb285f4f8fefb205020802cc84e5b6de96c..df59404ace1616639faca9f382ee788f738491d9 100644 (file)
@@ -1,23 +1,26 @@
 #!/usr/bin/env python
 # -*- coding:utf8 -*-
 # Copyright (C) 2012  FinalsClub Foundation
-
-from django.forms import ModelForm
+from django.forms import ModelForm, IntegerField, HiddenInput, Form
 from django.forms import TextInput
-
-from django_filepicker.forms import FPFileField
 from django_filepicker.widgets import FPFileWidget
 
 from karmaworld.apps.notes.models import Note
 
+
 class NoteForm(ModelForm):
     class Meta:
         model = Note
-        fields = ('name', 'tags', 'year',)
+        fields = ('name', 'tags',)
         widgets = {
           'name': TextInput()
         }
 
+
+class NoteDeleteForm(Form):
+    note = IntegerField(widget=HiddenInput())
+
+
 class FileUploadForm(ModelForm):
     auto_id = False
     class Meta:
index 2fd1bc2cee8a70ef1d32a0c89de08cc022757f98..d1db236ebf1649efb0544ba891c6dba5e3cca2e6 100644 (file)
@@ -4,8 +4,10 @@
 
 import traceback
 import logging
+from django.contrib import messages
 
 from django.core import serializers
+from django.core.exceptions import ValidationError
 from django.forms.formsets import formset_factory
 from karmaworld.apps.courses.models import Course
 from karmaworld.apps.notes.search import SearchIndex
@@ -21,7 +23,7 @@ from django.views.generic import View
 from django.views.generic.detail import SingleObjectMixin
 
 from karmaworld.apps.notes.models import Note
-from karmaworld.apps.notes.forms import NoteForm
+from karmaworld.apps.notes.forms import NoteForm, NoteDeleteForm
 
 
 logger = logging.getLogger(__name__)
@@ -33,6 +35,16 @@ USER_PROFILE_FLAGS_FIELD = 'flagged_notes'
 
 
 def note_page_context_helper(note, request, context):
+
+    if request.method == 'POST':
+        context['note_edit_form'] = NoteForm(request.POST)
+    else:
+        tags_string = ','.join([str(tag) for tag in note.tags.all()])
+        context['note_edit_form'] = NoteForm(initial={'name': note.name,
+                                                      'tags': tags_string})
+
+    context['note_delete_form'] = NoteDeleteForm(initial={'note': note.id})
+
     if note.is_pdf():
         context['pdf_controls'] = True
 
@@ -76,32 +88,29 @@ class NoteSaveView(FormView, SingleObjectMixin):
     model = Note
     template_name = 'notes/note_detail.html'
 
+    def post(self, request, *args, **kwargs):
+        self.object = self.get_object()
+        if self.object.user != request.user:
+            raise ValidationError("Only the owner of a note can edit it.")
+        return super(NoteSaveView, self).post(request, *args, **kwargs)
+
+    def get_success_url(self):
+        return self.object.get_absolute_url()
+
     def get_context_data(self, **kwargs):
         context = {
             'object': self.get_object(),
         }
         return super(NoteSaveView, self).get_context_data(**context)
 
-    def get_success_url(self):
-        """ On form submission success, redirect to what url """
-        #TODO: redirect to note slug if possible (auto-slugify)
-        return u'/{school_slug}/{course_slug}?url=/{school_slug}/{course_slug}/{pk}&name={name}&thankyou'.format(
-                school_slug=self.object.course.school.slug,
-                course_slug=self.object.course.slug,
-                pk=self.object.pk,
-                name=self.object.name
-            )
-
     def form_valid(self, form):
         """ Actions to take if the submitted form is valid
             namely, saving the new data to the existing note object
         """
-        self.object = self.get_object()
         if len(form.cleaned_data['name'].strip()) > 0:
             self.object.name = form.cleaned_data['name']
-        self.object.year = form.cleaned_data['year']
         # use *arg expansion to pass tags a list of tags
-        self.object.tags.add(*form.cleaned_data['tags'])
+        self.object.tags.set(*form.cleaned_data['tags'])
         # User has submitted this form, so set the SHOW flag
         self.object.is_hidden = False
         self.object.save()
@@ -127,6 +136,22 @@ class NoteView(View):
         return view(request, *args, **kwargs)
 
 
+class NoteDeleteView(FormView):
+    form_class = NoteDeleteForm
+
+    def form_valid(self, form):
+        self.note = Note.objects.get(id=form.cleaned_data['note'])
+        self.note.is_hidden = True
+        self.note.save()
+
+        messages.success(self.request, 'The note "{0}" was deleted successfully.'.format(self.note.name))
+
+        return super(FormView, self).form_valid(form)
+
+    def get_success_url(self):
+        return self.note.course.get_absolute_url()
+
+
 class NoteKeywordsView(FormView, SingleObjectMixin):
     """ Class-based view for the note html page """
     model = Note
@@ -256,6 +281,34 @@ class NoteSearchView(ListView):
         return super(NoteSearchView, self).get_context_data(**kwargs)
 
 
+def handle_edit_note(request):
+    course = Course.objects.get(pk=pk)
+    original_name = course.name
+    course_form = CourseForm(request.POST or None, instance=course)
+
+    if course_form.is_valid():
+        course_form.save()
+
+        course_json = serializers.serialize('json', [course,])
+        resp = json.loads(course_json)[0]
+
+        if (course.name != original_name):
+            course.set_slug()
+            resp['fields']['new_url'] = course.get_absolute_url()
+
+        return HttpResponse(json.dumps(resp), mimetype="application/json")
+    else:
+        return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'Validation error',
+                                      'errors': course_form.errors}),
+                                      mimetype="application/json")
+
+def edit_note(request, pk):
+    """
+    Saves the edited note metadata
+    """
+    ajax_base(request, edit_note, ['POST'])
+
+
 def process_note_thank_events(request_user, note):
     # Give points to the person who uploaded this note
     if note.user != request_user and note.user:
index 601e76ad01b8083402ad6929ce908559eabea9af..c0cffadf26a21af91e52c03506850a17b0a00df8 100644 (file)
@@ -113,6 +113,10 @@ button.inline-button {
   display: inline;
 }
 
+button.scary {
+  background-color: #F12A2A;
+}
+
 .joyride-content-wrapper .button {
   background-color: #008CBA;
 }
index e5d316a474cc1715c459c464ad49b5d55c9926da..81e3d7995ffade62e16f284b0dbf05782bc27a91 100644 (file)
@@ -208,6 +208,12 @@ $(function() {
     };
   });
 
+  $('#delete-note-button').click(function (event) {
+    if (!confirm("Are you sure you want to delete this note?")) {
+      event.preventDefault();
+    }
+  });
+
   // Embed the converted markdown if it is on the page, else default to the iframe
   if ($('#note-markdown').length > 0) {
     var note_markdown = $('#note-markdown');
index e2e11b7d64b8918c1cc4421894a6e16a3c8091d8..caec759ffa7ccb13e5a73c27d47354a738bdd65f 100644 (file)
@@ -26,7 +26,7 @@
     var annotator_js_url = "{{ STATIC_URL }}js/annotator-full.min.js";
     var annotator_css_url = "{{ STATIC_URL }}css/annotator.min.css";
     var setup_ajax_url = "{{ STATIC_URL }}js/setup-ajax.js";
-    var empty_js = "{{ STATIC_URL }}js/empty.js";
+    var note_edit_url = "{% url 'edit_note' note.id %}";
   </script>
   {% compress js %}
     <script src="{{ STATIC_URL }}js/setup-ajax.js"></script>
                   <i class="fa fa-download"></i> Download Note</button>
               {% endif %}
 
-              {% if user.get_profile.can_edit_items %}
+              {% if user.get_profile.can_edit_items and note.user != user %}
                 <button id="edit-note-tags" class="modify-button" data-reveal-id="note-tag-dialog">
                   <i class="fa fa-pencil-square-o"></i> Edit Tags
                 </button>
               {% endif %}
+
+              {% if note.user == request.user %}
+                <button id="edit-button" data-reveal-id="note-edit-dialog" class="modify-button"> <i class="fa fa-edit"></i> Edit This Note</button>&nbsp;&nbsp;
+              {% endif %}
+
+              {% if note.license %}
+                {{ note.license.html|safe }} {% if note.upstream_link %}<a href="{{ note.upstream_link }}" target="_blank">{{ note.upstream_link|slice:":80" }}</a>{% endif %}
+              {% endif %}
             </span>
           </span>
         </div>
           <strong>Tags: </strong>
           <span class="tags">
             {% for tag in note.tags.all %}
-              <span class="tag-span">{{ tag.name }}</span>
+              <span class="tag-span">
+                {{ tag.name }}{% if not forloop.last %}, {% endif %}
+              </span>
             {% endfor %}
           </span>
         </div><!-- /note_tags -->
         <a class="close-reveal-modal">&#215;</a>
         <div class="row">
           <div class="small-12 columns">
-            <p>Edit this note's tags:
-            <input id="note_tags_input" type="text" value="{% for tag in note.tags.all %}{{ tag.name }}{% if not forloop.last %}, {% endif %}{% endfor %}"></p>
+            <h3>Edit this note's tags</h3>
+            <input id="note_tags_input" type="text" value="{% for tag in note.tags.all %}{{ tag.name }}{% if not forloop.last %}, {% endif %}{% endfor %}">
             <button id="save_note_tags" type="submit" value="tags-form"><i class="fa fa-save"></i> Save</button>
           </div>
         </div>
       </div>
 
-      {% if note.license %}
+      <div id="note-edit-dialog" class="reveal-modal" data-reveal>
+        <a class="close-reveal-modal">&#215;</a>
         <div class="row">
-          <div id="note_pedigree" class="twelve columns activity_details_status">
-            {{ note.license.html|safe }} {% if note.upstream_link %}<a href="{{ note.upstream_link }}" target="_blank">{{ note.upstream_link|slice:":80" }}</a>{% endif %}
-          </div><!-- /note_pedigree -->
+          <div class="small-8 columns">
+            <h3>Edit Your Note</h3>
+          </div>
+          <div class="small-4 columns text-right">
+            <form method="POST" action="{% url 'note_delete' %}">
+              {% csrf_token %}
+              {{ note_delete_form }}
+              <button id="delete-note-button" type="submit" class="scary"><i class="fa fa-trash-o"></i> Delete Note</button>
+            </form>
+          </div>
         </div>
-      {% endif %}
+        <div class="row">
+          <form method="POST" action="{{ note.get_absolute_url }}">
+            {% csrf_token %}
+            <div class="small-12 large-6 columns">
+              {% with note_edit_form.name as field %}
+                {{ field.errors|safe }}
+                <label for="{{ field.id_for_label }}">{{ field.label }}:</label>
+                {{ field }}
+                <p>{{ field.help_text }}</p>
+              {% endwith %}
+            </div>
+            <div class="small-12 large-6 columns">
+              {% with note_edit_form.tags as field %}
+                {{ field.errors|safe }}
+                <label for="{{ field.id_for_label }}">{{ field.label }}:</label>
+                {{ field }}
+                <p>{{ field.help_text }}</p>
+              {% endwith %}
+            </div>
+            <div class="small-12 columns text-center">
+              <button type="submit"><i class="fa fa-save"></i> Save</button>
+            </div>
+          </form>
+        </div>
+      </div>
 
     </div><!-- /note header -->
 
index b1d0f632327ed3744a05acb8ea8c5a9b77bf7dcf..eccdde57f29c5cd0775cd6114cff6751b8266c8b 100644 (file)
@@ -15,7 +15,7 @@ from karmaworld.apps.courses.views import CourseListView
 from karmaworld.apps.courses.views import school_course_list
 from karmaworld.apps.courses.views import school_course_instructor_list
 from karmaworld.apps.notes.views import NoteView, thank_note, NoteSearchView, flag_note, downloaded_note, edit_note_tags, \
-    NoteKeywordsView
+    NoteKeywordsView, edit_note, NoteDeleteView
 from karmaworld.apps.moderation import moderator
 from karmaworld.apps.document_upload.views import save_fp_upload
 from karmaworld.apps.quizzes.views import QuizView, KeywordEditView, quiz_answer, get_keywords_annotator, \
@@ -98,6 +98,7 @@ urlpatterns = patterns('',
     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'),
+    url(r'^ajax/note/edit/(?P<pk>[\d]+)/$', edit_note, name='edit_note'),
 
     # Check if a quiz answer is correct
     url(r'^ajax/quiz/check/$', quiz_answer, name='quiz_answer'),
@@ -119,6 +120,8 @@ urlpatterns = patterns('',
     url(r'^note/' + SLUG.format('school_') + '/' + SLUG.format('course_') +'/'+ SLUG.format('') +'/keywords/$',
         NoteKeywordsView.as_view(), name='note_keywords'),
 
+    url(r'note/delete/$', NoteDeleteView.as_view(), name='note_delete'),
+
     # Quizzes
     url(r'^quiz/(?P<pk>[\d]+)/$',
         QuizView.as_view(), name='quiz'),