Commit 2e36fb29 by Sarina Canelake

Merge pull request #4046 from edx/hotfix/2014-06-11

Hotfix 2014-06-11
parents 21a784b4 13ec3698
from course_modes.models import CourseMode
from factory.django import DjangoModelFactory
from xmodule.modulestore.locations import SlashSeparatedCourseKey
# Factories don't have __init__ methods, and are self documenting
......@@ -7,9 +8,10 @@ from factory.django import DjangoModelFactory
class CourseModeFactory(DjangoModelFactory):
FACTORY_FOR = CourseMode
course_id = u'MITx/999/Robot_Super_Course'
course_id = SlashSeparatedCourseKey('MITx', '999', 'Robot_Super_Course')
mode_slug = 'audit'
mode_display_name = 'audit course'
min_price = 0
currency = 'usd'
expiration_datetime = None
suggested_prices = ''
import ddt
import unittest
from django.test import TestCase
from django.conf import settings
from django.core.urlresolvers import reverse
from mock import patch, Mock
from course_modes.tests.factories import CourseModeFactory
from student.tests.factories import CourseEnrollmentFactory, UserFactory
from xmodule.modulestore.locations import SlashSeparatedCourseKey
@ddt.ddt
class CourseModeViewTest(TestCase):
def setUp(self):
self.course_id = SlashSeparatedCourseKey('org', 'course', 'run')
for mode in ('audit', 'verified', 'honor'):
CourseModeFactory(mode_slug=mode, course_id=self.course_id)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@ddt.data(
# is_active?, enrollment_mode, upgrade?, redirect?
(True, 'verified', True, True), # User is already verified
(True, 'verified', False, True), # User is already verified
(True, 'honor', True, False), # User isn't trying to upgrade
(True, 'honor', False, True), # User is trying to upgrade
(True, 'audit', True, False), # User isn't trying to upgrade
(True, 'audit', False, True), # User is trying to upgrade
(False, 'verified', True, False), # User isn't active
(False, 'verified', False, False), # User isn't active
(False, 'honor', True, False), # User isn't active
(False, 'honor', False, False), # User isn't active
(False, 'audit', True, False), # User isn't active
(False, 'audit', False, False), # User isn't active
)
@ddt.unpack
@patch('course_modes.views.modulestore', Mock())
def test_reregister_redirect(self, is_active, enrollment_mode, upgrade, redirect):
enrollment = CourseEnrollmentFactory(
is_active=is_active,
mode=enrollment_mode,
course_id=self.course_id
)
self.client.login(
username=enrollment.user.username,
password='test'
)
if upgrade:
get_params = {'upgrade': True}
else:
get_params = {}
response = self.client.get(
reverse('course_modes_choose', args=[self.course_id.to_deprecated_string()]),
get_params,
follow=False,
)
if redirect:
self.assertEquals(response.status_code, 302)
self.assertTrue(response['Location'].endswith(reverse('dashboard')))
else:
self.assertEquals(response.status_code, 200)
# TODO: Fix it so that response.templates works w/ mako templates, and then assert
# that the right template rendered
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
@ddt.data(
'',
'1,,2',
'1, ,2',
'1, 2, 3'
)
@patch('course_modes.views.modulestore', Mock())
def test_suggested_prices(self, price_list):
course_id = SlashSeparatedCourseKey('org', 'course', 'price_course')
user = UserFactory()
for mode in ('audit', 'honor'):
CourseModeFactory(mode_slug=mode, course_id=course_id)
CourseModeFactory(mode_slug='verified', course_id=course_id, suggested_prices=price_list)
self.client.login(
username=user.username,
password='test'
)
response = self.client.get(
reverse('course_modes_choose', args=[self.course_id.to_deprecated_string()]),
follow=False,
)
self.assertEquals(response.status_code, 200)
# TODO: Fix it so that response.templates works w/ mako templates, and then assert
# that the right template rendered
......@@ -36,16 +36,14 @@ class ChooseModeView(View):
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
enrollment_mode = CourseEnrollment.enrollment_mode_for_user(request.user, course_key)
enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(request.user, course_key)
upgrade = request.GET.get('upgrade', False)
request.session['attempting_upgrade'] = upgrade
# Inactive users always need to re-register
# verified users do not need to register or upgrade
if enrollment_mode == 'verified':
return redirect(reverse('dashboard'))
# registered users who are not trying to upgrade do not need to re-register
if enrollment_mode is not None and upgrade is False:
if is_active and (upgrade is False or enrollment_mode == 'verified'):
return redirect(reverse('dashboard'))
modes = CourseMode.modes_for_course_dict(course_key)
......@@ -64,7 +62,11 @@ class ChooseModeView(View):
"upgrade": upgrade,
}
if "verified" in modes:
context["suggested_prices"] = [decimal.Decimal(x) for x in modes["verified"].suggested_prices.split(",")]
context["suggested_prices"] = [
decimal.Decimal(x.strip())
for x in modes["verified"].suggested_prices.split(",")
if x.strip()
]
context["currency"] = modes["verified"].currency.upper()
context["min_price"] = modes["verified"].min_price
......
......@@ -880,18 +880,15 @@ class CourseEnrollment(models.Model):
`user` is a Django User object
`course_id` is our usual course_id string (e.g. "edX/Test101/2013_Fall)
Returns the mode for both inactive and active users.
Returns None if the courseenrollment record does not exist.
Returns (mode, is_active) where mode is the enrollment mode of the student
and is_active is whether the enrollment is active.
Returns (None, None) if the courseenrollment record does not exist.
"""
try:
record = CourseEnrollment.objects.get(user=user, course_id=course_id)
if hasattr(record, 'mode'):
return record.mode
else:
return None
return (record.mode, record.is_active)
except cls.DoesNotExist:
return None
return (None, None)
@classmethod
def enrollments_for_user(cls, user):
......
......@@ -10,6 +10,7 @@ import factory
from factory.django import DjangoModelFactory
from uuid import uuid4
from pytz import UTC
from xmodule.modulestore.locations import SlashSeparatedCourseKey
# Factories don't have __init__ methods, and are self documenting
# pylint: disable=W0232, C0111
......@@ -109,14 +110,14 @@ class CourseEnrollmentFactory(DjangoModelFactory):
FACTORY_FOR = CourseEnrollment
user = factory.SubFactory(UserFactory)
course_id = u'edX/toy/2012_Fall'
course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
class CourseEnrollmentAllowedFactory(DjangoModelFactory):
FACTORY_FOR = CourseEnrollmentAllowed
email = 'test@edx.org'
course_id = 'edX/test/2012_Fall'
course_id = SlashSeparatedCourseKey('edX', 'toy', '2012_Fall')
class PendingEmailChangeFactory(DjangoModelFactory):
......
......@@ -7,7 +7,6 @@ from urlparse import urlparse
from os.path import splitext, basename
from HTMLParser import HTMLParser
def get_instructions(xmltree):
""" Removes <instructions> from the xmltree and returns them as a string, otherwise None. """
instructions = xmltree.find('instructions')
......
......@@ -9,13 +9,19 @@ from xmodule.raw_module import RawDescriptor
from xblock.core import Scope, String
from xmodule.annotator_mixin import get_instructions, html_to_text
from xmodule.annotator_token import retrieve_token
from xblock.fragment import Fragment
import textwrap
# Make '_' a no-op so we can scrape strings
_ = lambda text: text
class AnnotatableFields(object):
""" Fields for `ImageModule` and `ImageDescriptor`. """
data = String(help="XML data for the annotation", scope=Scope.content, default=textwrap.dedent("""\
data = String(help=_("XML data for the annotation"),
scope=Scope.content,
default=textwrap.dedent("""\
<annotatable>
<instructions>
<p>
......@@ -36,28 +42,47 @@ class AnnotatableFields(object):
</annotatable>
"""))
display_name = String(
display_name="Display Name",
help="Display name for this module",
display_name=_("Display Name"),
help=_("Display name for this module"),
scope=Scope.settings,
default='Image Annotation',
)
instructor_tags = String(
display_name="Tags for Assignments",
help="Add tags that automatically highlight in a certain color using the comma-separated form, i.e. imagery:red,parallelism:blue",
display_name=_("Tags for Assignments"),
help=_("Add tags that automatically highlight in a certain color using the comma-separated form, i.e. imagery:red,parallelism:blue"),
scope=Scope.settings,
default='professor:green,teachingAssistant:blue',
)
annotation_storage_url = String(
help="Location of Annotation backend",
help=_("Location of Annotation backend"),
scope=Scope.settings,
default="http://your_annotation_storage.com",
display_name="Url for Annotation Storage"
display_name=_("Url for Annotation Storage")
)
annotation_token_secret = String(
help="Secret string for annotation storage",
help=_("Secret string for annotation storage"),
scope=Scope.settings,
default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
display_name="Secret Token String for Annotation"
display_name=_("Secret Token String for Annotation")
)
default_tab = String(
display_name=_("Default Annotations Tab"),
help=_("Select which tab will be the default in the annotations table: myNotes, Instructor, or Public."),
scope=Scope.settings,
default="myNotes",
)
# currently only supports one instructor, will build functionality for multiple later
instructor_email = String(
display_name=_("Email for 'Instructor' Annotations"),
help=_("Email of the user that will be attached to all annotations that will be found in 'Instructor' tab."),
scope=Scope.settings,
default="",
)
annotation_mode = String(
display_name=_("Mode for Annotation Tool"),
help=_("Type in number corresponding to following modes: 'instructor' or 'everyone'"),
scope=Scope.settings,
default="everyone",
)
......@@ -91,18 +116,23 @@ class ImageAnnotationModule(AnnotatableFields, XModule):
""" Removes <instructions> from the xmltree and returns them as a string, otherwise None. """
return get_instructions(xmltree)
def get_html(self):
def student_view(self, context):
""" Renders parameters to template. """
context = {
'display_name': self.display_name_with_default,
'instructions_html': self.instructions,
'annotation_storage': self.annotation_storage_url,
'token': retrieve_token(self.user, self.annotation_token_secret),
'tag': self.instructor_tags,
'openseadragonjson': self.openseadragonjson,
'annotation_storage': self.annotation_storage_url,
'default_tab': self.default_tab,
'instructor_email': self.instructor_email,
'annotation_mode': self.annotation_mode,
}
return self.system.render_template('imageannotation.html', context)
fragment = Fragment(self.system.render_template('imageannotation.html', context))
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js")
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js")
return fragment
class ImageAnnotationDescriptor(AnnotatableFields, RawDescriptor): # pylint: disable=abstract-method
......
......@@ -226,7 +226,7 @@ class SplitTestModule(SplitTestFields, XModule):
Record in the tracking logs which child was rendered
"""
# TODO: use publish instead, when publish is wired to the tracking logs
self.system.track_function('xblock.split_test.child_render', {'child-id': self.child.scope_ids.usage_id})
self.system.track_function('xblock.split_test.child_render', {'child-id': self.child.scope_ids.usage_id.to_deprecated_string()})
return Response()
def get_icon_class(self):
......
......@@ -69,10 +69,10 @@ class ImageAnnotationModuleTestCase(unittest.TestCase):
actual = self.mod._extract_instructions(xmltree) # pylint: disable=protected-access
self.assertIsNone(actual)
def test_get_html(self):
def test_student_view(self):
"""
Tests the function that passes in all the information in the context that will be used in templates/textannotation.html
"""
context = self.mod.get_html()
context = self.mod.student_view({}).content
for key in ['display_name', 'instructions_html', 'annotation_storage', 'token', 'tag', 'openseadragonjson']:
self.assertIn(key, context)
......@@ -54,10 +54,10 @@ class TextAnnotationModuleTestCase(unittest.TestCase):
actual = self.mod._extract_instructions(xmltree) # pylint: disable=W0212
self.assertIsNone(actual)
def test_get_html(self):
def test_student_view(self):
"""
Tests the function that passes in all the information in the context that will be used in templates/textannotation.html
"""
context = self.mod.get_html()
context = self.mod.student_view({}).content
for key in ['display_name', 'tag', 'source', 'instructions_html', 'content_html', 'annotation_storage', 'token']:
self.assertIn(key, context)
......@@ -62,10 +62,10 @@ class VideoAnnotationModuleTestCase(unittest.TestCase):
self.assertEqual(expectedyoutube, result2)
self.assertEqual(expectednotyoutube, result1)
def test_get_html(self):
def test_student_view(self):
"""
Tests to make sure variables passed in truly exist within the html once it is all rendered.
"""
context = self.mod.get_html()
context = self.mod.student_view({}).content
for key in ['display_name', 'instructions_html', 'sourceUrl', 'typeSource', 'poster', 'annotation_storage']:
self.assertIn(key, context)
......@@ -8,7 +8,7 @@ from xmodule.raw_module import RawDescriptor
from xblock.core import Scope, String
from xmodule.annotator_mixin import get_instructions
from xmodule.annotator_token import retrieve_token
from xblock.fragment import Fragment
import textwrap
# Make '_' a no-op so we can scrape strings
......@@ -17,7 +17,9 @@ _ = lambda text: text
class AnnotatableFields(object):
"""Fields for `TextModule` and `TextDescriptor`."""
data = String(help=_("XML data for the annotation"), scope=Scope.content, default=textwrap.dedent("""\
data = String(help=_("XML data for the annotation"),
scope=Scope.content,
default=textwrap.dedent("""\
<annotatable>
<instructions>
<p>
......@@ -47,8 +49,43 @@ class AnnotatableFields(object):
scope=Scope.settings,
default='None',
)
annotation_storage_url = String(help=_("Location of Annotation backend"), scope=Scope.settings, default="http://your_annotation_storage.com", display_name=_("Url for Annotation Storage"))
annotation_token_secret = String(help=_("Secret string for annotation storage"), scope=Scope.settings, default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", display_name=_("Secret Token String for Annotation"))
diacritics = String(
display_name=_("Diacritic Marks"),
help=_("Add diacritic marks to be added to a text using the comma-separated form, i.e. markname;urltomark;baseline,markname2;urltomark2;baseline2"),
scope=Scope.settings,
default='',
)
annotation_storage_url = String(
help=_("Location of Annotation backend"),
scope=Scope.settings,
default="http://your_annotation_storage.com",
display_name=_("Url for Annotation Storage")
)
annotation_token_secret = String(
help=_("Secret string for annotation storage"),
scope=Scope.settings,
default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
display_name=_("Secret Token String for Annotation")
)
default_tab = String(
display_name=_("Default Annotations Tab"),
help=_("Select which tab will be the default in the annotations table: myNotes, Instructor, or Public."),
scope=Scope.settings,
default="myNotes",
)
# currently only supports one instructor, will build functionality for multiple later
instructor_email = String(
display_name=_("Email for 'Instructor' Annotations"),
help=_("Email of the user that will be attached to all annotations that will be found in 'Instructor' tab."),
scope=Scope.settings,
default="",
)
annotation_mode = String(
display_name=_("Mode for Annotation Tool"),
help=_("Type in number corresponding to following modes: 'instructor' or 'everyone'"),
scope=Scope.settings,
default="everyone",
)
class TextAnnotationModule(AnnotatableFields, XModule):
......@@ -73,7 +110,7 @@ class TextAnnotationModule(AnnotatableFields, XModule):
""" Removes <instructions> from the xmltree and returns them as a string, otherwise None. """
return get_instructions(xmltree)
def get_html(self):
def student_view(self, context):
""" Renders parameters to template. """
context = {
'course_key': self.runtime.course_id,
......@@ -82,10 +119,17 @@ class TextAnnotationModule(AnnotatableFields, XModule):
'source': self.source,
'instructions_html': self.instructions,
'content_html': self.content,
'annotation_storage': self.annotation_storage_url,
'token': retrieve_token(self.user_email, self.annotation_token_secret),
'diacritic_marks': self.diacritics,
'annotation_storage': self.annotation_storage_url,
'default_tab': self.default_tab,
'instructor_email': self.instructor_email,
'annotation_mode': self.annotation_mode,
}
return self.system.render_template('textannotation.html', context)
fragment = Fragment(self.system.render_template('textannotation.html', context))
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js")
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js")
return fragment
class TextAnnotationDescriptor(AnnotatableFields, RawDescriptor):
......
......@@ -9,6 +9,7 @@ from xmodule.raw_module import RawDescriptor
from xblock.core import Scope, String
from xmodule.annotator_mixin import get_instructions, get_extension
from xmodule.annotator_token import retrieve_token
from xblock.fragment import Fragment
import textwrap
......@@ -18,7 +19,9 @@ _ = lambda text: text
class AnnotatableFields(object):
""" Fields for `VideoModule` and `VideoDescriptor`. """
data = String(help=_("XML data for the annotation"), scope=Scope.content, default=textwrap.dedent("""\
data = String(help=_("XML data for the annotation"),
scope=Scope.content,
default=textwrap.dedent("""\
<annotatable>
<instructions>
<p>
......@@ -31,12 +34,51 @@ class AnnotatableFields(object):
display_name=_("Display Name"),
help=_("Display name for this module"),
scope=Scope.settings,
default='Video Annotation',
default=_('Video Annotation'),
)
sourceurl = String(
help=_("The external source URL for the video."),
display_name=_("Source URL"),
scope=Scope.settings, default="http://video-js.zencoder.com/oceans-clip.mp4"
)
poster_url = String(
help=_("Poster Image URL"),
display_name=_("Poster URL"),
scope=Scope.settings,
default=""
)
annotation_storage_url = String(
help=_("Location of Annotation backend"),
scope=Scope.settings,
default="http://your_annotation_storage.com",
display_name=_("Url for Annotation Storage")
)
annotation_token_secret = String(
help=_("Secret string for annotation storage"),
scope=Scope.settings,
default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
display_name=_("Secret Token String for Annotation")
)
default_tab = String(
display_name=_("Default Annotations Tab"),
help=_("Select which tab will be the default in the annotations table: myNotes, Instructor, or Public."),
scope=Scope.settings,
default="myNotes",
)
# currently only supports one instructor, will build functionality for multiple later
instructor_email = String(
display_name=_("Email for 'Instructor' Annotations"),
help=_("Email of the user that will be attached to all annotations that will be found in 'Instructor' tab."),
scope=Scope.settings,
default="",
)
annotation_mode = String(
display_name=_("Mode for Annotation Tool"),
help=_("Type in number corresponding to following modes: 'instructor' or 'everyone'"),
scope=Scope.settings,
default="everyone",
)
sourceurl = String(help=_("The external source URL for the video."), display_name=_("Source URL"), scope=Scope.settings, default="http://video-js.zencoder.com/oceans-clip.mp4")
poster_url = String(help=_("Poster Image URL"), display_name=_("Poster URL"), scope=Scope.settings, default="")
annotation_storage_url = String(help=_("Location of Annotation backend"), scope=Scope.settings, default="http://your_annotation_storage.com", display_name=_("Url for Annotation Storage"))
annotation_token_secret = String(help=_("Secret string for annotation storage"), scope=Scope.settings, default="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", display_name=_("Secret Token String for Annotation"))
class VideoAnnotationModule(AnnotatableFields, XModule):
'''Video Annotation Module'''
......@@ -72,7 +114,7 @@ class VideoAnnotationModule(AnnotatableFields, XModule):
''' get the extension of a given url '''
return get_extension(src_url)
def get_html(self):
def student_view(self, context):
""" Renders parameters to template. """
extension = self._get_extension(self.sourceurl)
......@@ -84,11 +126,16 @@ class VideoAnnotationModule(AnnotatableFields, XModule):
'typeSource': extension,
'poster': self.poster_url,
'content_html': self.content,
'annotation_storage': self.annotation_storage_url,
'token': retrieve_token(self.user_email, self.annotation_token_secret),
'annotation_storage': self.annotation_storage_url,
'default_tab': self.default_tab,
'instructor_email': self.instructor_email,
'annotation_mode': self.annotation_mode,
}
return self.system.render_template('videoannotation.html', context)
fragment = Fragment(self.system.render_template('videoannotation.html', context))
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js")
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js")
return fragment
class VideoAnnotationDescriptor(AnnotatableFields, RawDescriptor):
......
......@@ -380,19 +380,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
clickTimeThreshold: viewer.clickTimeThreshold,
clickDistThreshold: viewer.clickDistThreshold
});
/* Set elements to the control menu */
viewer.annotatorControl = viewer.wrapperAnnotation.element;
if( viewer.toolbar ){
viewer.toolbar.addControl(
viewer.annotatorControl,
{anchor: $.ControlAnchor.BOTTOM_RIGHT}
);
}else{
viewer.addControl(
viewer.annotatorControl,
{anchor: $.ControlAnchor.TOP_LEFT}
);
if(this.options.viewer.annotation_mode == "everyone" || this.options.viewer.flags){
/* Set elements to the control menu */
viewer.annotatorControl = viewer.wrapperAnnotation.element;
if( viewer.toolbar ){
viewer.toolbar.addControl(
viewer.annotatorControl,
{anchor: $.ControlAnchor.BOTTOM_RIGHT}
);
}else{
viewer.addControl(
viewer.annotatorControl,
{anchor: $.ControlAnchor.TOP_LEFT}
);
}
}
},
_reset: function(){
......
......@@ -379,14 +379,14 @@
display:inline-block;
color:#302f2f;
font-family:arial;
font-size:15px;
font-size:14px;
font-weight:bold;
padding:6px 24px;
text-decoration:none;
margin: 0px 0px 10px 0px;
cursor:pointer;
width:140px;
width:115px;
text-align:center;
}
......@@ -468,7 +468,7 @@
#mainCatch .searchbox input{
margin: 0;
padding: 0;
width: 60%;
width: 50%;
margin-left: 10px;
display: inline;
float: left;
......@@ -493,19 +493,28 @@
cursor:pointer;
}
#mainCatch .searchbox .clear-search-icon{
font-size: 12px;
text-decoration: underline;
float: right;
margin-top: 10px;
padding-right: 3px;
cursor:pointer;
}
#mainCatch .searchbox .search-icon:hover{
opacity:0.5;
box-shadow: 2px 4px 5px #888888;
}
#mainCatch .selectors{
width:40%;
width:45%;
position:relative;
float:left;
}
#mainCatch .searchbox{
width:60%;
width:52%;
position:relative;
float:right;
}
......@@ -515,6 +524,7 @@
position:relative;
padding-right:5px;
margin-top:8px;
font-size:14px;
}
#mainCatch .replies .replyItem .deleteReply{
......
......@@ -7872,14 +7872,14 @@ $.extend( $.IIIF1_1TileSource.prototype, $.TileSource.prototype, {
uri;
if ( level_width < this.tile_width && level_height < this.tile_height ){
iiif_size = level_width + "," + level_height;
iiif_size = level_width + ",";
iiif_region = 'full';
} else {
iiif_tile_x = x * iiif_tile_size_width;
iiif_tile_y = y * iiif_tile_size_height;
iiif_tile_w = Math.min( iiif_tile_size_width, this.width - iiif_tile_x );
iiif_tile_h = Math.min( iiif_tile_size_height, this.height - iiif_tile_y );
iiif_size = Math.ceil(iiif_tile_w * scale) + "," + Math.ceil(iiif_tile_h * scale);
iiif_size = Math.ceil(iiif_tile_w * scale) + ",";
iiif_region = [ iiif_tile_x, iiif_tile_y, iiif_tile_w, iiif_tile_h ].join(',');
}
uri = [ this['@id'], iiif_region, iiif_size, IIIF_ROTATION, IIIF_QUALITY ].join('/');
......
......@@ -181,7 +181,7 @@ class XQueueCertInterface(object):
course_name = course.display_name or course_id.to_deprecated_string()
is_whitelisted = self.whitelist.filter(user=student, course_id=course_id, whitelist=True).exists()
grade = grades.grade(student, self.request, course)
enrollment_mode = CourseEnrollment.enrollment_mode_for_user(student, course_id)
enrollment_mode, __ = CourseEnrollment.enrollment_mode_for_user(student, course_id)
mode_is_verified = (enrollment_mode == GeneratedCertificate.MODES.verified)
user_is_verified = SoftwareSecurePhotoVerification.user_is_verified(student)
user_is_reverified = SoftwareSecurePhotoVerification.user_is_reverified_for_all(course_id, student)
......
......@@ -11,9 +11,7 @@ from xmodule.modulestore.locations import SlashSeparatedCourseKey
@login_required
def notes(request, course_id):
''' Displays the student's notes. '''
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
course = get_course_with_access(request.user, 'load', course_key)
if not notes_enabled_for_course(course):
raise Http404
......@@ -28,6 +26,7 @@ def notes(request, course_id):
'student': student,
'storage': storage,
'token': retrieve_token(student.email, course.annotation_token_secret),
'default_tab': 'myNotes',
}
return render_to_response('notes.html', context)
......@@ -65,7 +65,7 @@ class VerifyView(View):
reverse('verify_student_verified',
kwargs={'course_id': course_id.to_deprecated_string()}) + "?upgrade={}".format(upgrade)
)
elif CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == 'verified':
elif CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True):
return redirect(reverse('dashboard'))
else:
# If they haven't completed a verification attempt, we have to
......@@ -119,7 +119,7 @@ class VerifiedView(View):
"""
upgrade = request.GET.get('upgrade', False)
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == 'verified':
if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True):
return redirect(reverse('dashboard'))
verify_mode = CourseMode.mode_for_course(course_id, "verified")
......@@ -284,7 +284,7 @@ def show_requirements(request, course_id):
Show the requirements necessary for the verification flow.
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == 'verified':
if CourseEnrollment.enrollment_mode_for_user(request.user, course_id) == ('verified', True):
return redirect(reverse('dashboard'))
upgrade = request.GET.get('upgrade', False)
......
......@@ -9,72 +9,69 @@
</style>
<div class="annotatable-wrapper">
<div class="annotatable-header">
% if display_name is not UNDEFINED and display_name is not None:
<div class="annotatable-title">${display_name}</div>
% endif
</div>
% if instructions_html is not UNDEFINED and instructions_html is not None:
<div class="annotatable-section shaded">
<div class="annotatable-section-title">
${_('Instructions')}
<a class="annotatable-toggle annotatable-toggle-instructions expanded" href="javascript:void(0)">${_('Collapse Instructions')}</a>
</div>
<div class="annotatable-section-body annotatable-instructions">
${instructions_html}
</div>
</div>
% endif
<div class="annotatable-section">
<div class="annotatable-content">
<div id="imageHolder" class="openseadragon1">
<div class="annotatable-header">
% if display_name is not UNDEFINED and display_name is not None:
<div class="annotatable-title">${display_name}</div>
% endif
</div>
% if instructions_html is not UNDEFINED and instructions_html is not None:
<div class="annotatable-section shaded">
<div class="annotatable-section-title">
${_('Instructions')}
<a class="annotatable-toggle annotatable-toggle-instructions expanded" href="javascript:void(0)">${_('Collapse Instructions')}</a>
</div>
<div class="annotatable-section-body annotatable-instructions">
${instructions_html}
</div>
</div>
% endif
<div class="annotatable-section">
<div class="annotatable-content">
<div id="imageHolder" class="openseadragon1">
<%namespace name='static' file='/static_content.html'/>
${static.css(group='style-vendor-tinymce-content', raw=True)}
${static.css(group='style-vendor-tinymce-skin', raw=True)}
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/tinymce.full.min.js', raw=True)}" />
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js', raw=True)}" />
</div>
<div id="catchDIV">
## Translators: Notes below refer to annotations. They wil later be put under a "Notes" section.
<div class="annotationListContainer">${_('You do not have any notes.')}</div>
</div>
</div>
</div>
<div id="catchDIV">
<div class="annotationListContainer">${_('Note: only instructors may annotate.')}</div>
</div>
</div>
</div>
</div>
<script>
function onClickHideInstructions(){
//Reset function if there is more than one event handler
$(this).off();
$(this).on('click',onClickHideInstructions);
var hide = $(this).html()=='Collapse Instructions'?true:false,
cls, txt,slideMethod;
txt = (hide ? 'Expand' : 'Collapse') + ' Instructions';
cls = (hide ? ['expanded', 'collapsed'] : ['collapsed', 'expanded']);
slideMethod = (hide ? 'slideUp' : 'slideDown');
$(this).text(txt).removeClass(cls[0]).addClass(cls[1]);
$(this).parents('.annotatable-section:first').find('.annotatable-instructions')[slideMethod]();
}
$('.annotatable-toggle-instructions').on('click', onClickHideInstructions);
//Grab uri of the course
var parts = window.location.href.split("/"),
uri = '',
courseid;
for (var index = 0; index <= 9; index += 1) uri += parts[index]+"/"; //Get the unit url
courseid = parts[4] + "/" + parts[5] + "/" + parts[6];
//Change uri in cms
var lms_location = $('.sidebar .preview-button').attr('href');
if (typeof lms_location!='undefined'){
courseid = parts[4].split(".").join("/");
uri = window.location.protocol;
for (var index = 0; index <= 9; index += 1) uri += lms_location.split("/")[index]+"/"; //Get the unit url
}
var unit_id = $('#sequence-list').find('.active').attr("data-element");
function onClickHideInstructions(){
//Reset function if there is more than one event handler
$(this).off();
$(this).on('click',onClickHideInstructions);
var hide = $(this).html()=='Collapse Instructions'?true:false,
cls, txt,slideMethod;
txt = (hide ? 'Expand' : 'Collapse') + ' Instructions';
cls = (hide ? ['expanded', 'collapsed'] : ['collapsed', 'expanded']);
slideMethod = (hide ? 'slideUp' : 'slideDown');
$(this).text(txt).removeClass(cls[0]).addClass(cls[1]);
$(this).parents('.annotatable-section:first').find('.annotatable-instructions')[slideMethod]();
}
$('.annotatable-toggle-instructions').on('click', onClickHideInstructions);
//Grab uri of the course
var parts = window.location.href.split("/"),
uri = '',
courseid;
for (var index = 0; index <= 9; index += 1) uri += parts[index]+"/"; //Get the unit url
courseid = parts[4] + "/" + parts[5] + "/" + parts[6];
//Change uri in cms
var lms_location = $('.sidebar .preview-button').attr('href');
if (typeof lms_location!='undefined'){
courseid = parts[4].split(".").join("/");
uri = window.location.protocol;
for (var index = 0; index <= 9; index += 1) uri += lms_location.split("/")[index]+"/"; //Get the unit url
}
var unit_id = $('#sequence-list').find('.active').attr("data-element");
uri += unit_id;
var pagination = 100,
is_staff = !('${user.is_staff}'=='False'),
var pagination = 100,
is_staff = ('${user.email}'=='${instructor_email}'),
options = {
optionsAnnotator: {
permissions:{
......@@ -105,7 +102,7 @@
if (annotation.permissions) {
tokens = annotation.permissions[action] || [];
if (is_staff){
return true;
return true;
}
if (tokens.length === 0) {
return true;
......@@ -155,7 +152,7 @@
offset:0,
uri:uri,
media:'image',
userid:'${user.email}',
userid:'${user.email}',
}
},
highlightTags:{
......@@ -174,37 +171,52 @@
},
optionsOpenSeadragon:{
id: "imageHolder",
annotation_mode: "${annotation_mode}",
flags: is_staff,
prefixUrl: "${settings.STATIC_URL}" + "js/vendor/ova/images/",
${openseadragonjson}
},
optionsOSDA:{},
};
tinymce.baseURL = "${settings.STATIC_URL}" + "js/vendor/tinymce/js/tinymce";
var imgURLRoot = "${settings.STATIC_URL}" + "js/vendor/ova/catch/img/";
if (typeof Annotator != 'undefined'){
//remove old instances
if (Annotator._instances.length !== 0) {
$('#imageHolder').annotator("destroy");
}
delete osda;
//Load the plugin Image/Text Annotation
var osda = new OpenSeadragonAnnotation($('#imageHolder'),options);
//Catch
var annotator = osda.annotator,
catchOptions = {
media:'image',
externalLink:false,
imageUrlRoot:imgURLRoot,
showMediaSelector: false,
showPublicPrivate: true,
userId:'${user.email}',
pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff
},
Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
//remove old instances
if (Annotator._instances.length !== 0) {
$('#imageHolder').annotator("destroy");
}
delete osda;
//Load the plugin Image/Text Annotation
var osda = new OpenSeadragonAnnotation($('#imageHolder'),options);
var userId = ('${default_tab}'.toLowerCase() === 'instructor') ?
'${instructor_email}':
'${user.email}';
//Catch
var annotator = osda.annotator;
var catchOptions = {
media:'image',
externalLink:false,
imageUrlRoot:imgURLRoot,
showMediaSelector: false,
showPublicPrivate: true,
userId:userId,
pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff,
default_tab: "${default_tab}",
instructor_email: "${instructor_email}",
annotation_mode: "${annotation_mode}",
};
// if annotations are opened to everyone (2) or if they want to create no annotations (1 with no instructor)
// then the table at the bottom of the source should be displayed
if ("${annotation_mode}" == "everyone" || ("${annotation_mode}" == "instructor" && "${instructor_email}" != ""))
var Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
// if it is in instructor mode only (1), the annotator should be destroyed for all except the instructor
if ("${annotation_mode}" == "instructor" && "${instructor_email}" == "" && !is_staff)
osda.annotator.destroy();
}
</script>
\ No newline at end of file
<%! from django.utils.translation import ugettext as _ %>
<%namespace name='static' file='static_content.html'/>
${static.css(group='style-vendor-tinymce-content', raw=True)}
${static.css(group='style-vendor-tinymce-skin', raw=True)}
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/tinymce.full.min.js', raw=True)}"></script>
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js', raw=True)}" ></script>
<%inherit file="main.html" />
<%!
from django.core.urlresolvers import reverse
......@@ -102,7 +106,7 @@
if (annotation.permissions) {
tokens = annotation.permissions[action] || [];
if (is_staff){
return true;
return true;
}
if (tokens.length === 0) {
return true;
......@@ -128,7 +132,7 @@
},
},
auth: {
tokenUrl: location.protocol+'//'+location.host+"/token?course_id=${course.id.to_deprecated_string()}"
token: "${token}"
},
store: {
// The endpoint of the store on your server.
......@@ -158,37 +162,34 @@
optionsRichText: {
tinymce:{
selector: "li.annotator-item textarea",
plugins: "media image insertdatetime link code",
plugins: "media image codemirror",
menubar: false,
toolbar_items_size: 'small',
extended_valid_elements : "iframe[src|frameborder|style|scrolling|class|width|height|name|align|id]",
toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media rubric | code ",
toolbar: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image rubric | code ",
}
return true;
},
},
auth: {
token: "${token}"
},
store: {
// The endpoint of the store on your server.
prefix: "${storage}",
annotationData: {},
urls: {
// These are the default URLs.
create: '/create',
read: '/read/:id',
update: '/update/:id',
destroy: '/delete/:id',
search: '/search'
},
auth: {
token: "${token}"
},
store: {
// The endpoint of the store on your server.
prefix: "${storage}",
annotationData: {},
urls: {
// These are the default URLs.
create: '/create',
read: '/read/:id',
update: '/update/:id',
destroy: '/delete/:id',
search: '/search'
},
}
};
tinyMCE.baseURL = "${settings.STATIC_URL}" + "js/vendor/ova";
var imgURLRoot = "${settings.STATIC_URL}" + "js/vendor/ova/catch/img/";
//remove old instances
if (Annotator._instances.length !== 0) {
$('#notesHolder').annotator("destroy");
......@@ -207,7 +208,8 @@
showMediaSelector: true,
showPublicPrivate: true,
pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff
flags:is_staff,
default_tab: "${default_tab}",
},
Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
</script>
......
......@@ -2,8 +2,6 @@
<%namespace name='static' file='/static_content.html'/>
${static.css(group='style-vendor-tinymce-content', raw=True)}
${static.css(group='style-vendor-tinymce-skin', raw=True)}
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/tinymce.full.min.js', raw=True)}" />
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js', raw=True)}" />
<div class="annotatable-wrapper">
<div class="annotatable-header">
......@@ -167,7 +165,6 @@ ${static.css(group='style-vendor-tinymce-skin', raw=True)}
};
var imgURLRoot = "${settings.STATIC_URL}" + "js/vendor/ova/catch/img/";
tinymce.baseURL = "${settings.STATIC_URL}" + "js/vendor/tinymce/js/tinymce";
//remove old instances
if (Annotator._instances.length !== 0) {
......@@ -177,17 +174,24 @@ ${static.css(group='style-vendor-tinymce-skin', raw=True)}
//Load the plugin Video/Text Annotation
var ova = new OpenVideoAnnotation.Annotator($('#textHolder'),options);
var userId = ('${default_tab}'.toLowerCase() === 'instructor') ?
'${instructor_email}':
'${user.email}';
//Catch
var annotator = ova.annotator,
catchOptions = {
var annotator = ova.annotator;
var catchOptions = {
media:'text',
externalLink:false,
imageUrlRoot:imgURLRoot,
showMediaSelector: false,
showPublicPrivate: true,
userId:'${user.email}',
userId:userId,
pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff
},
Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
flags:is_staff,
default_tab: "${default_tab}",
instructor_email: "${instructor_email}",
annotation_mode: "${annotation_mode}",
};
var Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
</script>
......@@ -2,9 +2,6 @@
<%namespace name='static' file='/static_content.html'/>
${static.css(group='style-vendor-tinymce-content', raw=True)}
${static.css(group='style-vendor-tinymce-skin', raw=True)}
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/tinymce.full.min.js', raw=True)}" />
<script type="text/javascript" src="${static.url('js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js', raw=True)}" />
<div class="annotatable-wrapper">
<div class="annotatable-header">
......@@ -168,7 +165,6 @@ ${static.css(group='style-vendor-tinymce-skin', raw=True)}
};
var imgURLRoot = "${settings.STATIC_URL}" + "js/vendor/ova/catch/img/";
tinymce.baseURL = "${settings.STATIC_URL}" + "js/vendor/tinymce/js/tinymce";
//remove old instances
if (Annotator._instances.length !== 0) {
......@@ -179,18 +175,24 @@ ${static.css(group='style-vendor-tinymce-skin', raw=True)}
var ova = new OpenVideoAnnotation.Annotator($('#videoHolder'),options);
ova.annotator.addPlugin('Tags');
var userId = ('${default_tab}'.toLowerCase() === 'instructor') ?
'${instructor_email}':
'${user.email}';
//Catch
var annotator = ova.annotator,
catchOptions = {
var annotator = ova.annotator;
var catchOptions = {
media:'video',
externalLink:false,
imageUrlRoot:imgURLRoot,
showMediaSelector: false,
showPublicPrivate: true,
userId:'${user.email}',
userId:userId,
pagination:pagination,//Number of Annotations per load in the pagination,
flags:is_staff
},
Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
flags:is_staff,
default_tab: "${default_tab}",
instructor_email: "${instructor_email}",
annotation_mode: "${annotation_mode}",
};
var Catch = new CatchAnnotation($('#catchDIV'),catchOptions);
</script>
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