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
|
DEBUG = True
|
||||||
TEMPLATE_DEBUG = DEBUG
|
TEMPLATE_DEBUG = DEBUG
|
||||||
|
|
||||||
|
KEYSTORE = {
|
||||||
|
'host': 'localhost',
|
||||||
|
'db': 'mongo_base',
|
||||||
|
'collection': 'key_store',
|
||||||
|
}
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'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
|
# from django.contrib import admin
|
||||||
# admin.autodiscover()
|
# admin.autodiscover()
|
||||||
|
|
||||||
urlpatterns = patterns('cms.views',
|
urlpatterns = patterns('',
|
||||||
url(r'^(?P<course>[^/]+)/calendar/', 'calendar', name='calendar'),
|
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 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):
|
@login_required
|
||||||
return render_to_response('calendar.html', {})
|
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
|
sympy
|
||||||
newrelic
|
newrelic
|
||||||
glob2
|
glob2
|
||||||
|
pymongo
|
||||||
|
|||||||
Reference in New Issue
Block a user