Allow login in the cms, and read a particular course from mongo
This commit is contained in:
61
cms/djangoapps/instructor/models.py
Normal file
61
cms/djangoapps/instructor/models.py
Normal file
@@ -0,0 +1,61 @@
|
||||
"""
|
||||
WE'RE USING MIGRATIONS!
|
||||
|
||||
If you make changes to this model, be sure to create an appropriate migration
|
||||
file and check it in at the same time as your model changes. To do that,
|
||||
|
||||
1. Go to the mitx dir
|
||||
2. ./manage.py schemamigration user --auto description_of_your_change
|
||||
3. Add the migration file created in mitx/courseware/migrations/
|
||||
"""
|
||||
import uuid
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class UserProfile(models.Model):
|
||||
class Meta:
|
||||
db_table = "auth_userprofile"
|
||||
|
||||
## CRITICAL TODO/SECURITY
|
||||
# Sanitize all fields.
|
||||
# This is not visible to other users, but could introduce holes later
|
||||
user = models.OneToOneField(User, unique=True, db_index=True, related_name='profile')
|
||||
name = models.CharField(blank=True, max_length=255, db_index=True)
|
||||
org = models.CharField(blank=True, max_length=255, db_index=True)
|
||||
|
||||
|
||||
class Registration(models.Model):
|
||||
''' Allows us to wait for e-mail before user is registered. A
|
||||
registration profile is created when the user creates an
|
||||
account, but that account is inactive. Once the user clicks
|
||||
on the activation key, it becomes active. '''
|
||||
class Meta:
|
||||
db_table = "auth_registration"
|
||||
|
||||
user = models.ForeignKey(User, unique=True)
|
||||
activation_key = models.CharField(('activation key'), max_length=32, unique=True, db_index=True)
|
||||
|
||||
def register(self, user):
|
||||
# MINOR TODO: Switch to crypto-secure key
|
||||
self.activation_key = uuid.uuid4().hex
|
||||
self.user = user
|
||||
self.save()
|
||||
|
||||
def activate(self):
|
||||
self.user.is_active = True
|
||||
self.user.save()
|
||||
#self.delete()
|
||||
|
||||
|
||||
class PendingNameChange(models.Model):
|
||||
user = models.OneToOneField(User, unique=True, db_index=True)
|
||||
new_name = models.CharField(blank=True, max_length=255)
|
||||
rationale = models.CharField(blank=True, max_length=1024)
|
||||
|
||||
|
||||
class PendingEmailChange(models.Model):
|
||||
user = models.OneToOneField(User, unique=True, db_index=True)
|
||||
new_email = models.CharField(blank=True, max_length=255, db_index=True)
|
||||
activation_key = models.CharField(('activation key'), max_length=32, unique=True, db_index=True)
|
||||
16
cms/djangoapps/instructor/tests.py
Normal file
16
cms/djangoapps/instructor/tests.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
||||
49
cms/djangoapps/instructor/views.py
Normal file
49
cms/djangoapps/instructor/views.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import logging
|
||||
|
||||
from django.views.decorators.http import require_http_methods, require_POST, require_GET
|
||||
from django.contrib.auth import logout, authenticate, login
|
||||
from django.shortcuts import redirect
|
||||
from mitxmako.shortcuts import render_to_response
|
||||
|
||||
from django_future.csrf import ensure_csrf_cookie
|
||||
|
||||
log = logging.getLogger("mitx.student")
|
||||
|
||||
|
||||
@require_http_methods(['GET', 'POST'])
|
||||
def do_login(request):
|
||||
if request.method == 'POST':
|
||||
return post_login(request)
|
||||
elif request.method == 'GET':
|
||||
return get_login(request)
|
||||
|
||||
|
||||
@require_POST
|
||||
@ensure_csrf_cookie
|
||||
def post_login(request):
|
||||
username = request.POST['username']
|
||||
password = request.POST['password']
|
||||
user = authenticate(username=username, password=password)
|
||||
if user is not None:
|
||||
if user.is_active:
|
||||
login(request, user)
|
||||
return redirect(request.POST.get('next', '/'))
|
||||
else:
|
||||
raise Exception("Can't log in, account disabled")
|
||||
else:
|
||||
raise Exception("Can't log in, invalid authentication")
|
||||
|
||||
|
||||
@require_GET
|
||||
@ensure_csrf_cookie
|
||||
def get_login(request):
|
||||
return render_to_response('login.html', {
|
||||
'next': request.GET.get('next')
|
||||
})
|
||||
|
||||
|
||||
@ensure_csrf_cookie
|
||||
def logout_user(request):
|
||||
''' HTTP request to log in the user. Redirects to marketing page'''
|
||||
logout(request)
|
||||
return redirect('/')
|
||||
@@ -6,6 +6,12 @@ from .common import *
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
KEYSTORE = {
|
||||
'host': 'localhost',
|
||||
'db': 'mongo_base',
|
||||
'collection': 'key_store',
|
||||
}
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
|
||||
80
cms/lib/keystore/__init__.py
Normal file
80
cms/lib/keystore/__init__.py
Normal file
@@ -0,0 +1,80 @@
|
||||
"""
|
||||
This module provides an abstraction for working objects that conceptually have
|
||||
the following attributes:
|
||||
|
||||
location: An identifier for an item, of which there might be many revisions
|
||||
children: A list of urls for other items required to fully define this object
|
||||
data: A set of nested data needed to define this object
|
||||
editor: The editor/owner of the object
|
||||
parents: Url pointers for objects that this object was derived from
|
||||
revision: What revision of the item this is
|
||||
"""
|
||||
|
||||
|
||||
class Location(object):
|
||||
''' Encodes a location.
|
||||
Can be:
|
||||
* String (url)
|
||||
* Tuple
|
||||
* Dictionary
|
||||
'''
|
||||
def __init__(self, location):
|
||||
self.update(location)
|
||||
|
||||
def update(self, location):
|
||||
if isinstance(location, basestring):
|
||||
self.tag = location.split('/')[0][:-1]
|
||||
(self.org, self.course, self.category, self.name) = location.split('/')[2:]
|
||||
elif isinstance(location, list):
|
||||
(self.tag, self.org, self.course, self.category, self.name) = location
|
||||
elif isinstance(location, dict):
|
||||
self.tag = location['tag']
|
||||
self.org = location['org']
|
||||
self.course = location['course']
|
||||
self.category = location['category']
|
||||
self.name = location['name']
|
||||
elif isinstance(location, Location):
|
||||
self.update(location.list())
|
||||
|
||||
def url(self):
|
||||
return "i4x://{org}/{course}/{category}/{name}".format(**self.dict())
|
||||
|
||||
def list(self):
|
||||
return [self.tag, self.org, self.course, self.category, self.name]
|
||||
|
||||
def dict(self):
|
||||
return {'tag': self.tag,
|
||||
'org': self.org,
|
||||
'course': self.course,
|
||||
'category': self.category,
|
||||
'name': self.name}
|
||||
|
||||
def to_json(self):
|
||||
return self.dict()
|
||||
|
||||
|
||||
class KeyStore(object):
|
||||
def get_children_for_item(self, location):
|
||||
"""
|
||||
Returns the children for the most recent revision of the object
|
||||
with the specified location.
|
||||
|
||||
If no object is found at that location, raises keystore.exceptions.ItemNotFoundError
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class KeyStoreItem(object):
|
||||
"""
|
||||
An object from a KeyStore, which can be saved back to that keystore
|
||||
"""
|
||||
def __init__(self, location, children, data, editor, parents, revision):
|
||||
self.location = location
|
||||
self.children = children
|
||||
self.data = data
|
||||
self.editor = editor
|
||||
self.parents = parents
|
||||
self.revision = revision
|
||||
|
||||
def save(self):
|
||||
raise NotImplementedError
|
||||
12
cms/lib/keystore/django.py
Normal file
12
cms/lib/keystore/django.py
Normal file
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
Module that provides a connection to the keystore specified in the django settings.
|
||||
|
||||
Passes settings.KEYSTORE as kwargs to MongoKeyStore
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.conf import settings
|
||||
from .mongo import MongoKeyStore
|
||||
|
||||
keystore = MongoKeyStore(**settings.KEYSTORE)
|
||||
7
cms/lib/keystore/exceptions.py
Normal file
7
cms/lib/keystore/exceptions.py
Normal file
@@ -0,0 +1,7 @@
|
||||
"""
|
||||
Exceptions thrown by KeyStore objects
|
||||
"""
|
||||
|
||||
|
||||
class ItemNotFoundError(Exception):
|
||||
pass
|
||||
26
cms/lib/keystore/mongo.py
Normal file
26
cms/lib/keystore/mongo.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import pymongo
|
||||
from . import KeyStore
|
||||
from .exceptions import ItemNotFoundError
|
||||
|
||||
|
||||
class MongoKeyStore(KeyStore):
|
||||
"""
|
||||
A Mongodb backed KeyStore
|
||||
"""
|
||||
def __init__(self, host, db, collection, port=27017):
|
||||
self.collection = pymongo.connection.Connection(
|
||||
host=host,
|
||||
port=port
|
||||
)[db][collection]
|
||||
|
||||
def get_children_for_item(self, location):
|
||||
item = self.collection.find_one(
|
||||
{'location': location.dict()},
|
||||
fields={'children': True},
|
||||
sort=[('revision', pymongo.ASCENDING)],
|
||||
)
|
||||
|
||||
if item is None:
|
||||
raise ItemNotFoundError()
|
||||
|
||||
return item['children']
|
||||
11
cms/templates/login.html
Normal file
11
cms/templates/login.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<form name="login" action="login", method="post">
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="${csrf_token}"/>
|
||||
|
||||
% if next is not None:
|
||||
<input type="hidden" name="next" value="${next}"/>
|
||||
% endif
|
||||
|
||||
Username: <input type="text" name="username" />
|
||||
Possword: <input type="password" name="password" />
|
||||
<input type="submit" value="Submit" />
|
||||
</form>
|
||||
@@ -4,6 +4,7 @@ from django.conf.urls.defaults import patterns, url
|
||||
# from django.contrib import admin
|
||||
# admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('cms.views',
|
||||
url(r'^(?P<course>[^/]+)/calendar/', 'calendar', name='calendar'),
|
||||
urlpatterns = patterns('',
|
||||
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/calendar/', 'cms.views.calendar', name='calendar'),
|
||||
url(r'^accounts/login/', 'instructor.views.do_login', name='login'),
|
||||
)
|
||||
|
||||
11
cms/views.py
11
cms/views.py
@@ -1,5 +1,12 @@
|
||||
from mitxmako.shortcuts import render_to_response
|
||||
from keystore import Location
|
||||
from keystore.django import keystore
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
|
||||
def calendar(request, course):
|
||||
return render_to_response('calendar.html', {})
|
||||
@login_required
|
||||
def calendar(request, org, course):
|
||||
weeks = keystore.get_children_for_item(
|
||||
Location(['i4x', org, course, 'Course', 'course'])
|
||||
)
|
||||
return render_to_response('calendar.html', {'weeks': weeks})
|
||||
|
||||
0
common/lib/django_future/__init__.py
Normal file
0
common/lib/django_future/__init__.py
Normal file
@@ -23,3 +23,4 @@ requests
|
||||
sympy
|
||||
newrelic
|
||||
glob2
|
||||
pymongo
|
||||
|
||||
Reference in New Issue
Block a user