Commit 0d419076 by Lyla Fischer

merge with cas-master

parents 9060c5ee 580164e1
......@@ -45,6 +45,8 @@ from auth.authz import get_user_by_email, add_user_to_course_group, remove_user_
from auth.authz import ADMIN_ROLE_NAME, EDITOR_ROLE_NAME
from .utils import get_course_location_for_item
from xmodule.templates import all_templates
log = logging.getLogger(__name__)
......@@ -128,7 +130,8 @@ def course_index(request, org, course, name):
return render_to_response('overview.html', {
'sections': sections,
'upload_asset_callback_url': upload_asset_callback_url
'upload_asset_callback_url': upload_asset_callback_url,
'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty')
})
......@@ -144,8 +147,10 @@ def edit_subsection(request, location):
if item.location.category != 'sequential':
return HttpResponseBadRequest
return render_to_response('edit_subsection.html',
{'subsection':item})
return render_to_response('edit_subsection.html',
{'subsection': item,
'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty')
})
@login_required
def edit_unit(request, location):
......@@ -166,7 +171,7 @@ def edit_unit(request, location):
lms_link = "{lms_base}/courses/{course_id}/jump_to/{location}".format(
lms_base=settings.LMS_BASE,
# TODO: These will need to be changed to point to the particular instance of this problem in the particular course
course_id= modulestore().get_containing_courses(item.location)[0].id,
course_id = modulestore().get_containing_courses(item.location)[0].id,
location=item.location,
)
else:
......@@ -188,10 +193,23 @@ def edit_unit(request, location):
in item.get_children()
]
# TODO (cpennington): If we share units between courses,
# this will need to change to check permissions correctly so as
# to pick the correct parent subsection
containing_subsection_locs = modulestore().get_parent_locations(location)
containing_subsection = modulestore().get_item(containing_subsection_locs[0])
containing_section_locs = modulestore().get_parent_locations(containing_subsection.location)
containing_section = modulestore().get_item(containing_section_locs[0])
return render_to_response('unit.html', {
'unit': item,
'components': components,
'component_templates': component_templates,
'lms_link': lms_link,
'subsection': containing_subsection,
'section': containing_section,
'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty')
})
......@@ -209,10 +227,6 @@ def preview_component(request, location):
})
@login_required
def delete_unit(request, location):
pass
def user_author_string(user):
'''Get an author string for commits by this user. Format:
......@@ -382,12 +396,31 @@ def get_module_previews(request, descriptor):
preview_html.append(module.get_html())
return preview_html
def _delete_item(item, recurse=False):
if recurse:
children = item.get_children()
for child in children:
_delete_item(child, recurse)
modulestore().delete_item(item.location);
@login_required
@expect_json
def delete_item(request):
item_location = request.POST['id']
modulestore().delete_item(item_location)
# check permissions for this user within this course
if not has_access(request.user, item_location):
raise PermissionDenied()
# optional parameter to delete all children (default False)
delete_children = request.POST.get('delete_children', False)
item = modulestore().get_item(item_location)
_delete_item(item, delete_children)
return HttpResponse()
......@@ -429,6 +462,8 @@ def save_item(request):
def clone_item(request):
parent_location = Location(request.POST['parent_location'])
template = Location(request.POST['template'])
display_name = request.POST.get('display_name')
if not has_access(request.user, parent_location):
raise PermissionDenied()
......@@ -440,6 +475,10 @@ def clone_item(request):
# TODO: This needs to be deleted when we have proper storage for static content
new_item.metadata['data_dir'] = parent.metadata['data_dir']
# replace the display name with an optional parameter passed in from the caller
if display_name is not None:
new_item.metadata['display_name'] = display_name
modulestore().update_metadata(new_item.location.url(), new_item.own_metadata)
modulestore().update_children(parent_location, parent.definition.get('children', []) + [new_item.location.url()])
......@@ -656,3 +695,7 @@ def asset_index(request, org, course, name):
'upload_asset_callback_url': upload_asset_callback_url
})
# points to the temporary course landing page with log in and sign up
def landing(request, org, course, coursename):
return render_to_response('temp-course-landing.html', {})
......@@ -21,11 +21,104 @@ $(document).ready(function() {
$('.unit-history ol a').bind('click', showHistoryModal);
$modal.bind('click', hideModal);
$modalCover.bind('click', hideHistoryModal);
$('.assets .upload-button').bind('click', showUploadModal);
$('.upload-modal .close-button').bind('click', hideModal);
$('.unit .item-actions .delete-button').bind('click', deleteUnit);
$('.new-unit-item').bind('click', createNewUnit);
$('.save-subsection').bind('click', saveSubsection);
// making the unit list sortable
$('.sortable-unit-list').sortable();
$('.sortable-unit-list').disableSelection();
$('.sortable-unit-list').bind('sortstop', onUnitReordered);
});
// This method only changes the ordering of the child objects in a subsection
function onUnitReordered() {
var subsection_id = $(this).data('subsection-id');
var _els = $(this).children('li:.leaf');
var children = new Array();
for(var i=0;i<_els.length;i++) {
el = _els[i];
children[i] = $(el).data('id');
}
// call into server to commit the new order
$.ajax({
url: "/save_item",
type: "POST",
dataType: "json",
contentType: "application/json",
data:JSON.stringify({ 'id' : subsection_id, 'metadata' : null, 'data': null, 'children' : children})
});
}
function saveSubsection(e) {
e.preventDefault();
var id = $(this).data('id');
// pull all metadata editable fields on page
var metadata_fields = $('input[data-metadata-name]');
metadata = {};
for(var i=0; i< metadata_fields.length;i++) {
el = metadata_fields[i];
metadata[$(el).data("metadata-name")] = el.value;
}
children =[];
$.ajax({
url: "/save_item",
type: "POST",
dataType: "json",
contentType: "application/json",
data:JSON.stringify({ 'id' : id, 'metadata' : metadata, 'data': null, 'children' : children}),
success: function() {
alert('Your changes have been saved.');
},
error: function() {
alert('There has been an error while saving your changes.');
}
});
}
function createNewUnit(e) {
e.preventDefault();
parent = $(this).data('parent');
template = $(this).data('template');
$.post('/clone_item',
{'parent_location' : parent,
'template' : template,
'display_name': 'New Unit',
},
function(data) {
// redirect to the edit page
window.location = "/edit/" + data['id'];
});
}
function deleteUnit(e) {
e.preventDefault();
if(!confirm('Are you sure you wish to delete this item. It cannot be reversed!'))
return;
var _li_el = $(this).parents('li.leaf');
var id = _li_el.data('id');
$.post('/delete_item',
{'id': id, 'delete_children' : true},
function(data) {
_li_el.remove();
});
}
function showUploadModal(e) {
e.preventDefault();
$('.upload-modal').show();
......
......@@ -194,4 +194,4 @@
margin-left: 96px;
}
}
}
\ No newline at end of file
}
body.no-header {
.primary-header {
display: none;
}
}
.primary-header {
width: 100%;
height: 36px;
......
// This is a temporary page, which will be replaced once we have a more extensive course catalog and marketing site for edX labs.
.class-landing {
.main-wrapper {
width: 700px !important;
margin: 100px auto;
}
.class-info {
padding: 30px 40px 40px;
@extend .window;
hgroup {
padding-bottom: 26px;
border-bottom: 1px solid $mediumGrey;
}
h1 {
float: none;
font-size: 30px;
font-weight: 300;
margin: 0;
}
h2 {
color: #5d6779;
}
.class-actions {
@include clearfix;
padding: 15px 0;
margin-bottom: 18px;
border-bottom: 1px solid $mediumGrey;
}
.log-in-form {
@include clearfix;
padding: 15px 0 20px;
margin-bottom: 18px;
border-bottom: 1px solid $mediumGrey;
.log-in-submit-button {
@include blue-button;
padding: 6px 20px 8px;
margin: 24px 0 0;
}
.column {
float: left;
width: 41%;
margin-right: 1%;
&.submit {
width: 16%;
margin-right: 0;
}
label {
float: left;
}
}
input {
width: 100%;
font-family: $sans-serif;
font-size: 13px;
}
.forgot-button {
float: right;
margin-bottom: 6px;
font-size: 12px;
}
}
.sign-up-button {
@include blue-button;
display: block;
width: 250px;
margin: auto;
}
.log-in-button {
@include white-button;
float: right;
}
.sign-up-button,
.log-in-button {
padding: 8px 0 12px;
font-size: 18px;
font-weight: 300;
text-align: center;
}
.class-description {
margin-top: 30px;
font-size: 14px;
}
p + p {
margin-top: 22px;
}
}
.edx-labs-logo-small {
display: block;
width: 124px;
height: 30px;
margin: auto;
background: url(../img/edx-labs-logo-small.png) no-repeat;
text-indent: -9999px;
overflow: hidden;
}
}
\ No newline at end of file
.sign-up-box,
.log-in-box {
width: 500px;
margin: 200px auto;
margin: 100px auto;
border-radius: 3px;
header {
......@@ -19,7 +20,7 @@
}
}
.log-in-form {
form {
padding: 40px;
border: 1px solid $darkGrey;
border-top-width: 0;
......@@ -34,22 +35,33 @@
font-weight: 700;
}
.email-field,
.password-field {
input[type="text"],
input[type="email"],
input[type="password"] {
width: 100%;
font-size: 20px;
font-weight: 300;
}
.row {
@include clearfix;
margin-bottom: 24px;
.split {
float: left;
width: 48%;
&:first-child {
margin-right: 4%;
}
}
}
.form-actions {
margin-bottom: 0;
}
.log-in-button {
input[type="submit"] {
@include blue-button;
margin-right: 10px;
padding: 8px 20px 10px;
......@@ -63,6 +75,5 @@
margin-top: 10px;
text-align: right;
font-size: 13px;
border-top: 1px solid $lightGrey;
}
}
\ No newline at end of file
......@@ -15,6 +15,7 @@
@import "unit";
@import "assets";
@import "course-info";
@import "landing";
@import "graphics";
@import "modal";
@import "alerts";
......
......@@ -12,11 +12,11 @@
<article class="subsection-body window">
<div class="subsection-name-input">
<label>Display Name:</label>
<input type="text" value="Welcome to 6.002x" class="subsection-display-name-input" />
<input type="text" value="${subsection.metadata['display_name']}" class="subsection-display-name-input" data-metadata-name="display_name"/>
</div>
<div>
<label>Subtitle:</label>
<input type="text" value="Lecture Sequence" class="unit-subtitle" />
<input type="text" value="${subsection.metadata['subtitle'] if 'subtitle' in subsection.metadata else ''}" class="unit-subtitle" data-metadata-name="subtitle"/>
</div>
<div class="unit-list">
<label>Units:</label>
......@@ -55,7 +55,7 @@
<a href="#" class="toggle-off">hide</a><a href="#" class="large-toggle"></a><a href="#" class="toggle-on">show</a>
</div>
<div class="row unit-actions">
<a href="#" class="save-button">Save</a>
<a href="#" class="save-button save-subsection" data-id="${subsection.location}">Save</a>
<a href="preview.html" target="_blank" class="preview-button">Preview</a>
</div>
</div>
......
<%inherit file="base.html" />
<%! from django.core.urlresolvers import reverse %>
<%block name="title">Log in</%block>
<%block name="bodyclass">no-header</%block>
<%block name="content">
......@@ -10,11 +11,11 @@
</header>
<form class="log-in-form" id="login_form" action="login_post" method="post">
<div class="row">
<label>Email:</label>
<label>Email</label>
<input name="email" type="email" class="email-field">
</div>
<div class="row">
<label>Password:</label>
<label>Password</label>
<input name="password" type="password" class="password-field">
</div>
<div class="row form-actions">
......
<%inherit file="base.html" />
<%block name="title">Sign up</%block>
<%block name="bodyclass">no-header</%block>
<%block name="content">
<section class="main-container">
<section class="main-content">
<header>
<h3>Sign Up for edX</h3>
<hr>
</header>
<div id="register">
<form id="register_form" method="post">
<div id="register_error" name="register_error"></div>
<label>E-mail</label>
<input name="email" type="email" placeholder="E-mail">
<label>Password</label>
<input name="password" type="password" placeholder="Password">
<label>Public Username</label>
<input name="username" type="text" placeholder="Public Username">
<label>Full Name</label>
<input name="name" type="text" placeholder="Full Name">
<article class="sign-up-box">
<header>
<h1>Register for edX Labs</h1>
</header>
<form id="register_form" method="post">
<div id="register_error" name="register_error"></div>
<div class="row">
<label>Email</label>
<input name="email" type="email">
</div>
<div class="row">
<label>Password</label>
<input name="password" type="password">
</div>
<div class="row">
<label>Public Username</label>
<input name="username" type="text">
</div>
<div class="row">
<label>Full Name</label>
<input name="name" type="text">
</div>
<div class="row">
<div class="split">
<label>Your Location</label>
<input name="location" type="text" placeholder="Your Location">
<input name="location" type="text">
</div>
<div class="split">
<label>Preferred Language</label>
<input name="language" type="text" placeholder="Preferred Language">
<label class="terms-of-service">
<input name="terms_of_service" type="checkbox" value="true">
I agree to the
<a href="#">Terms of Service</a>
</label>
<!-- no honor code for CMS, but need it because we're using the lms student object -->
<input name="honor_code" type="checkbox" value="true" checked="true" hidden="true">
<div class="submit">
<input name="submit" type="submit" value="Create My Account">
</div>
</form>
<section class="login-extra">
<p>
<span>Already have an account? <a href="#">Login.</a></span>
</p>
</section>
<input name="language" type="text">
</div>
</div>
<div class="row">
<label class="terms-of-service">
<input name="terms_of_service" type="checkbox" value="true">
I agree to the
<a href="#">Terms of Service</a>
</label>
</div>
<!-- no honor code for CMS, but need it because we're using the lms student object -->
<input name="honor_code" type="checkbox" value="true" checked="true" hidden="true">
<div class="row form-actions submit">
<input name="submit" type="submit" value="Create My Account">
</div>
</form>
<div class="log-in-extra">
<p>Not enrolled? <a href="/">Sign up.</a></p>
</div>
</article>
<script type="text/javascript">
(function() {
......@@ -81,8 +88,4 @@
});
})(this)
</script>
</section>
</section>
</%block>
</%block>
\ No newline at end of file
<%inherit file="base.html" />
<%! from django.core.urlresolvers import reverse %>
<%block name="title">Landing</%block>
<%block name="bodyclass">no-header class-landing</%block>
<%block name="content">
<div class="main-wrapper">
<article class="class-info">
<hgroup>
<h1>Circuits and Electronics</h1>
<h2>Massachusetts Institute of Technology</h2>
</hgroup>
<div class="log-in-form">
<form>
<div class="column">
<label>Email</label>
<input name="email" type="email" class="email-field" tabindex="1">
</div>
<div class="column">
<label>Password</label><a href="#" class="forgot-button">Forgot?</a>
<input name="password" type="password" class="password-field" tabindex="2">
</div>
<div class="column submit">
<input name="submit" type="submit" value="Log In" class="log-in-submit-button" tabindex="3">
</div>
</form>
</div>
<div class="class-description">
<p>Ut laoreet dolore magna aliquam erat volutpat ut wisi enim ad minim veniam quis nostrud. Est usus legentis in iis qui, facit eorum claritatem Investigationes demonstraverunt lectores. Vel illum dolore eu feugiat nulla facilisis at vero eros, et accumsan et iusto? Te feugait nulla facilisi nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming! Et quinta decima eodem modo typi qui nunc nobis, videntur parum clari fiant sollemnes in? Diam nonummy nibh euismod tincidunt exerci tation ullamcorper, suscipit lobortis nisl ut aliquip ex? Nunc putamus parum, claram anteposuerit litterarum formas humanitatis per seacula quarta decima.</p>
<p>Gothica quam nunc putamus parum claram anteposuerit litterarum formas humanitatis per seacula. Facilisi nam liber tempor cum soluta nobis eleifend.</p>
<p><a href="#" class="sign-up-button">Sign Up</a></p>
</div>
</article>
<footer>
<a href="#" class="edx-labs-logo-small">edX Labs</a>
</footer>
</div>
</%block>
\ No newline at end of file
<%inherit file="base.html" />
<%! from django.core.urlresolvers import reverse %>
<%namespace name="units" file="widgets/units.html" />
<%block name="bodyclass">unit</%block>
<%block name="title">CMS Unit</%block>
<%block name="jsextra">
......@@ -83,37 +84,36 @@
</div>
<div class="row unit-actions">
<a href="#" class="save-button">Save</a>
<a href="preview.html" target="_blank" class="preview-button">Preview</a>
<a href="${lms_link}" target="_blank" class="preview-button">Preview</a>
</div>
</div>
</div>
<div class="window unit-location">
<h4>Unit Location</h4>
<div class="window-contents">
<div><input type="text" class="url" value="/courseware/Week_1/My_Unit" disabled /></div>
<ol>
<li>
<a href="#" class="section-item">Week 1</a>
<div><input type="text" class="url" value="/courseware/${section.url_name}/${subsection.url_name}" disabled /></div>
<section class="courseware-section branch">
<header>
<div class="item-details">
<h3>${section.display_name}</h3>
</div>
</header>
<div class="unit-list">
<ol>
<li>
<a href="#" class="section-item"><span class="folder-icon"></span>Administrivia and Circuit Elements</a>
<ol>
<li><a href="#" class="section-item"><span class="file-icon"></span>Motiviation for 6.002</a></li>
<li><a href="#" class="section-item"><span class="file-icon"></span>Administrivia</a></li>
<li><a href="#" class="section-item"><span class="file-icon"></span>Course Overview</a></li>
<li><a href="#" class="section-item"><span class="file-icon"></span>Lumped Element Abstraction</a></li>
<li><a href="#" class="section-item"><span class="file-icon"></span>Simple Power</a></li>
<li><a href="#" class="current section-item"><span class="file-icon"></span>New Unit</a></li>
<li>
<a href="unit.html" class="new-unit-item">
<span class="new-unit-icon"></span>New Unit
<li class="branch">
<div class="section-item">
<div>
<a href="${reverse('edit_subsection', args=[subsection.location])}">
<span class="folder-icon"></span>
<span class="subsection-name"><span class="subsection-name-value">${subsection.display_name}</span></span>
</a>
</li>
</ol>
</div>
</div>
${units.enum_units(subsection, actions=False, selected=unit.location)}
</li>
</ol>
</li>
</ol>
</div>
</section>
</div>
</div>
</div>
......
......@@ -3,25 +3,38 @@
<!--
This def will enumerate through a passed in subsection and list all of the units
-->
<%def name="enum_units(subsection)">
<ol>
<%def name="enum_units(subsection, actions=True, selected=None, sortable=True)">
<ol ${'class="sortable-unit-list"' if sortable else ''} data-subsection-id="${subsection.location}">
% for unit in subsection.get_children():
<li class="leaf">
<div class="section-item">
<li class="leaf unit" data-id="${unit.location}">
<%
if unit.location == selected:
selected_class = 'editing'
else:
selected_class = ''
%>
<div class="section-item ${selected_class}">
<a href="${reverse('edit_unit', args=[unit.location])}" class="private-item">
<span class="${unit.category}-icon"></span>${unit.display_name} <span class="private-tag">- private</span>
<span class="${unit.category}-icon"></span>
${unit.display_name}
<span class="private-tag">- private</span>
</a>
% if actions:
<div class="item-actions">
<a href="${reverse('delete_unit', args=[unit.location])}" classs="edit-button wip"><span class="delete-icon"></span></a>
<a href="#" class="drag-handle wip"></a>
<a href="#" class="delete-button" data-id="${unit.location}"><span class="delete-icon"></span></a>
<a href="#" class="drag-handle"></a>
</div>
% endif
</div>
</li>
% endfor
<li>
<a href="#" class="new-unit-item wip">
<a href="#" class="new-unit-item" data-template="${create_new_unit_template}" data-parent="${subsection.location}">
<span class="new-unit-icon"></span>New Unit
</a>
</li>
</ol>
</%def>
......@@ -11,7 +11,6 @@ urlpatterns = ('',
url(r'^$', 'contentstore.views.index', name='index'),
url(r'^edit/(?P<location>.*?)$', 'contentstore.views.edit_unit', name='edit_unit'),
url(r'^subsection/(?P<location>.*?)$', 'contentstore.views.edit_subsection', name='edit_subsection'),
url(r'^delete/(?P<location>.*?)$', 'contentstore.views.delete_unit', name='delete_unit'),
url(r'^preview_component/(?P<location>.*?)$', 'contentstore.views.preview_component', name='preview_component'),
url(r'^save_item$', 'contentstore.views.save_item', name='save_item'),
url(r'^delete_item$', 'contentstore.views.delete_item', name='delete_item'),
......@@ -30,6 +29,10 @@ urlpatterns = ('',
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<name>[^/]+)/remove_user$',
'contentstore.views.remove_user', name='remove_user'),
url(r'^(?P<org>[^/]+)/(?P<course>[^/]+)/assets/(?P<name>[^/]+)$', 'contentstore.views.asset_index', name='asset_index')
# temporary landing page for a course
url(r'^landing/(?P<org>[^/]+)/(?P<course>[^/]+)/course/(?P<coursename>[^/]+)$', 'contentstore.views.landing', name='landing')
)
# User creation and updating views
......
......@@ -45,5 +45,9 @@ class VerticalModule(XModule):
class VerticalDescriptor(SequenceDescriptor):
module_class = VerticalModule
# cdodge: override the SequenceDescript's template_dir_name to point to default template directory
template_dir_name = "default"
js = {'coffee': [resource_string(__name__, 'js/src/vertical/edit.coffee')]}
js_module_name = "VerticalDescriptor"
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