Commit 9350a2c0 by Steve Strassmann

refactoring views

parent 0bea50ed
from new import * # TODO: replace asterisks, should explicitly enumerate imports instead
from error import *
from course import *
from item import *
from public import *
from user import *
from preview import *
from assets import *
from checklist import *
from requests import landing
"""
from main import * from assets import asset_index, upload_asset, import_course, generate_export_course, export_course
from checklist import get_checklists, update_checklist
""" from component import *
from course import *
from error import not_found, server_error, render_404, render_500
from item import save_item, clone_item, delete_item
from preview import preview_dispatch, preview_component
from public import signup, old_login_redirect, login_page, howitworks, ux_alerts
from user import index, add_user, remove_user, manage_users
from tabs import edit_tabs, reorder_static_tabs
from requests import edge, event, landing
#from auth.authz import is_user_in_course_group_role, get_users_in_course_group_by_role
#from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_from_course_group
#from auth.authz import INSTRUCTOR_ROLE_NAME, STAFF_ROLE_NAME, create_all_course_groups
from auth.authz import STAFF_ROLE_NAME, INSTRUCTOR_ROLE_NAME from auth.authz import STAFF_ROLE_NAME, INSTRUCTOR_ROLE_NAME
from auth.authz import is_user_in_course_group_role from auth.authz import is_user_in_course_group_role
from contentstore.utils import get_course_location_for_item from contentstore.utils import get_course_location_for_item
from django.core.exceptions import PermissionDenied
def get_location_and_verify_access(request, org, course, name): def get_location_and_verify_access(request, org, course, name):
""" """
......
import logging, json, os, tarfile, shutil
from tempfile import mkdtemp
from path import path
from django.conf import settings
from django.http import HttpResponse, HttpResponseBadRequest from django.http import HttpResponse, HttpResponseBadRequest
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django_future.csrf import ensure_csrf_cookie from django_future.csrf import ensure_csrf_cookie
from django.core.urlresolvers import reverse
from django.core.servers.basehttp import FileWrapper
from django.core.files.temp import NamedTemporaryFile
from mitxmako.shortcuts import render_to_response
from cache_toolbox.core import del_cached_content
from contentstore.utils import get_url_reverse
from xmodule.modulestore.xml_importer import import_from_xml
from xmodule.contentstore.django import contentstore
from xmodule.modulestore.xml_exporter import export_to_xml
from xmodule.modulestore.django import modulestore
from xmodule.modulestore import Location
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
from access import get_location_and_verify_access
from xmodule.util.date_utils import get_default_time_display from xmodule.util.date_utils import get_default_time_display
from mitxmako.shortcuts import render_to_response
from access import get_location_and_verify_access
from auth.authz import create_all_course_groups
@login_required @login_required
@ensure_csrf_cookie @ensure_csrf_cookie
...@@ -116,3 +133,122 @@ def upload_asset(request, org, course, coursename): ...@@ -116,3 +133,122 @@ def upload_asset(request, org, course, coursename):
response['asset_url'] = StaticContent.get_url_path_from_location(content.location) response['asset_url'] = StaticContent.get_url_path_from_location(content.location)
return response return response
@ensure_csrf_cookie
@login_required
def import_course(request, org, course, name):
location = get_location_and_verify_access(request, org, course, name)
if request.method == 'POST':
filename = request.FILES['course-data'].name
if not filename.endswith('.tar.gz'):
return HttpResponse(json.dumps({'ErrMsg': 'We only support uploading a .tar.gz file.'}))
data_root = path(settings.GITHUB_REPO_ROOT)
course_subdir = "{0}-{1}-{2}".format(org, course, name)
course_dir = data_root / course_subdir
if not course_dir.isdir():
os.mkdir(course_dir)
temp_filepath = course_dir / filename
logging.debug('importing course to {0}'.format(temp_filepath))
# stream out the uploaded files in chunks to disk
temp_file = open(temp_filepath, 'wb+')
for chunk in request.FILES['course-data'].chunks():
temp_file.write(chunk)
temp_file.close()
tf = tarfile.open(temp_filepath)
tf.extractall(course_dir + '/')
# find the 'course.xml' file
for r, d, f in os.walk(course_dir):
for files in f:
if files == 'course.xml':
break
if files == 'course.xml':
break
if files != 'course.xml':
return HttpResponse(json.dumps({'ErrMsg': 'Could not find the course.xml file in the package.'}))
logging.debug('found course.xml at {0}'.format(r))
if r != course_dir:
for fname in os.listdir(r):
shutil.move(r / fname, course_dir)
module_store, course_items = import_from_xml(modulestore('direct'), settings.GITHUB_REPO_ROOT,
[course_subdir], load_error_modules=False,
static_content_store=contentstore(),
target_location_namespace=Location(location),
draft_store=modulestore())
# we can blow this away when we're done importing.
shutil.rmtree(course_dir)
logging.debug('new course at {0}'.format(course_items[0].location))
create_all_course_groups(request.user, course_items[0].location)
return HttpResponse(json.dumps({'Status': 'OK'}))
else:
course_module = modulestore().get_item(location)
return render_to_response('import.html', {
'context_course': course_module,
'active_tab': 'import',
'successful_import_redirect_url': get_url_reverse('CourseOutline', course_module)
})
@ensure_csrf_cookie
@login_required
def generate_export_course(request, org, course, name):
location = get_location_and_verify_access(request, org, course, name)
loc = Location(location)
export_file = NamedTemporaryFile(prefix=name + '.', suffix=".tar.gz")
root_dir = path(mkdtemp())
# export out to a tempdir
logging.debug('root = {0}'.format(root_dir))
export_to_xml(modulestore('direct'), contentstore(), loc, root_dir, name, modulestore())
#filename = root_dir / name + '.tar.gz'
logging.debug('tar file being generated at {0}'.format(export_file.name))
tf = tarfile.open(name=export_file.name, mode='w:gz')
tf.add(root_dir / name, arcname=name)
tf.close()
# remove temp dir
shutil.rmtree(root_dir / name)
wrapper = FileWrapper(export_file)
response = HttpResponse(wrapper, content_type='application/x-tgz')
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(export_file.name)
response['Content-Length'] = os.path.getsize(export_file.name)
return response
@ensure_csrf_cookie
@login_required
def export_course(request, org, course, name):
location = get_location_and_verify_access(request, org, course, name)
course_module = modulestore().get_item(location)
return render_to_response('export.html', {
'context_course': course_module,
'active_tab': 'export',
'successful_import_redirect_url': ''
})
import json
from django.http import HttpResponse, HttpResponseBadRequest from django.http import HttpResponse, HttpResponseBadRequest
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django_future.csrf import ensure_csrf_cookie from django_future.csrf import ensure_csrf_cookie
from access import get_location_and_verify_access
from mitxmako.shortcuts import render_to_response from mitxmako.shortcuts import render_to_response
from xmodule.modulestore import Location
from xmodule.modulestore.inheritance import own_metadata
from contentstore.utils import get_modulestore, get_url_reverse from contentstore.utils import get_modulestore, get_url_reverse
from requests import get_request_method
from access import get_location_and_verify_access
@ensure_csrf_cookie @ensure_csrf_cookie
@login_required @login_required
......
from django.http import HttpResponse, HttpResponseServerError, HttpResponseNotFound from django.http import HttpResponseServerError, HttpResponseNotFound
from mitxmako.shortcuts import render_to_string, render_to_response from mitxmako.shortcuts import render_to_string, render_to_response
......
import json
from uuid import uuid4
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse from django.http import HttpResponse
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.inheritance import own_metadata
from util.json_request import expect_json from util.json_request import expect_json
from mitxmako.shortcuts import render_to_response from contentstore.utils import get_modulestore
from access import has_access
from requests import _xmodule_recurse
# cdodge: these are categories which should not be parented, they are detached from the hierarchy
DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info']
@login_required @login_required
......
import logging, sys
import static_replace
from xmodule_modifiers import replace_static_urls
from xmodule.error_module import ErrorDescriptor
from xmodule.errortracker import exc_info_to_str
from django.core.urlresolvers import reverse
from mitxmako.shortcuts import render_to_response
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from xblock.runtime import DbModel from xblock.runtime import DbModel
from xmodule.x_module import ModuleSystem from xmodule.x_module import ModuleSystem
...@@ -6,6 +13,14 @@ from xmodule_modifiers import wrap_xmodule ...@@ -6,6 +13,14 @@ from xmodule_modifiers import wrap_xmodule
from session_kv_store import SessionKeyValueStore from session_kv_store import SessionKeyValueStore
from requests import render_from_lms from requests import render_from_lms
from functools import partial from functools import partial
from xmodule.modulestore import Location
from access import has_access
from xmodule.modulestore.django import modulestore
from xmodule.exceptions import NotFoundError, ProcessingError
from django.http import HttpResponse, Http404, HttpResponseBadRequest, HttpResponseForbidden
log = logging.getLogger(__name__)
@login_required @login_required
def preview_dispatch(request, preview_id, location, dispatch=None): def preview_dispatch(request, preview_id, location, dispatch=None):
......
from external_auth.views import ssl_login_shortcut from external_auth.views import ssl_login_shortcut
from mitxmako.shortcuts import render_to_response from mitxmako.shortcuts import render_to_response
from django_future.csrf import ensure_csrf_cookie from django_future.csrf import ensure_csrf_cookie
from requests import index from django.core.context_processors import csrf
from django.shortcuts import redirect
from django.conf import settings
from user import index
""" """
Public views Public views
......
from django.contrib.auth.decorators import login_required import json
from django_future.csrf import ensure_csrf_cookie
from mitxmako.shortcuts import render_to_response
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from access import has_access
from contentstore.utils import get_url_reverse, get_lms_link_for_item
from django.conf import settings
@login_required
@ensure_csrf_cookie
def index(request):
"""
List all courses available to the logged in user
"""
courses = modulestore('direct').get_items(['i4x', None, None, 'course', None])
# filter out courses that we don't have access too
def course_filter(course):
return (has_access(request.user, course.location)
and course.location.course != 'templates'
and course.location.org != ''
and course.location.course != ''
and course.location.name != '')
courses = filter(course_filter, courses)
return render_to_response('index.html', {
'new_course_template': Location('i4x', 'edx', 'templates', 'course', 'Empty'),
'courses': [(course.display_name,
get_url_reverse('CourseOutline', course),
get_lms_link_for_item(course.location, course_id=course.location.course_id))
for course in courses],
'user': request.user,
'disable_course_creation': settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False) and not request.user.is_staff
})
# ==== Views with per-item permissions================================
from django.http import HttpResponse
from mitxmako.shortcuts import render_to_string, render_to_response
# points to the temporary course landing page with log in and sign up # points to the temporary course landing page with log in and sign up
def landing(request, org, course, coursename): def landing(request, org, course, coursename):
......
from xblock.runtime import KeyValueStore from xblock.runtime import KeyValueStore, InvalidScopeError
class SessionKeyValueStore(KeyValueStore): class SessionKeyValueStore(KeyValueStore):
def __init__(self, request, model_data): def __init__(self, request, model_data):
......
from access import has_access
from util.json_request import expect_json
from django.http import HttpResponse, HttpResponseBadRequest
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django_future.csrf import ensure_csrf_cookie
from mitxmako.shortcuts import render_to_response
from xmodule.modulestore import Location
from xmodule.modulestore.inheritance import own_metadata
from xmodule.modulestore.django import modulestore
from contentstore.utils import get_course_for_item
def initialize_course_tabs(course):
# set up the default tabs
# I've added this because when we add static tabs, the LMS either expects a None for the tabs list or
# at least a list populated with the minimal times
# @TODO: I don't like the fact that the presentation tier is away of these data related constraints, let's find a better
# place for this. Also rather than using a simple list of dictionaries a nice class model would be helpful here
# This logic is repeated in xmodule/modulestore/tests/factories.py
# so if you change anything here, you need to also change it there.
course.tabs = [{"type": "courseware"},
{"type": "course_info", "name": "Course Info"},
{"type": "discussion", "name": "Discussion"},
{"type": "wiki", "name": "Wiki"},
{"type": "progress", "name": "Progress"}]
modulestore('direct').update_metadata(course.location.url(), own_metadata(course))
@login_required
@expect_json
def reorder_static_tabs(request):
tabs = request.POST['tabs']
course = get_course_for_item(tabs[0])
if not has_access(request.user, course.location):
raise PermissionDenied()
# get list of existing static tabs in course
# make sure they are the same lengths (i.e. the number of passed in tabs equals the number
# that we know about) otherwise we can drop some!
existing_static_tabs = [t for t in course.tabs if t['type'] == 'static_tab']
if len(existing_static_tabs) != len(tabs):
return HttpResponseBadRequest()
# load all reference tabs, return BadRequest if we can't find any of them
tab_items = []
for tab in tabs:
item = modulestore('direct').get_item(Location(tab))
if item is None:
return HttpResponseBadRequest()
tab_items.append(item)
# now just go through the existing course_tabs and re-order the static tabs
reordered_tabs = []
static_tab_idx = 0
for tab in course.tabs:
if tab['type'] == 'static_tab':
reordered_tabs.append({'type': 'static_tab',
'name': tab_items[static_tab_idx].display_name,
'url_slug': tab_items[static_tab_idx].location.name})
static_tab_idx += 1
else:
reordered_tabs.append(tab)
# OK, re-assemble the static tabs in the new order
course.tabs = reordered_tabs
modulestore('direct').update_metadata(course.location, own_metadata(course))
return HttpResponse()
@login_required
@ensure_csrf_cookie
def edit_tabs(request, org, course, coursename):
location = ['i4x', org, course, 'course', coursename]
course_item = modulestore().get_item(location)
# check that logged in user has permissions to this item
if not has_access(request.user, location):
raise PermissionDenied()
# see tabs have been uninitialized (e.g. supporing courses created before tab support in studio)
if course_item.tabs is None or len(course_item.tabs) == 0:
initialize_course_tabs(course_item)
# first get all static tabs from the tabs list
# we do this because this is also the order in which items are displayed in the LMS
static_tabs_refs = [t for t in course_item.tabs if t['type'] == 'static_tab']
static_tabs = []
for static_tab_ref in static_tabs_refs:
static_tab_loc = Location(location)._replace(category='static_tab', name=static_tab_ref['url_slug'])
static_tabs.append(modulestore('direct').get_item(static_tab_loc))
components = [
static_tab.location.url()
for static_tab
in static_tabs
]
return render_to_response('edit-tabs.html', {
'active_tab': 'pages',
'context_course': course_item,
'components': components
})
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django_future.csrf import ensure_csrf_cookie from django_future.csrf import ensure_csrf_cookie
from util.json_request import expect_json
from mitxmako.shortcuts import render_to_response from mitxmako.shortcuts import render_to_response
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from contentstore.utils import get_url_reverse, get_lms_link_for_item
from access import has_access
from requests import create_json_response
from util.json_request import expect_json
from auth.authz import STAFF_ROLE_NAME, INSTRUCTOR_ROLE_NAME, get_users_in_course_group_by_role
from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_from_course_group
def user_author_string(user): def user_author_string(user):
'''Get an author string for commits by this user. Format: '''Get an author string for commits by this user. Format:
first last <email@email.com>. first last <email@email.com>.
...@@ -22,6 +36,33 @@ def user_author_string(user): ...@@ -22,6 +36,33 @@ def user_author_string(user):
@login_required @login_required
@ensure_csrf_cookie @ensure_csrf_cookie
def index(request):
"""
List all courses available to the logged in user
"""
courses = modulestore('direct').get_items(['i4x', None, None, 'course', None])
# filter out courses that we don't have access too
def course_filter(course):
return (has_access(request.user, course.location)
and course.location.course != 'templates'
and course.location.org != ''
and course.location.course != ''
and course.location.name != '')
courses = filter(course_filter, courses)
return render_to_response('index.html', {
'new_course_template': Location('i4x', 'edx', 'templates', 'course', 'Empty'),
'courses': [(course.display_name,
get_url_reverse('CourseOutline', course),
get_lms_link_for_item(course.location, course_id=course.location.course_id))
for course in courses],
'user': request.user,
'disable_course_creation': settings.MITX_FEATURES.get('DISABLE_COURSE_CREATION', False) and not request.user.is_staff
})
@login_required
@ensure_csrf_cookie
def manage_users(request, location): def manage_users(request, location):
''' '''
This view will return all CMS users who are editors for the specified course This view will return all CMS users who are editors for the specified course
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment