note.tags.add(tag)
return note
- def save(self, *args, **kwargs):
+ def save(self, user=None, session_key=None, *args, **kwargs):
super(RawDocument, self).save(*args, **kwargs)
if not self.is_processed:
- tasks.process_raw_document.delay(self)
-
+ tasks.process_raw_document.delay(self, user, session_key)
auto_add_check_unique_together(RawDocument)
logger = get_task_logger(__name__)
@task()
-def process_raw_document(raw_document):
+def process_raw_document(raw_document, user, session_key):
""" Process a RawDocument instance in to a Note instance """
try:
- convert_raw_document(raw_document)
+ convert_raw_document(raw_document, user=user, session_key=session_key)
except:
logger.error(traceback.format_exc())
from karmaworld.apps.courses.models import School
from karmaworld.apps.document_upload.forms import RawDocumentForm
from karmaworld.apps.notes.gdrive import *
-from karmaworld.apps.notes.models import Note
+from karmaworld.apps.notes.models import Note, find_orphan_notes
+TEST_USERNAME = 'alice'
+
+class _FakeRequest:
+ def __init__(self, session):
+ self.session = session
class ConversionTest(TestCase):
self.course.save()
self.client = Client()
- def doConversionForPost(self, post):
+ def doConversionForPost(self, post, user=None, session_key=None):
self.assertEqual(Note.objects.count(), 0)
r_d_f = RawDocumentForm(post)
self.assertTrue(r_d_f.is_valid())
raw_document = r_d_f.save(commit=False)
raw_document.fp_file = post['fp_file']
- convert_raw_document(raw_document)
+ convert_raw_document(raw_document, user=user, session_key=session_key)
self.assertEqual(Note.objects.count(), 1)
def testPlaintextConversion(self):
+ """Test upload of a plain text file"""
self.doConversionForPost({'fp_file': 'https://www.filepicker.io/api/file/S2lhT3INSFCVFURR2RV7',
'course': str(self.course.id),
'name': 'graph3.txt',
'mimetype': 'text/plain'})
def testEvernoteConversion(self):
+ """Test upload of an Evernote note"""
self.doConversionForPost({'fp_file': 'https://www.filepicker.io/api/file/vOtEo0FrSbu2WDbAOzLn',
'course': str(self.course.id),
'name': 'KarmaNotes test 3',
'mimetype': 'text/enml'})
def testPdfConversion(self):
+ """Test upload of a PDF"""
self.doConversionForPost({'fp_file': 'https://www.filepicker.io/api/file/8l6mtMURnu1uXvcvJo9s',
'course': str(self.course.id),
'name': 'geneve_1564.pdf',
'mimetype': 'application/pdf'})
def testGarbage(self):
+ """Test upload of a file with a bogus mimetype"""
with self.assertRaises(ValueError):
self.doConversionForPost({'fp_file': 'https://www.filepicker.io/api/file/H85Xl8VURqiGusxhZKMl',
'course': str(self.course.id),
'tags': '',
'mimetype': 'application/octet-stream'})
+ def testSessionUserAssociation1(self):
+ """Test setting the user of an uploaded document to a known
+ user in our database"""
+ user = User(username=TEST_USERNAME)
+ user.save()
+ self.doConversionForPost({'fp_file': 'https://www.filepicker.io/api/file/S2lhT3INSFCVFURR2RV7',
+ 'course': str(self.course.id),
+ 'name': 'graph3.txt',
+ 'tags': '',
+ 'mimetype': 'text/plain'},
+ user=user)
+ note = Note.objects.all()[0]
+ self.assertEqual(note.user, user)
+
+ def testSessionUserAssociation2(self):
+ """Test setting the user of an uploaded document
+ to an existing user in the database, finding them
+ through a session key."""
+ user = User(username=TEST_USERNAME)
+ user.save()
+ s = SessionStore()
+ s['_auth_user_id'] = user.pk
+ s.save()
+ self.doConversionForPost({'fp_file': 'https://www.filepicker.io/api/file/S2lhT3INSFCVFURR2RV7',
+ 'course': str(self.course.id),
+ 'name': 'graph3.txt',
+ 'tags': '',
+ 'mimetype': 'text/plain'},
+ session_key=s.session_key)
+ note = Note.objects.all()[0]
+ self.assertEqual(note.user, user)
+
+
+
+ def testSessionUserAssociation3(self):
+ """Test setting the user of an uploaded document
+ to an existing user in the database, finding them
+ through a session key."""
+ s = SessionStore()
+ s.save()
+ self.doConversionForPost({'fp_file': 'https://www.filepicker.io/api/file/S2lhT3INSFCVFURR2RV7',
+ 'course': str(self.course.id),
+ 'name': 'graph3.txt',
+ 'tags': '',
+ 'mimetype': 'text/plain'},
+ session_key=s.session_key)
+ user = User(username=TEST_USERNAME)
+ user.save()
+
+ # Normally this next bit is called automatically, but
+ # in testing we need to call it manually
+ note = Note.objects.all()[0]
+ s = SessionStore(session_key=s.session_key)
+ find_orphan_notes(note, user=user, request=_FakeRequest(s))
+
+ note = Note.objects.all()[0]
+ self.assertEqual(note.user, user)
+
+
+
from django.http import HttpResponse
from karmaworld.apps.document_upload.forms import RawDocumentForm
-from karmaworld.apps.users.models import KarmaUser
def save_fp_upload(request):
if r_d_f.is_valid():
raw_document = r_d_f.save(commit=False)
- time_a = datetime.datetime.now()
raw_document.fp_file = request.POST['fp_file']
- time_b = datetime.datetime.now()
- delta = time_b - time_a
raw_document.ip = request.META['REMOTE_ADDR']
raw_document.uploaded_at = datetime.datetime.utcnow()
- if request.POST['email'] != '':
- raw_document.user = KarmaUser.objects.get_or_create(email=request.POST['email'])[0]
- time_c = datetime.datetime.now()
+
# note that .save() has the side-effect of kicking of a celery processing task
- raw_document.save()
+ if request.user.is_authenticated():
+ raw_document.save(user=request.user)
+ else:
+ raw_document.save(session_key=request.session.session_key)
# save the tags to the database, too. don't forget those guys.
r_d_f.save_m2m()
- time_d = datetime.datetime.now()
- delta = time_d - time_c
- print "d\t%s" % delta
+
return HttpResponse({'success'})
else:
return HttpResponse(r_d_f.errors, status=400)
class CourseModerator(CourseAdmin):
date_heirarchy = 'updated_at'
# Identify fields to display on the Change page
- list_display = ('name', 'flags', 'school', 'academic_year', 'created_at', 'updated_at', 'instructor_name')
+ list_display = ('name', 'flags', 'school', 'created_at', 'updated_at', 'instructor_name')
# Sort by highest number of flags first, and then by date for ties.
ordering = ('-flags', '-updated_at')
# Enable resetting flags
# Copyright (C) 2012 FinalsClub Foundation
import datetime
+from django.contrib.auth.models import User
+from django.contrib.sessions.backends.db import SessionStore
+from django.core.exceptions import ObjectDoesNotExist
import os
import subprocess
import tempfile
PDF_MIMETYPE = 'application/pdf'
PPT_MIMETYPES = ['application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation']
+UPLOADED_NOTES_SESSION_KEY = 'uploaded_notes'
def extract_file_details(fileobj):
details = None
return file_dict
-def convert_raw_document(raw_document):
+def convert_raw_document(raw_document, user=None, session_key=None):
""" Upload a raw document to google drive and get a Note back"""
fp_file = raw_document.get_file()
if 'year' in note_details and note_details['year']:
note.year = note_details['year']
+ # If we know the user who uploaded this,
+ # associate them with the note
+ if user:
+ note.user = user
# Finally, save whatever data we got back from google
note.save()
+
+ if session_key and not user:
+ s = SessionStore(session_key=session_key)
+ # If the person who uploaded this made an
+ # account or signed in while convert_raw_document
+ # was running, associate their account with this note
+ try:
+ uid = s['_auth_user_id']
+ user = User.objects.get(pk=uid)
+ note.user = user
+ note.save()
+ # If we don't know the user who uploaded
+ # this, then we should have a session key
+ # instead. Associate this note with the session
+ # so if the uploader later creates an account,
+ # we can find notes they uploaded
+ except (KeyError, ObjectDoesNotExist):
+ uploaded_notes = s.get(UPLOADED_NOTES_SESSION_KEY, [])
+ uploaded_notes.append(note.id)
+ s[UPLOADED_NOTES_SESSION_KEY] = uploaded_notes
+ s.save()
+
+
--- /dev/null
+# -*- 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):
+
+ # Changing field 'Note.user'
+ db.alter_column('notes_note', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.SET_NULL))
+
+ def backwards(self, orm):
+
+ # Changing field 'Note.user'
+ db.alter_column('notes_note', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['users.KarmaUser'], null=True, on_delete=models.SET_NULL))
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ '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'},
+ 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Department']", 'null': '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'}),
+ 'flags': ('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.department': {
+ 'Meta': {'object_name': 'Department'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': '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'}),
+ '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'}),
+ 'hashtag': ('django.db.models.fields.CharField', [], {'max_length': '16', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ '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'})
+ },
+ 'licenses.license': {
+ 'Meta': {'object_name': 'License'},
+ 'html': ('django.db.models.fields.TextField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '80'})
+ },
+ 'notes.note': {
+ 'Meta': {'ordering': "['-uploaded_at']", 'object_name': 'Note'},
+ 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.Course']"}),
+ 'file_type': ('django.db.models.fields.CharField', [], {'default': "'???'", 'max_length': '15', 'null': 'True', 'blank': 'True'}),
+ 'flags': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'fp_file': ('django_filepicker.models.FPFileField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
+ 'gdrive_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', '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.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
+ 'is_hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'license': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['licenses.License']", 'null': 'True', 'blank': 'True'}),
+ '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'}),
+ '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'}),
+ 'upstream_link': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+ 'year': ('django.db.models.fields.IntegerField', [], {'default': '2014', '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']"})
+ }
+ }
+
+ complete_apps = ['notes']
\ No newline at end of file
Contains only the minimum for handling files and their representation
"""
import datetime
+import traceback
+import logging
+from allauth.account.signals import user_logged_in
+from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import SET_NULL
from django.db.models.signals import post_save, post_delete, pre_save
from django.dispatch import receiver
+from karmaworld.apps.notes.gdrive import UPLOADED_NOTES_SESSION_KEY
import os
import urllib
from karmaworld.settings.manual_unique_together import auto_add_check_unique_together
+logger = logging.getLogger(__name__)
fs = FileSystemStorage(location=settings.MEDIA_ROOT)
upstream_link = models.URLField(max_length=1024, blank=True, null=True, unique=True)
# metadata relevant to the Upload process
- user = models.ForeignKey('users.KarmaUser', blank=True, null=True, on_delete=SET_NULL)
+ user = models.ForeignKey(User, blank=True, null=True, on_delete=SET_NULL)
ip = models.GenericIPAddressField(blank=True, null=True,
help_text=u"IP address of the uploader")
uploaded_at = models.DateTimeField(null=True, default=datetime.datetime.utcnow)
return
note = kwargs['instance']
- index = SearchIndex()
- if kwargs['created']:
- update_note_counts(note)
- index.add_note(note)
- else:
- index.update_note(note, note.old_instance)
+ try:
+ index = SearchIndex()
+ if kwargs['created']:
+ update_note_counts(note)
+ index.add_note(note)
+ else:
+ index.update_note(note, note.old_instance)
+ except Exception:
+ logger.error("Error with IndexDen:\n" + traceback.format_exc())
@receiver(post_delete, sender=Note, weak=False)
# Remove document from search index
index = SearchIndex()
index.remove_note(note)
+
+@receiver(user_logged_in, weak=True)
+def find_orphan_notes(sender, **kwargs):
+ user = kwargs['user']
+ s = kwargs['request'].session
+ uploaded_note_ids = s.get(UPLOADED_NOTES_SESSION_KEY, [])
+ notes = Note.objects.filter(id__in=uploaded_note_ids)
+ for note in notes:
+ note.user = user
+ note.save()
already in the index, it will be overwritten."""
if note.text:
logger.info("Indexing {n}".format(n=note))
- self.index.add_document(note.id, SearchIndex._note_to_dict(note), variables={0: note.thanks})
+ #self.index.add_document(note.id, SearchIndex._note_to_dict(note), variables={0: note.thanks})
else:
logger.info("Note {n} has no text, will not add to IndexDen".format(n=note))
new_note.course != old_note.course or \
new_note.uploaded_at != old_note.uploaded_at:
logger.info("Indexing {n}".format(n=new_note))
- self.index.add_document(new_note.id, SearchIndex._note_to_dict(new_note), variables={0: new_note.thanks})
+ #self.index.add_document(new_note.id, SearchIndex._note_to_dict(new_note), variables={0: new_note.thanks})
# If only the thanks count has changed, we can
# just send that
# Copyright (C) 2012 FinalsClub Foundation
import json
+import traceback
+import logging
from django.core.exceptions import ObjectDoesNotExist
from karmaworld.apps.courses.models import Course
from karmaworld.apps.notes.search import SearchIndex
from karmaworld.apps.notes.models import Note
from karmaworld.apps.notes.forms import NoteForm
+logger = logging.getLogger(__name__)
PDF_MIMETYPES = (
'application/pdf',
else:
page = 0
- index = SearchIndex()
+ try:
+ index = SearchIndex()
- if 'course_id' in self.request.GET:
- raw_results = index.search(self.request.GET['query'],
- self.request.GET['course_id'],
- page=page)
+ if 'course_id' in self.request.GET:
+ raw_results = index.search(self.request.GET['query'],
+ self.request.GET['course_id'],
+ page=page)
+ else:
+ raw_results = index.search(self.request.GET['query'],
+ page=page)
+
+ except Exception:
+ logger.error("Error with IndexDen:\n" + traceback.format_exc())
+ self.error = True
+ return Note.objects.none()
else:
- raw_results = index.search(self.request.GET['query'],
- page=page)
+ self.error = False
instances = Note.objects.in_bulk(raw_results.ordered_ids)
results = []
if 'course_id' in self.request.GET:
kwargs['course'] = Course.objects.get(id=self.request.GET['course_id'])
+ if self.error:
+ kwargs['error'] = True
+ return super(NoteSearchView, self).get_context_data(**kwargs)
+
# If query returned more search results than could
# fit on one page, show "Next" button
if self.has_more:
# -*- coding:utf8 -*-
# Copyright (C) 2013 FinalsClub Foundation
from django.contrib import admin
-from karmaworld.apps.users.models import KarmaUser
+from karmaworld.apps.users.models import UserProfile
-admin.site.register(KarmaUser)
\ No newline at end of file
+admin.site.register(UserProfile)
\ No newline at end of file
--- /dev/null
+# -*- 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):
+ # Deleting model 'KarmaUser'
+ db.delete_table('users_karmauser')
+
+ # Adding model 'UserProfile'
+ db.create_table('users_userprofile', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)),
+ ('school', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['courses.School'], null=True, blank=True)),
+ ('karma', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('users', ['UserProfile'])
+
+
+ def backwards(self, orm):
+ # Adding model 'KarmaUser'
+ db.create_table('users_karmauser', (
+ ('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)),
+ ('school', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['courses.School'], null=True, blank=True)),
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('karma', self.gf('django.db.models.fields.IntegerField')(default=0)),
+ ))
+ db.send_create_signal('users', ['KarmaUser'])
+
+ # Deleting model 'UserProfile'
+ db.delete_table('users_userprofile')
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ '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.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'}),
+ 'hashtag': ('django.db.models.fields.CharField', [], {'max_length': '16', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
+ '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'})
+ },
+ 'users.userprofile': {
+ 'Meta': {'object_name': 'UserProfile'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'karma': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'school': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['courses.School']", 'null': 'True', 'blank': 'True'}),
+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+ }
+ }
+
+ complete_apps = ['users']
\ No newline at end of file
#!/usr/bin/env python
# -*- coding:utf8 -*-
# Copyright (C) 2013 FinalsClub Foundation
-from django.contrib import admin
-
+from allauth.account.signals import user_logged_in
+from django.contrib.auth.models import User
+from django.db.models.signals import post_save
+from django.dispatch import receiver
from django.db import models
+from karmaworld.apps.courses.models import School
-class KarmaUserManager(models.Manager):
- """ Handle restoring data. """
- def get_by_natural_key(self, email):
- """
- Return a KarmaUser defined by his/her email address.
- """
- return self.get(email=email)
-
+class UserProfile(models.Model):
+ user = models.OneToOneField(User)
-class KarmaUser(models.Model):
- objects = KarmaUserManager()
+ school = models.ForeignKey(School, blank=True, null=True)
- email = models.EmailField(blank=False, null=False, unique=True)
+ karma = models.IntegerField(default=0)
def __unicode__(self):
- return u'KarmaUser: {0}'.format(self.email)
+ return self.user.__unicode__()
+
+
+@receiver(post_save, sender=User, weak=True)
+def create_user_profile(sender, instance, created, **kwargs):
+ if created:
+ UserProfile.objects.create(user=instance)
- def natural_key(self):
- """
- A KarmaUser is uniquely defined by his/her email address.
- """
- return (self.email,)
--- /dev/null
+#!/usr/bin/env python
+# -*- coding:utf8 -*-
+# Copyright (C) 2013 FinalsClub Foundation
+
+from django.contrib.auth.models import User
+from django.views.generic import TemplateView
+from django.views.generic.detail import SingleObjectMixin
+
+
+class ProfileView(TemplateView, SingleObjectMixin):
+ model = User
+ context_object_name = 'user' # name passed to template
+ template_name = 'user_profile.html'
+
+ def get_object(self, queryset=None):
+ u = self.request.user
+ return self.request.user
+
--- /dev/null
+#account_content
+{
+ padding-top: 46px;
+}
+
+h1
+{
+ margin-top: 1em;
+ text-align: center;
+ font-family: "MuseoSlab-500";
+ font-size: 36px;
+}
+
+h2
+{
+ margin-top: 1em;
+ text-align: center;
+ font-family: "MuseoSlab-500";
+ font-size: 24px;
+}
+
+ul
+{
+ list-style-type: none;
+}
+
+button,
+button:hover
+{
+ border: none;
+ background-color: white;
+ color: #f05a28;
+ cursor: pointer;
+ font: 30px/1.2em "MuseoSlab-300", serif;
+ margin: 1px;
+ text-align: center;
+ text-transform: uppercase;
+}
+
cursor: pointer;
padding-bottom: 16px;
}
+
#login_container a
{
float: right;
+ padding-left: 15px;
}
#login_container.clicked
.inline-form {
border-bottom: 1px dashed #666;
}
+
+/* Social account integration */
+
+.facebook-login-btn
+{
+ content: url(../img/facebook.png);
+}
'django.core.context_processors.tz',
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.request',
+
+ # allauth specific context processors
+ "allauth.account.context_processors.account",
+ "allauth.socialaccount.context_processors.socialaccount",
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders
# Tagging https://github.com/yedpodtrzitko/django-taggit
'taggit',
+
+ 'allauth',
+ 'allauth.account',
+ 'allauth.socialaccount',
+ # ... include the providers you want to enable:
+ 'allauth.socialaccount.providers.facebook',
+ 'allauth.socialaccount.providers.google',
+ 'allauth.socialaccount.providers.openid',
+ 'allauth.socialaccount.providers.twitter',
)
LOCAL_APPS = (
########## END APP CONFIGURATION
+########## AUTHENTICATION
+
+AUTHENTICATION_BACKENDS = (
+ # Needed to login by username in Django admin, regardless of `allauth`
+ "django.contrib.auth.backends.ModelBackend",
+
+ # `allauth` specific authentication methods, such as login by e-mail
+ "allauth.account.auth_backends.AuthenticationBackend",
+)
+
+ACCOUNT_EMAIL_REQUIRED = True
+ACCOUNT_AUTHENTICATION_METHOD = "email"
+ACCOUNT_CONFIRM_EMAIL_ON_GET = True
+ACCOUNT_EMAIL_VERIFICATION = "optional"
+ACCOUNT_EMAIL_SUBJECT_PREFIX = "KarmaNotes.org"
+ACCOUNT_USER_MODEL_EMAIL_FIELD = "email"
+ACCOUNT_USERNAME_REQUIRED = False
+SOCIALACCOUNT_EMAIL_REQUIRED = True
+SOCIALACCOUNT_EMAIL_VERIFICATION = "optional"
+
+# You can't log in with a username,
+# you must use an authenitcation provider
+ACCOUNT_USER_MODEL_USERNAME_FIELD = None
+ACCOUNT_USER_MODEL_EMAIL_FIELD = None
+
+AUTH_PROFILE_MODULE = 'accounts.UserProfile'
+
+######### END AUTHENTICATION
+
########## LOGGING CONFIGURATION
# See: https://docs.djangoproject.com/en/dev/ref/settings/#logging
LOGGING = {
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load i18n %}
+
+{% block head_title %}{% trans "Account Inactive" %}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Account Inactive" %}</h1>
+
+<p>{% trans "This account is inactive." %}</p>
+{% endblock %}
--- /dev/null
+{% load url from future %}
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+<head>
+ <meta charset="utf-8" />
+ <meta name="viewport" content="width=device-width" />
+ <title>KarmaNotes -- {% block head_title %}{% endblock %}</title>
+
+ <link rel="shortcut icon" href="{{ STATIC_URL }}img/favicon.ico">
+ <link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/fontface/fontface.css">
+ <link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}vendor/foundation-4.2.3/css/foundation.css">
+ <link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/global.css">
+ <link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/media.css">
+ <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css">
+ <link rel="stylesheet" type="text/css" media="all" href="{{ STATIC_URL }}css/accounts.css">
+ <!-- include Font Awesome -->
+ <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
+
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
+ <script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
+ <script src="{{ STATIC_URL }}js/jquery-scrollto.js" ></script>
+
+ <!-- block pagescripts -->
+ {% block pagescripts %}
+ {% endblock %}
+ <!-- end block pagescripts -->
+
+ <!-- block pagestyle -->
+ {% block pagestyle %}
+ {% endblock %}
+ <!-- end block pagescripts -->
+
+ {% include 'partial/trackers.html' %}
+
+
+</head>
+<body>
+
+<!-- include header -->
+{% include 'header.html' %}
+<!-- end include header -->
+
+<!-- block content -->
+
+<section id="account_content">
+ <div class="row">
+ <div class="small-12 columns center">
+ {% block content %}
+ {% endblock %}
+ </div>
+ </div>
+</section>
+
+<!-- end block content -->
+
+<!-- include footer-->
+{% include 'footer.html' %}
+<!-- end include footer-->
+
+<!-- block bodyscripts -->
+{% block extra_body %}
+{% endblock %}
+<!-- end block bodyscripts -->
+
+</body>
+</html>
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load i18n %}
+{% load url from future %}
+
+{% block head_title %}{% trans "Account" %}{% endblock %}
+
+{% block content %}
+ <h1>{% trans "E-mail Addresses" %}</h1>
+{% if user.emailaddress_set.all %}
+<p>{% trans 'The following e-mail addresses are associated with your account:' %}</p>
+
+<form action="{% url 'account_email' %}" class="email_list" method="post">
+{% csrf_token %}
+<fieldset class="blockLabels">
+
+ {% for emailaddress in user.emailaddress_set.all %}
+<div class="ctrlHolder">
+ <label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
+
+ <input id="email_radio_{{forloop.counter}}" type="radio" name="email" {% if emailaddress.primary %}checked="checked"{%endif %} value="{{emailaddress.email}}"/>
+
+{{ emailaddress.email }}
+ {% if emailaddress.verified %}
+ <span class="verified">{% trans "Verified" %}</span>
+ {% else %}
+ <span class="unverified">{% trans "Unverified" %}</span>
+ {% endif %}
+ {% if emailaddress.primary %}<span class="primary">{% trans "Primary" %}</span>{% endif %}
+</label>
+</div>
+ {% endfor %}
+
+<div class="buttonHolder">
+ <button class="secondaryAction" type="submit" name="action_primary" >{% trans 'Make Primary' %}</button>
+ <button class="secondaryAction" type="submit" name="action_send" >{% trans 'Re-send Verification' %}</button>
+ <button class="primaryAction" type="submit" name="action_remove" >{% trans 'Remove' %}</button>
+</div>
+
+</fieldset>
+</form>
+
+{% else %}
+<p><strong>{% trans 'Warning:'%}</strong> {% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}</p>
+
+{% endif %}
+
+
+ <h2>{% trans "Add E-mail Address" %}</h2>
+
+ <form method="post" action="{% url 'account_email' %}" class="add_email">
+ {% csrf_token %}
+ {{ form.as_p}}
+ <button name="action_add" type="submit">{% trans "Add E-mail" %}</button>
+ </form>
+
+{% endblock %}
+
+
+{% block extra_body %}
+<script type="text/javascript">
+(function() {
+ var message = "{% trans 'Do you really want to remove the selected e-mail address?' %}";
+ var actions = document.getElementsByName('action_remove');
+ if (actions.length) {
+ actions[0].addEventListener("click", function(e) {
+ if (! confirm(message)) {
+ e.preventDefault();
+ }
+ });
+ }
+})();
+</script>
+{% endblock %}
--- /dev/null
+{% load account %}{% user_display user as user_display %}{% load i18n %}{% autoescape off %}{% blocktrans with current_site.name as site_name %}User {{ user_display }} at {{ site_name }} has given this as an email address.
+
+To confirm this is correct, go to {{ activate_url }}
+{% endblocktrans %}{% endautoescape %}
--- /dev/null
+{% include "account/email/email_confirmation_message.txt" %}
--- /dev/null
+{% include "account/email/email_confirmation_subject.txt" %}
--- /dev/null
+{% load i18n %}
+{% autoescape off %}
+{% blocktrans %}Confirm E-mail Address{% endblocktrans %}
+{% endautoescape %}
--- /dev/null
+{% load i18n %}{% blocktrans with site.domain as site_domain and user.username as username %}You're receiving this e-mail because you or someone else has requested a password for your user account at {{site_domain}}.
+It can be safely ignored if you did not request a password reset. Click the link below to reset your password.
+
+{{password_reset_url}}
+
+In case you forgot, your username is {{username}}.
+
+Thanks for using our site!
+{% endblocktrans %}
--- /dev/null
+{% load i18n %}
+{% autoescape off %}
+{% blocktrans %}Password Reset E-mail{% endblocktrans %}
+{% endautoescape %}
\ No newline at end of file
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+{% load account %}
+
+{% block head_title %}{% trans "Confirm E-mail Address" %}{% endblock %}
+
+
+{% block content %}
+<h1>{% trans "Confirm E-mail Address" %}</h1>
+
+{% if confirmation %}
+
+{% user_display confirmation.email_address.user as user_display %}
+
+<p>{% blocktrans with confirmation.email_address.email as email %}Please confirm that <a href="mailto:{{email}}">{{ email }}</a> is an e-mail address for user {{ user_display }}.{% endblocktrans %}</p>
+
+<form method="post" action="{% url 'account_confirm_email' confirmation.key %}">
+{% csrf_token %}
+ <button type="submit">{% trans 'Confirm' %}</button>
+</form>
+
+{% else %}
+
+{% url 'account_email' as email_url %}
+
+<p>{% blocktrans %}This e-mail confirmation link expired or is invalid. Please <a href="{{ email_url}}">issue a new e-mail confirmation request</a>.{% endblocktrans %}</p>
+
+{% endif %}
+
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load i18n %}
+{% load account %}
+
+{% block head_title %}{% trans "Confirm E-mail Address" %}{% endblock %}
+
+
+{% block content %}
+
+<h1>{% trans "Confirm E-mail Address" %}</h1>
+
+{% user_display email_address.user as user_display %}
+
+<p>{% blocktrans with email_address.email as email %}You have confirmed that <a href="mailto:{{email}}">{{ email }}</a> is an e-mail address for user {{ user_display }}.{% endblocktrans %}</p>
+
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load i18n %}
+{% load account %}
+{% load url from future %}
+
+{% block head_title %}{% trans "Sign In" %}{% endblock %}
+
+{% block content %}
+
+<h1>{% trans "Sign In" %}</h1>
+
+{% if socialaccount.providers %}
+<p>{% blocktrans with site.name as site_name %}Please sign in with one
+of your existing third party accounts. Or, <a href="{{ signup_url }}">sign up</a>
+for a {{site_name}} account and sign in below:{% endblocktrans %}</p>
+
+<div class="socialaccount_ballot">
+
+ <ul class="socialaccount_providers">
+ {% include "socialaccount/snippets/provider_list.html" with process="login" %}
+ </ul>
+
+ <div class="login-or">{% trans 'or' %}</div>
+
+</div>
+
+{% include "socialaccount/snippets/login_extra.html" %}
+
+{% else %}
+<p>{% blocktrans %}If you have not created an account yet, then please
+<a href="{{ signup_url }}">sign up</a> first.{% endblocktrans %}</p>
+{% endif %}
+
+<form class="login" method="POST" action="{% url 'account_login' %}">
+ {% csrf_token %}
+ {{ form.as_p }}
+ {% if redirect_field_value %}
+ <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
+ {% endif %}
+ <a class="button secondaryAction" href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>
+ <button class="primaryAction" type="submit">{% trans "Sign In" %}</button>
+</form>
+
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+
+{% block head_title %}{% trans "Sign Out" %}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Sign Out" %}</h1>
+
+<p>{% trans 'Are you sure you want to sign out?' %}</p>
+
+<form method="post" action="{% url 'account_logout' %}">
+ {% csrf_token %}
+ {% if redirect_field_value %}
+ <input type="hidden" name="{{redirect_field_name}}" value="{{redirect_field_value}}"/>
+ {% endif %}
+ <button type="submit">{% trans 'Sign Out' %}</button>
+</form>
+
+
+{% endblock %}
--- /dev/null
+{% load i18n %}
+{% blocktrans %}You cannot remove your primary e-mail address ({{email}}).{% endblocktrans %}
--- /dev/null
+{% load i18n %}
+{% blocktrans %}Confirmation e-mail sent to {{email}}.{% endblocktrans %}
--- /dev/null
+{% load i18n %}
+{% blocktrans %}You have confirmed {{email}}.{% endblocktrans %}
--- /dev/null
+{% load i18n %}
+{% blocktrans %}Removed e-mail address {{email}}.{% endblocktrans %}
--- /dev/null
+{% load account %}
+{% load i18n %}
+{% user_display user as name %}
+{% blocktrans %}Successfully signed in as {{name}}.{% endblocktrans %}
--- /dev/null
+{% load i18n %}
+{% blocktrans %}You have signed out.{% endblocktrans %}
--- /dev/null
+{% load i18n %}
+{% blocktrans %}Password successfully changed.{% endblocktrans %}
+
--- /dev/null
+{% load i18n %}
+{% blocktrans %}Password successfully set.{% endblocktrans %}
+
--- /dev/null
+{% load i18n %}
+{% blocktrans %}Primary e-mail address set.{% endblocktrans %}
--- /dev/null
+{% load i18n %}
+{% blocktrans %}Your primary e-mail address must be verified.{% endblocktrans %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+
+{% block head_title %}{% trans "Change Password" %}{% endblock %}
+
+{% block content %}
+ <h1>{% trans "Change Password" %}</h1>
+
+ <form method="POST" action="{% url 'account_change_password' %}" class="password_change">
+ {% csrf_token %}
+ {{ form.as_p }}
+ <button type="submit" name="action">{% trans "Change Password" %}</button>
+ </form>
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load i18n %}
+{% load account %}
+
+{% block head_title %}{% trans "Password Reset" %}{% endblock %}
+
+{% block content %}
+
+ <h1>{% trans "Password Reset" %}</h1>
+ {% if user.is_authenticated %}
+ {% include "account/snippets/already_logged_in.html" %}
+ {% endif %}
+
+ <p>{% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}</p>
+
+ <form method="POST" action="{% url 'account_reset_password' %}" class="password_reset">
+ {% csrf_token %}
+ {{ form.as_p }}
+ <input type="submit" value="{% trans "Reset My Password" %}" />
+ </form>
+
+ <p>{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}</p>
+{% endblock %}
+
+{% block extra_body %}
+ <script>
+ $("#id_email").focus();
+ </script>
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load i18n %}
+{% load account %}
+
+{% block head_title %}{% trans "Password Reset" %}{% endblock %}
+
+{% block content %}
+ <h1>{% trans "Password Reset" %}</h1>
+
+ {% if user.is_authenticated %}
+ {% include "account/snippets/already_logged_in.html" %}
+ {% endif %}
+
+ <p>{% blocktrans %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}</p>
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+{% block head_title %}{% trans "Change Password" %}{% endblock %}
+
+{% block content %}
+ <h1>{% if token_fail %}{% trans "Bad Token" %}{% else %}{% trans "Change Password" %}{% endif %}</h1>
+
+ {% if token_fail %}
+ {% url 'account_reset_password' as passwd_reset_url %}
+ <p>{% blocktrans %}The password reset link was invalid, possibly because it has already been used. Please request a <a href="{{ passwd_reset_url }}">new password reset</a>.{% endblocktrans %}</p>
+ {% else %}
+ {% if form %}
+ <form method="POST" action=".">
+ {% csrf_token %}
+ {{ form.as_p }}
+ <input type="submit" name="action" value="{% trans "change password" %}"/>
+ </form>
+ {% else %}
+ <p>{% trans 'Your password is now changed.' %}</p>
+ {% endif %}
+ {% endif %}
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+{% block head_title %}{% trans "Change Password" %}{% endblock %}
+
+{% block content %}
+ <h1>{% trans "Change Password" %}</h1>
+ <p>{% trans 'Your password is now changed.' %}</p>
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+
+{% block head_title %}{% trans "Set Password" %}{% endblock %}
+
+{% block content %}
+ <h1>{% trans "Set Password" %}</h1>
+
+ <form method="POST" action="{% url 'account_set_password' %}" class="password_set">
+ {% csrf_token %}
+ {{ form.as_p }}
+ <input type="submit" name="action" value="{% trans "Set Password" %}"/>
+ </form>
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+
+{% block head_title %}{% trans "Signup" %}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Sign Up" %}</h1>
+
+<p>{% blocktrans %}Already have an account? Then please <a href="{{ login_url }}">sign in</a>.{% endblocktrans %}</p>
+
+<form class="signup" id="signup_form" method="post" action="{% url 'account_signup' %}">
+ {% csrf_token %}
+ {{ form.as_p }}
+ {% if redirect_field_value %}
+ <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
+ {% endif %}
+ <button type="submit">{% trans "Sign Up" %} »</button>
+</form>
+
+
+{% endblock %}
+
+
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+
+{% block head_title %}{% trans "Sign Up Closed" %}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Sign Up Closed" %}</h1>
+
+<p>{% trans "We are sorry, but the sign up is currently closed." %}</p>
+{% endblock %}
+
+
--- /dev/null
+{% load i18n %}
+{% load account %}
+
+{% user_display user as user_display %}
+<p><strong>{% trans "Note" %}:</strong> {% blocktrans %}you are already logged in as {{ user_display }}.{% endblocktrans %}</p>
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load i18n %}
+
+{% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %}
+
+{% block content %}
+ <h1>{% trans "Verify Your E-mail Address" %}</h1>
+
+ <p>{% blocktrans %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}</p>
+
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+
+{% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Verify Your E-mail Address" %}</h1>
+
+{% url 'account_email' as email_url %}
+
+<p>{% blocktrans %}This part of the site requires us to verify that
+you are who you claim to be. For this purpose, we require that you
+verify ownership of your e-mail address. {% endblocktrans %}</p>
+
+<p>{% blocktrans %}We have sent an e-mail to you for
+verification. Please click on the link inside this e-mail. Please
+contact us if you do not receive it within a few minutes.{% endblocktrans %}</p>
+
+<p>{% blocktrans %}<strong>Note:</strong> you can still <a href="{{email_url}}">change your e-mail address</a>.{% endblocktrans %}</p>
+
+
+{% endblock %}
{% endblock %}
<!-- end block pagescripts -->
- <script type="text/javascript">
- if (document.location.host === 'www.karmanotes.org' ||
- document.location.host === 'karmanotes.org') {
- var _gaq = _gaq || [];
- _gaq.push(['_setAccount', 'UA-36897868-1']);
- _gaq.push(['_setDomainName', 'karmanotes.org']);
- _gaq.push(['_trackPageview']);
-
- (function() {
- var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
- ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
- var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
- })();
- }
- </script>
-
- <script type='text/javascript'>
- if (document.location.host === 'www.karmanotes.org' ||
- document.location.host === 'karmanotes.org') {
- window.__wtw_lucky_site_id = 17613;
-
- (function() {
- var wa = document.createElement('script'); wa.type = 'text/javascript'; wa.async = true;
- wa.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://ca17613') + '.luckyorange.com/w.js';
- var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(wa, s);
- })();
- }
- </script>
+ {% include 'partial/trackers.html' %}
</head>
</div>
{% endif %}
- <div id="login_container" class="small-1 columns">
- <a class=white href="{% url 'about' %}">About</a>
+ <div id="login_container" class="small-2 columns">
+ {% if request.user.is_authenticated %}
+ <a class="white" href="{% url 'account_logout' %}">Log Out</a>
+ {% else %}
+ <a class="white" href="{% url 'account_login' %}">Log In</a>
+ {% endif %}
+ <a class="white" href="{% url 'about' %}">About</a>
</div>
</div>
{% else %}
<div id="no_results" class="small-12 columns center column">
<h4>Sorry! No results were found.</h4>
+ {% if error %}
+ <h4>There was an error with your search.</h4>
+ {% endif %}
</div>
{% endif %}
</div>
{% load url from future %}
+{% load socialaccount_tags %}
<section id=filepicker-form class="extend-form">
<!-- Javascript -->
<script type="text/javascript" src="//api.filepicker.io/v1/filepicker.js"></script>
<p>Thank you for sharing:</p>
<ul id="uploaded_files">
</ul>
- <p>Your files are being processed and should be viewable within a few minutes.</p>
- <p>If you'd like to share again, please <a href="">click here</a>.</p>
+ {% if not request.user.is_authenticated %}
+ <p>We'd love to have you sign up so you can claim the notes you just uploaded, and
+ build a reputation for uploading great notes.</p>
+ <p><a href='{% provider_login_url "facebook" %}'>
+ <span class="facebook-login-btn"></span>
+ </a></p>
+ {% endif %}
</div>
</div>
</form>
</div>
</div>
- <div id="save-btn-wrapper" class="hide row">
- <div class="small-10 small-offset-1 columns">
- <div class="row" style="display: inline;">
- <form class="inline-form" method="POST" action="{% url 'upload_post' %}">
- <div class="small-11 large-6 columns">
- <legend>Your Email Address (Optional)</legend>
- <input type="text" class="intext" id="id_email" name="email"
- placeholder="">
- </div>
- </form>
- </div>
- <div class="row">
- <div class="small-8 large-6 columns">
- <div id="save-btn" class="action-btn">
- <i class="fa fa-save"></i> Save
- </div>
- </div>
- </div>
+ <div class="small-8 small-offset-3 columns large-2">
+ <div id="save-btn" class="hide">
+ <i class="fa fa-save"></i> Save
</div>
</div>
$(this).parent().parent().remove();
});
- $('#save-btn-wrapper').show();
+ $('#save-btn').show();
}
$('#save-btn').on('click', function(e){
$('#uploaded_files').append($('<li>', {text: uploaded_files[i]}));
}
$('#success').show();
- $('#save-btn-wrapper').hide();
+ $('#save-btn').hide();
$('#forms_container .inline-form').remove();
if (document.location.host === 'www.karmanotes.org' ||
document.location.host === 'karmanotes.org') {
_gat._getTracker()._trackEvent('upload', 'upload form submitted');
}
- setTimeout(function(){
- location.reload(true);
- }, 15000);
}
});
// Add the name we've just uploaded to the list
--- /dev/null
+
+<script type="text/javascript">
+ if (document.location.host === 'www.karmanotes.org' ||
+ document.location.host === 'karmanotes.org') {
+ var _gaq = _gaq || [];
+ _gaq.push(['_setAccount', 'UA-36897868-1']);
+ _gaq.push(['_setDomainName', 'karmanotes.org']);
+ _gaq.push(['_trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+ }
+</script>
+
+<script type='text/javascript'>
+ if (document.location.host === 'www.karmanotes.org' ||
+ document.location.host === 'karmanotes.org') {
+ window.__wtw_lucky_site_id = 17613;
+
+ (function() {
+ var wa = document.createElement('script'); wa.type = 'text/javascript'; wa.async = true;
+ wa.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://ca17613') + '.luckyorange.com/w.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(wa, s);
+ })();
+ }
+</script>
--- /dev/null
+{% extends "socialaccount/base.html" %}
+
+{% load i18n %}
+
+{% block head_title %}{% trans "Social Network Login Failure" %}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Social Network Login Failure" %}</h1>
+
+<p>{% trans "An error occurred while attempting to login via your social network account." %}</p>
+{% endblock %}
--- /dev/null
+{% extends "account/base.html" %}
+
--- /dev/null
+{% extends "socialaccount/base.html" %}
+
+{% load i18n %}
+{% load url from future %}
+
+{% block head_title %}{% trans "Account Connections" %}{% endblock %}
+
+{% block content %}
+<h1>{% trans "Account Connections" %}</h1>
+
+{% if form.accounts %}
+<p>{% blocktrans %}You can sign in to your account using any of the following third party accounts:{% endblocktrans %}</p>
+
+
+<form method="post" action="{% url 'socialaccount_connections' %}">
+{% csrf_token %}
+
+<fieldset>
+{% if form.non_field_errors %}
+<div id="errorMsg">{{form.non_field_errors}}</div>
+{% endif %}
+
+{% for base_account in form.accounts %}
+{% with base_account.get_provider_account as account %}
+<div>
+<label for="id_account_{{base_account.id}}">
+<input id="id_account_{{base_account.id}}" type="radio" name="account" value="{{base_account.id}}"/>
+<span class="socialaccount_provider {{base_account.provider}} {{account.get_brand.id}}">{{account.get_brand.name}}</span>
+{{account}}
+</label>
+</div>
+{% endwith %}
+{% endfor %}
+
+<div>
+<button type="submit">{% trans 'Remove' %}</button>
+</div>
+
+</fieldset>
+
+</form>
+
+{% else %}
+<p>{% trans 'You currently have no social network accounts connected to this account.' %}</p>
+{% endif %}
+
+<h2>{% trans 'Add a 3rd Party Account' %}</h2>
+
+<ul class="socialaccount_providers">
+{% include "socialaccount/snippets/provider_list.html" with process="connect" %}
+</ul>
+
+{% include "socialaccount/snippets/login_extra.html" %}
+
+{% endblock %}
--- /dev/null
+{% extends "socialaccount/base.html" %}
+
+{% load url from future %}
+{% load i18n %}
+
+{% block head_title %}{% trans "Login Cancelled" %}{% endblock %}
+
+{% block content %}
+
+<h1>{% trans "Login Cancelled" %}</h1>
+
+{% url 'account_login' as login_url %}
+
+<p>{% blocktrans %}You decided to cancel logging in to our site using one of your existing accounts. If this was a mistake, please proceed to <a href="{{login_url}}">sign in</a>.{% endblocktrans %}</p>
+
+{% endblock %}
+
--- /dev/null
+{% load i18n %}
+{% blocktrans %}The social account has been connected.{% endblocktrans %}
--- /dev/null
+{% load i18n %}
+{% blocktrans %}The social account is already connected to a different account.{% endblocktrans %}
--- /dev/null
+{% load i18n %}
+{% blocktrans %}The social account has been disconnected.{% endblocktrans %}
--- /dev/null
+{% extends "socialaccount/base.html" %}
+{% load url from future %}
+
+{% load i18n %}
+
+{% block head_title %}{% trans "Signup" %}{% endblock %}
+
+{% block content %}
+ <h1>{% trans "Sign Up" %}</h1>
+
+<p>{% blocktrans with provider_name=account.get_provider.name site_name=site.name %}You are about to use your {{provider_name}} account to login to
+{{site_name}}. As a final step, please complete the following form:{% endblocktrans %}</p>
+
+<form class="signup" id="signup_form" method="post" action="{% url 'socialaccount_signup' %}">
+ {% csrf_token %}
+ {{ form.as_p }}
+ {% if redirect_field_value %}
+ <input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
+ {% endif %}
+ <button type="submit">{% trans "Sign Up" %} »</button>
+</form>
+
+
+{% endblock %}
--- /dev/null
+{% load socialaccount %}
+
+{% providers_media_js %}
+
--- /dev/null
+{% load socialaccount %}
+
+{% for provider in socialaccount.providers %}
+{% if provider.id == "openid" %}
+{% for brand in provider.get_brands %}
+<li>
+ <a title="{{brand.name}}"
+ class="socialaccount_provider {{provider.id}} {{brand.id}}"
+ href="{% provider_login_url provider.id openid=brand.openid_url process=process %}"
+ >{{brand.name}}</a>
+</li>
+{% endfor %}
+{% endif %}
+<li>
+ <a title="{{provider.name}}" class="socialaccount_provider {{provider.id}}"
+ href="{% provider_login_url provider.id process=process %}">{{provider.name}}</a>
+</li>
+{% endfor %}
+
--- /dev/null
+{% extends "account/base.html" %}
+{% load url from future %}
+{% load account %}
+
+{% block head_title %}
+ Your KarmaNotes Profile
+{% endblock %}
+
+{% block content %}
+ <section id="account_content">
+
+ <div class="row">
+ <div class="small-12 columns">
+ <h1>Hello there, {% user_display user %}.</h1>
+ <p>Here are the notes that you've uploaded:</p>
+ <ul>
+ {% for note in user.note_set.all %}
+ <li><a href="{{ note.get_absolute_url }}">{{ note.name }}</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+ </div>
+
+
+ </section><!--/about_content-->
+{% endblock %}
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.models import Note
from karmaworld.apps.notes.views import NoteView, thank_note, NoteSearchView, flag_note
from karmaworld.apps.notes.views import RawNoteDetailView
from karmaworld.apps.notes.views import PDFView
from karmaworld.apps.moderation import moderator
from karmaworld.apps.document_upload.views import save_fp_upload
+from karmaworld.apps.users.views import ProfileView
# See: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#hooking-adminsite-instances-into-your-urlconf
+
+
admin.autodiscover()
# reused named regex capture groups
url(r'^terms/$', direct_to_template, { 'template': 'terms.html' }, name='terms'),
url(r'^about/$', AboutView.as_view(), name='about'),
+ # All Auth
+ url(r'^accounts/', include('allauth.urls')),
+ url(r'^accounts/profile/', ProfileView.as_view(), name='accounts_profile'),
+
# VIEW for viewing a Note's gdrive generated html, used as iframe
url(r'^raw/(?P<pk>\d+)$', RawNoteDetailView.as_view(), name='note_raw'),
#url(r'^pdfview$', PDFView.as_view(), name='pdf'),
gdshortener
git+https://github.com/flaptor/indextank-py.git
html2text
+django-allauth