From: Charles Connell Date: Mon, 23 Dec 2013 17:32:40 +0000 (-0500) Subject: More work on course and note page headers #223 X-Git-Tag: release-20150131~366^2~6 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=3157c52ef79928c2c4f42350b9966993edb5320f;p=oweals%2Fkarmaworld.git More work on course and note page headers #223 --- diff --git a/karmaworld/apps/document_upload/models.py b/karmaworld/apps/document_upload/models.py index d44bdd2..54b44ce 100644 --- a/karmaworld/apps/document_upload/models.py +++ b/karmaworld/apps/document_upload/models.py @@ -33,7 +33,8 @@ class RawDocument(Document): ip=self.ip, uploaded_at=self.uploaded_at, fp_file=self.fp_file, - user=self.user) + user=self.user, + mimetype=self.mimetype) note.save() for tag in self.tags.all(): note.tags.add(tag) diff --git a/karmaworld/apps/notes/migrations/0006_auto__add_field_note_thanks.py b/karmaworld/apps/notes/migrations/0006_auto__add_field_note_thanks.py new file mode 100644 index 0000000..74459ff --- /dev/null +++ b/karmaworld/apps/notes/migrations/0006_auto__add_field_note_thanks.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'Note.thanks' + db.add_column('notes_note', 'thanks', + self.gf('django.db.models.fields.PositiveIntegerField')(default=0), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Note.thanks' + db.delete_column('notes_note', 'thanks') + + + models = { + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'courses.course': { + 'Meta': {'ordering': "['-file_count', 'school', 'name']", 'unique_together': "(('school', 'name', 'instructor_name'),)", 'object_name': 'Course'}, + 'academic_year': ('django.db.models.fields.IntegerField', [], {'default': '2013', 'null': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'desc': ('django.db.models.fields.TextField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}), + 'file_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'instructor_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'instructor_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'school': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.School']"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}), + 'updated_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}) + }, + 'courses.school': { + 'Meta': {'ordering': "['-file_count', '-priority', 'name']", 'object_name': 'School'}, + 'alias': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'facebook_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'file_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'priority': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '150', 'null': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '511', 'blank': 'True'}), + 'usde_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'notes.note': { + 'Meta': {'ordering': "['-uploaded_at']", 'object_name': 'Note'}, + 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Course']"}), + 'desc': ('django.db.models.fields.TextField', [], {'max_length': '511', 'null': 'True', 'blank': 'True'}), + 'download_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), + 'embed_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}), + 'file_type': ('django.db.models.fields.CharField', [], {'default': "'???'", 'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'fp_file': ('django_filepicker.models.FPFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'is_flagged': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_moderated': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'mimetype': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'note_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'pdf_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '255', 'null': 'True'}), + 'text': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'thanks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'tweeted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.utcnow', 'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['users.KarmaUser']", 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'year': ('django.db.models.fields.IntegerField', [], {'default': '2013', 'null': 'True', 'blank': 'True'}) + }, + 'taggit.tag': { + 'Meta': {'ordering': "['namespace', 'name']", 'object_name': 'Tag'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), + 'namespace': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'}) + }, + 'taggit.taggeditem': { + 'Meta': {'object_name': 'TaggedItem'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) + }, + 'users.karmauser': { + 'Meta': {'object_name': 'KarmaUser'}, + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + } + } + + complete_apps = ['notes'] \ No newline at end of file diff --git a/karmaworld/apps/notes/models.py b/karmaworld/apps/notes/models.py index c889fea..ab5f46d 100644 --- a/karmaworld/apps/notes/models.py +++ b/karmaworld/apps/notes/models.py @@ -169,6 +169,8 @@ class Note(Document): is_flagged = models.BooleanField(default=False) is_moderated = models.BooleanField(default=False) + thanks = models.PositiveIntegerField(default=0) + def __unicode__(self): return u"Note: {0} {1} -- {2}".format(self.file_type, self.name, self.uploaded_at) diff --git a/karmaworld/apps/notes/tasks.py b/karmaworld/apps/notes/tasks.py new file mode 100644 index 0000000..e69de29 diff --git a/karmaworld/apps/notes/views.py b/karmaworld/apps/notes/views.py index f1384ce..83c83a7 100644 --- a/karmaworld/apps/notes/views.py +++ b/karmaworld/apps/notes/views.py @@ -1,12 +1,14 @@ #!/usr/bin/env python # -*- coding:utf8 -*- # Copyright (C) 2012 FinalsClub Foundation +import json +from django.core.exceptions import ObjectDoesNotExist import os from django.conf import settings from django.contrib.sites.models import Site -from django.http import HttpResponse +from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseNotFound from django.views.generic import DetailView from django.views.generic import FormView from django.views.generic import View @@ -18,6 +20,12 @@ from karmaworld.apps.notes.models import Note from karmaworld.apps.notes.forms import NoteForm +ZOOM_MIMETYPES = ( + 'application/pdf', + 'image/jpeg', + 'image/png', + 'image/tiff' +) def is_pdf(self): if self.object.file_type == 'pdf': @@ -45,6 +53,10 @@ class NoteDetailView(DetailView): kwargs['pdf'] = is_pdf(self) kwargs['ppt'] = is_ppt(self) + if self.object.mimetype and \ + self.object.mimetype in ZOOM_MIMETYPES: + kwargs['zoom'] = True + return super(NoteDetailView, self).get_context_data(**kwargs) @@ -134,3 +146,21 @@ class PDFView(DetailView): # os.path.basename(self.object.note_file.name)) return super(PDFView, self).get_context_data(**kwargs) + +def thank_note(request, pk): + """Record that somebody has thanked a note.""" + if not (request.method == 'POST' and request.is_ajax()): + # return that the api call failed + return HttpResponseBadRequest(json.dumps({'status': 'fail', 'message': 'must be a POST ajax request'}), + mimetype="application/json") + + try: + note = Note.objects.get(pk=pk) + note.thanks += 1 + note.save() + except ObjectDoesNotExist: + return HttpResponseNotFound(json.dumps({'status': 'fail', 'message': 'note id does not match a note'}), + mimetype="application/json") + + return HttpResponse(status=204) + diff --git a/karmaworld/assets/css/course.css b/karmaworld/assets/css/course.css deleted file mode 100644 index 0f5e550..0000000 --- a/karmaworld/assets/css/course.css +++ /dev/null @@ -1,25 +0,0 @@ -#course_content -{ - padding-top: 46px; -} - -#course_header -{ - padding-bottom: 20px; - margin-bottom: 20px; -} - -#course_subhead -{ - font-family: "MuseoSans-900"; - font-size: 10px; - text-align: center; - text-transform: uppercase; - padding-top: 47px; - padding-bottom: 1px; -} - -#course_name -{ - -} \ No newline at end of file diff --git a/karmaworld/assets/css/note_course_pages.css b/karmaworld/assets/css/note_course_pages.css index f313813..694c2f9 100644 --- a/karmaworld/assets/css/note_course_pages.css +++ b/karmaworld/assets/css/note_course_pages.css @@ -2,7 +2,7 @@ #note_header { - margin-bottom: 20px; + padding-bottom: 20px; } #note_content, #course_content, #school_content @@ -72,10 +72,20 @@ padding-bottom: 45px; } -#social-buttons { +#note-buttons { padding-bottom: 10px; } +#zoom-buttons { + padding-bottom: 20px; + padding-top: 20px +} + +.zoom-button { + cursor: pointer; + margin-right: 10px; +} + /* COURSES */ #course_meta, #school_meta @@ -112,3 +122,9 @@ { padding-top: 45px; } + +#course_header +{ + padding-bottom: 20px; + margin-bottom: 20px; +} diff --git a/karmaworld/assets/img/note_thank_disabled.png b/karmaworld/assets/img/note_thank_disabled.png new file mode 100644 index 0000000..a4f0ddc Binary files /dev/null and b/karmaworld/assets/img/note_thank_disabled.png differ diff --git a/karmaworld/assets/js/add-course.js b/karmaworld/assets/js/add-course.js index bec4290..8dd6197 100644 --- a/karmaworld/assets/js/add-course.js +++ b/karmaworld/assets/js/add-course.js @@ -43,21 +43,6 @@ $(function() { $(".modal_content").hide(); }); - function setupAjax(){ - // Assumes variable csrf_token is made available - // by embedding document - $.ajaxSetup({ - beforeSend: function(xhr, settings) { - if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { - // Only send the token to relative URLs i.e. locally. - xhr.setRequestHeader("X-CSRFToken", csrf_token); - } - } - }); - } - - setupAjax(); - $("#str_school").autocomplete({ source: function(request, response){ $.ajax({ diff --git a/karmaworld/assets/js/note-detail.js b/karmaworld/assets/js/note-detail.js new file mode 100644 index 0000000..1aabd4d --- /dev/null +++ b/karmaworld/assets/js/note-detail.js @@ -0,0 +1,66 @@ +// resize the iframe based on internal contents on page load +function autoResize(id){ + var newheight; + var newwidth; + + if(document.getElementById){ + newheight = document.getElementById(id).contentWindow.document .body.scrollHeight; + newwidth = document.getElementById(id).contentWindow.document .body.scrollWidth; + } + + document.getElementById(id).height = (newheight+ 10) + "px"; + document.getElementById(id).width= (newwidth + 5) + "px"; + + + var currFFZoom = 1; + var currIEZoom = 100; + var frameBody = $('#noteframe').contents().find('body'); + + $('#plus-btn').on('click',function(){ + if ($.browser.mozilla){ + var step = 0.25; + currFFZoom += step; + frameBody.css('MozTransform','scale(' + currFFZoom + ')'); + } else { + var step = 25; + currIEZoom += step; + frameBody.css('zoom', ' ' + currIEZoom + '%'); + } + }); + + $('#minus-btn').on('click',function(){ + if ($.browser.mozilla){ + var step = 0.25; + currFFZoom -= step; + frameBody.css('MozTransform','scale(' + currFFZoom + ')'); + } else { + var step = 25; + currIEZoom -= step; + frameBody.css('zoom', ' ' + currIEZoom + '%'); + } + }); +} + +$(function() { + $("#thank-button").click(function() { + event.preventDefault(); + + // increment number in page right away + var thankNumber = $("#thank-number"); + thankNumber.text(parseInt(thankNumber.text()) + 1); + + // disable thank button so it can't + // be pressed again + $(this).hide(); + $('#thank-button-disabled').show(); + $(this).unbind('click'); + + // tell server that somebody thanked + // this note + $.ajax({ + url: note_thank_url, + dataType: "json", + type: 'POST' + }); + }); +}); diff --git a/karmaworld/assets/js/note-iframe.js b/karmaworld/assets/js/note-iframe.js deleted file mode 100644 index 48ec0c7..0000000 --- a/karmaworld/assets/js/note-iframe.js +++ /dev/null @@ -1,42 +0,0 @@ -// resize the iframe based on internal contents on page load -function autoResize(id){ - var newheight; - var newwidth; - - if(document.getElementById){ - newheight = document.getElementById(id).contentWindow.document .body.scrollHeight; - newwidth = document.getElementById(id).contentWindow.document .body.scrollWidth; - } - - document.getElementById(id).height = (newheight+ 10) + "px"; - document.getElementById(id).width= (newwidth + 5) + "px"; - - - var currFFZoom = 1; - var currIEZoom = 100; - var frameBody = $('#noteframe').contents().find('body'); - - $('#plus-btn').on('click',function(){ - if ($.browser.mozilla){ - var step = 0.25; - currFFZoom += step; - frameBody.css('MozTransform','scale(' + currFFZoom + ')'); - } else { - var step = 25; - currIEZoom += step; - frameBody.css('zoom', ' ' + currIEZoom + '%'); - } - }); - - $('#minus-btn').on('click',function(){ - if ($.browser.mozilla){ - var step = 0.25; - currFFZoom -= step; - frameBody.css('MozTransform','scale(' + currFFZoom + ')'); - } else { - var step = 25; - currIEZoom -= step; - frameBody.css('zoom', ' ' + currIEZoom + '%'); - } - }); -} diff --git a/karmaworld/assets/js/setup-ajax.js b/karmaworld/assets/js/setup-ajax.js new file mode 100644 index 0000000..1c7fa33 --- /dev/null +++ b/karmaworld/assets/js/setup-ajax.js @@ -0,0 +1,17 @@ +function setupAjax(){ + // Assumes variable csrf_token is made available + // by embedding document + $.ajaxSetup({ + beforeSend: function(xhr, settings) { + if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) { + // Only send the token to relative URLs i.e. locally. + xhr.setRequestHeader("X-CSRFToken", csrf_token); + console.log("preparing request to " + settings.url); + } + } + }); +} + +$(function() { + setupAjax(); +}); \ No newline at end of file diff --git a/karmaworld/templates/courses/course_detail.html b/karmaworld/templates/courses/course_detail.html index 674d320..8c12017 100644 --- a/karmaworld/templates/courses/course_detail.html +++ b/karmaworld/templates/courses/course_detail.html @@ -12,7 +12,6 @@ {% block pagestyle %} - {% endblock %} @@ -25,12 +24,6 @@
-
-
- course -
-
-
{% if course.url %} diff --git a/karmaworld/templates/notes/note_detail.html b/karmaworld/templates/notes/note_detail.html index bf00a27..569eeb0 100644 --- a/karmaworld/templates/notes/note_detail.html +++ b/karmaworld/templates/notes/note_detail.html @@ -10,7 +10,12 @@ {% endblock %} {% block pagescripts %} - + + + {% endblock %} {% block content %} @@ -28,45 +33,50 @@
{{ note.name }} -
-
-
+
+
+
+ So far, {{ note.thanks }} people have completely fallen in love with with these notes. +
+
+
+ +
+
-
- + +
+ note_thank + note_thank
-
-
-
-
-
-
+
-
-
- - +
+ + {% if zoom %} +
+
+ +
+ {% endif %} + -
{% if pdf or ppt %} @@ -112,24 +122,3 @@ {% endblock %} - -{% block bodyscripts %} - -
- - - - -{% endblock %} \ No newline at end of file diff --git a/karmaworld/templates/partial/add_course.html b/karmaworld/templates/partial/add_course.html index 07e37a7..4958efd 100644 --- a/karmaworld/templates/partial/add_course.html +++ b/karmaworld/templates/partial/add_course.html @@ -5,6 +5,7 @@ var json_school_course_instructor_list = "{% url 'json_school_course_instructor_list' %}" var csrf_token = "{{ csrf_token }}"; +
diff --git a/karmaworld/urls.py b/karmaworld/urls.py index 93562ac..c247bcc 100644 --- a/karmaworld/urls.py +++ b/karmaworld/urls.py @@ -15,7 +15,7 @@ from karmaworld.apps.courses.views import CourseListView from karmaworld.apps.courses.views import school_list 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 +from karmaworld.apps.notes.views import NoteView, thank_note from karmaworld.apps.notes.views import RawNoteDetailView from karmaworld.apps.notes.views import PDFView from karmaworld.apps.document_upload.views import save_fp_upload @@ -71,6 +71,9 @@ urlpatterns = patterns('', CourseDetailView.as_view(), name='course_detail'), ## NOTE MODEL + # Ajax endpoint to thank a note + url(r'^ajax/note/thank/(?P[\d]+)/$', thank_note, name='thank_note'), + # Valid url cases to the Note page # a: school/course/id # b: school/course/id/slug