From cf8f6eccf38700944e9f95a5d9d5e532b6baaa1d Mon Sep 17 00:00:00 2001
From: daniel cebrian
Date: Fri, 7 Feb 2014 16:16:09 +0100
Subject: [PATCH 01/18] first commit to the branch
Added fix to horizontal extension in the TinyMCE RichText-Annotator.
Also includes trying to add annotator to Studio. Only thing failing
currently is getting TinyMCE to work at all.
Adding fixed to merging conflicts
Added folksonomic tags for annotations and also added the ability for video annotation to work on studio minus tinymce
Adding License to Tags plugin
Changed way that token is retrieved so it does not have to create a new view just to retrieve that item.
Other fixes:
1. Removed calls in base to tinymce found in the annotation tool so as
not to confuse it with the item already existing.
2. Removed references to token and in the url schema and moved the
firebase_token_generator out of the student django apps for the time
being (eventually it should be retrieved from the original project if
applicable)
Added ways for instructors to allow students to use diacritic tags in their text annotations
Added quick fix to diacritic annotator to work properly based on whether instructor added items or not
Removed reference to tinymce in OVA
removed instances of courseid as token handling has changed.
fixed duplicated code issue for token retriever. It's now its own file
Added token_secret to the variables for the annotation module
Fixed some quality errors and one retrive_token I missed.
---
cms/envs/common.py | 2 +-
cms/templates/base.html | 50 ++++-
common/djangoapps/student/tests/tests.py | 25 +--
common/djangoapps/student/views.py | 24 ---
common/lib/xmodule/xmodule/annotator_token.py | 27 +++
.../xmodule}/firebase_token_generator.py | 31 +--
.../xmodule/tests/test_annotator_token.py | 20 ++
.../xmodule/tests/test_textannotation.py | 15 +-
.../xmodule}/tests/test_token_generator.py | 7 +-
.../xmodule/tests/test_videoannotation.py | 100 +--------
.../xmodule/xmodule/textannotation_module.py | 34 +--
.../xmodule/xmodule/videoannotation_module.py | 81 +------
.../css/vendor/ova/diacritic-annotator.css | 6 +
.../css/vendor/ova/richText-annotator.css | 12 ++
.../static/css/vendor/ova/tags-annotator.css | 2 +-
common/static/js/vendor/ova/catch/js/catch.js | 4 +-
.../js/vendor/ova/diacritic-annotator.js | 203 ++++++++++++++++++
.../js/vendor/ova/flagging-annotator.js | 3 +-
common/static/js/vendor/ova/ova.js | 4 +-
common/static/js/vendor/ova/rangeslider.js | 2 +-
.../static/js/vendor/ova/reply-annotator.js | 3 +-
.../js/vendor/ova/richText-annotator.js | 6 +-
.../static/js/vendor/ova/share-annotator.js | 3 +-
common/static/js/vendor/ova/tags-annotator.js | 32 ++-
common/test/data/tilde/about/index.html~ | 1 -
common/test/data/tilde/static/example.txt~ | 1 -
lms/djangoapps/notes/views.py | 6 +-
lms/envs/common.py | 2 +
lms/templates/notes.html | 6 +-
lms/templates/textannotation.html | 67 +++---
lms/templates/videoannotation.html | 70 +++---
lms/urls.py | 1 -
32 files changed, 488 insertions(+), 362 deletions(-)
create mode 100644 common/lib/xmodule/xmodule/annotator_token.py
rename common/{djangoapps/student => lib/xmodule/xmodule}/firebase_token_generator.py (78%)
create mode 100644 common/lib/xmodule/xmodule/tests/test_annotator_token.py
rename common/{djangoapps/student => lib/xmodule/xmodule}/tests/test_token_generator.py (92%)
create mode 100644 common/static/css/vendor/ova/diacritic-annotator.css
create mode 100644 common/static/js/vendor/ova/diacritic-annotator.js
delete mode 100644 common/test/data/tilde/about/index.html~
delete mode 100644 common/test/data/tilde/static/example.txt~
diff --git a/cms/envs/common.py b/cms/envs/common.py
index 9e199e33723c..00e606e42f47 100644
--- a/cms/envs/common.py
+++ b/cms/envs/common.py
@@ -319,7 +319,7 @@
'css/vendor/ui-lightness/jquery-ui-1.8.22.custom.css',
'css/vendor/jquery.qtip.min.css',
'js/vendor/markitup/skins/simple/style.css',
- 'js/vendor/markitup/sets/wiki/style.css'
+ 'js/vendor/markitup/sets/wiki/style.css',
],
'output_filename': 'css/cms-style-vendor.css',
},
diff --git a/cms/templates/base.html b/cms/templates/base.html
index 93a3ec4ff7f6..658c7f41de9d 100644
--- a/cms/templates/base.html
+++ b/cms/templates/base.html
@@ -80,6 +80,20 @@
"accessibility": "js/src/accessibility_tools",
"draggabilly": "js/vendor/draggabilly.pkgd",
"URI": "js/vendor/URI.min",
+ "annotator": "js/vendor/ova/annotator-full",
+ "video.dev": "js/vendor/ova/video.dev",
+ "vjs.youtube": 'js/vendor/ova/vjs.youtube',
+ "rangeslider": 'js/vendor/ova/rangeslider',
+ "share-annotator": 'js/vendor/ova/share-annotator',
+ "richText-annotator": 'js/vendor/ova/richText-annotator',
+ "reply-annotator": 'js/vendor/ova/reply-annotator',
+ "tags-annotator": 'js/vendor/ova/tags-annotator',
+ "diacritic-annotator": 'js/vendor/ova/diacritic-annotator',
+ "flagging-annotator": 'js/vendor/ova/flagging-annotator',
+ "jquery-Watch": 'js/vendor/ova/jquery-Watch',
+ "ova": 'js/vendor/ova/ova',
+ "catch": 'js/vendor/ova/catch/js/catch',
+ "handlebars": 'js/vendor/ova/catch/js/handlebars-1.1.2',
// externally hosted files
"tender": [
@@ -95,7 +109,7 @@
// and leaves the path component intact.
"//www.youtube.com/player_api?noext",
// if youtube fails to load, fallback on a local file
- // so that require doesn't fall over
+ // so that require doesnt fall over
"js/src/youtube_fallback"
]
},
@@ -227,6 +241,40 @@
"coffee/src/logger": {
exports: "Logger",
deps: ["coffee/src/ajax_prefix"]
+ },
+ "video.dev": {
+ exports:"videojs"
+ },
+ "vjs.youtube": {
+ deps: ["video.dev"]
+ },
+ "rangeslider": {
+ deps: ["video.dev"]
+ },
+ "annotator": {
+ exports: "Annotator"
+ },
+ "share-annotator": {
+ deps: ["annotator"]
+ },
+ "richText-annotator": {
+ deps: ["annotator", "tinymce"]
+ },
+ "reply-annotator": {
+ deps: ["annotator"]
+ },
+ "tags-annotator": {
+ deps: ["annotator"]
+ },
+ "diacritic-annotator": {
+ deps: ["annotator"]
+ },
+ "flagging-annotator": {
+ deps: ["annotator"]
+ },
+ "ova":{
+ exports: "ova",
+ deps: ["annotator", "video.dev", "vjs.youtube", "rangeslider", "share-annotator", "tinymce", "richText-annotator", "reply-annotator", "tags-annotator", "flagging-annotator", "diacritic-annotator", "jquery-Watch", "catch", "handlebars", "URI"]
}
},
// load jquery and gettext automatically
diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py
index 48d8bb642eb2..93f4fbd7f4a3 100644
--- a/common/djangoapps/student/tests/tests.py
+++ b/common/djangoapps/student/tests/tests.py
@@ -25,7 +25,7 @@
from student.models import anonymous_id_for_user, user_by_anonymous_id, CourseEnrollment, unique_id_for_user
from student.views import (process_survey_link, _cert_info,
- change_enrollment, complete_course_mode_info, token)
+ change_enrollment, complete_course_mode_info)
from student.tests.factories import UserFactory, CourseModeFactory
import shoppingcart
@@ -458,26 +458,3 @@ def test_roundtrip_for_logged_user(self):
anonymous_id = anonymous_id_for_user(self.user, self.course.id)
real_user = user_by_anonymous_id(anonymous_id)
self.assertEqual(self.user, real_user)
-
-
-@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
-class Token(ModuleStoreTestCase):
- """
- Test for the token generator. This creates a random course and passes it through the token file which generates the
- token that will be passed in to the annotation_storage_url.
- """
- request_factory = RequestFactory()
- COURSE_SLUG = "100"
- COURSE_NAME = "test_course"
- COURSE_ORG = "edx"
-
- def setUp(self):
- self.course = CourseFactory.create(org=self.COURSE_ORG, display_name=self.COURSE_NAME, number=self.COURSE_SLUG)
- self.user = User.objects.create(username="username", email="username")
- self.req = self.request_factory.post('/token?course_id=edx/100/test_course', {'user': self.user})
- self.req.user = self.user
-
- def test_token(self):
- expected = HttpResponse("eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3N1ZWRBdCI6ICIyMDE0LTAxLTIzVDE5OjM1OjE3LjUyMjEwNC01OjAwIiwgImNvbnN1bWVyS2V5IjogInh4eHh4eHh4LXh4eHgteHh4eC14eHh4LXh4eHh4eHh4eHh4eCIsICJ1c2VySWQiOiAidXNlcm5hbWUiLCAidHRsIjogODY0MDB9.OjWz9mzqJnYuzX-f3uCBllqJUa8PVWJjcDy_McfxLvc", mimetype="text/plain")
- response = token(self.req)
- self.assertEqual(expected.content.split('.')[0], response.content.split('.')[0])
diff --git a/common/djangoapps/student/views.py b/common/djangoapps/student/views.py
index 4e9226b32556..42c1694dc39e 100644
--- a/common/djangoapps/student/views.py
+++ b/common/djangoapps/student/views.py
@@ -44,7 +44,6 @@
create_comments_service_user, PasswordHistory
)
from student.forms import PasswordResetFormNoActive
-from student.firebase_token_generator import create_token
from verify_student.models import SoftwareSecurePhotoVerification, MidcourseReverificationWindow
from certificates.models import CertificateStatuses, certificate_status_for_student
@@ -1780,26 +1779,3 @@ def change_email_settings(request):
track.views.server_track(request, "change-email-settings", {"receive_emails": "no", "course": course_id}, page='dashboard')
return JsonResponse({"success": True})
-
-
-@login_required
-def token(request):
- '''
- Return a token for the backend of annotations.
- It uses the course id to retrieve a variable that contains the secret
- token found in inheritance.py. It also contains information of when
- the token was issued. This will be stored with the user along with
- the id for identification purposes in the backend.
- '''
- course_id = request.GET.get("course_id")
- course = course_from_id(course_id)
- dtnow = datetime.datetime.now()
- dtutcnow = datetime.datetime.utcnow()
- delta = dtnow - dtutcnow
- newhour, newmin = divmod((delta.days * 24 * 60 * 60 + delta.seconds + 30) // 60, 60)
- newtime = "%s%+02d:%02d" % (dtnow.isoformat(), newhour, newmin)
- secret = course.annotation_token_secret
- custom_data = {"issuedAt": newtime, "consumerKey": secret, "userId": request.user.email, "ttl": 86400}
- newtoken = create_token(secret, custom_data)
- response = HttpResponse(newtoken, mimetype="text/plain")
- return response
diff --git a/common/lib/xmodule/xmodule/annotator_token.py b/common/lib/xmodule/xmodule/annotator_token.py
new file mode 100644
index 000000000000..55feb589c080
--- /dev/null
+++ b/common/lib/xmodule/xmodule/annotator_token.py
@@ -0,0 +1,27 @@
+'''
+This file contains a function used to retrieve the token for the annotation backend
+without having to create a view, but just returning a string instead.
+
+It can be called from other files by using the following:
+from xmodule.annotator_token import retrieve_token
+'''
+import datetime
+from xmodule.firebase_token_generator import create_token
+
+
+def retrieve_token(userid, secret):
+ '''
+ Return a token for the backend of annotations.
+ It uses the course id to retrieve a variable that contains the secret
+ token found in inheritance.py. It also contains information of when
+ the token was issued. This will be stored with the user along with
+ the id for identification purposes in the backend.
+ '''
+ dtnow = datetime.datetime.now()
+ dtutcnow = datetime.datetime.utcnow()
+ delta = dtnow - dtutcnow
+ newhour, newmin = divmod((delta.days * 24 * 60 * 60 + delta.seconds + 30) // 60, 60)
+ newtime = "%s%+02d:%02d" % (dtnow.isoformat(), newhour, newmin)
+ custom_data = {"issuedAt": newtime, "consumerKey": secret, "userId": userid, "ttl": 86400}
+ newtoken = create_token(secret, custom_data)
+ return newtoken
diff --git a/common/djangoapps/student/firebase_token_generator.py b/common/lib/xmodule/xmodule/firebase_token_generator.py
similarity index 78%
rename from common/djangoapps/student/firebase_token_generator.py
rename to common/lib/xmodule/xmodule/firebase_token_generator.py
index f84a85277e47..3d34483f6880 100644
--- a/common/djangoapps/student/firebase_token_generator.py
+++ b/common/lib/xmodule/xmodule/firebase_token_generator.py
@@ -11,7 +11,6 @@
from base64 import urlsafe_b64encode
import hashlib
import hmac
-import sys
try:
import json
except ImportError:
@@ -30,25 +29,17 @@ def create_token(secret, data):
return _encode_token(secret, data)
-if sys.version_info < (2, 7):
- def _encode(bytes_data):
- '''
- Takes a json object, string, or binary and
- uses python's urlsafe_b64encode to encode data
- and make it safe pass along in a url.
- To make sure it does not conflict with variables
- we make sure equal signs are removed.
- More info: docs.python.org/2/library/base64.html
- '''
- encoded = urlsafe_b64encode(bytes(bytes_data))
- return encoded.decode('utf-8').replace('=', '')
-else:
- def _encode(bytes_info):
- '''
- Same as above function but for Python 2.7 or later
- '''
- encoded = urlsafe_b64encode(bytes_info)
- return encoded.decode('utf-8').replace('=', '')
+def _encode(bytes_info):
+ '''
+ Takes a json object, string, or binary and
+ uses python's urlsafe_b64encode to encode data
+ and make it safe pass along in a url.
+ To make sure it does not conflict with variables
+ we make sure equal signs are removed.
+ More info: docs.python.org/2/library/base64.html
+ '''
+ encoded = urlsafe_b64encode(bytes_info)
+ return encoded.decode('utf-8').replace('=', '')
def _encode_json(obj):
diff --git a/common/lib/xmodule/xmodule/tests/test_annotator_token.py b/common/lib/xmodule/xmodule/tests/test_annotator_token.py
new file mode 100644
index 000000000000..e190d7518ee8
--- /dev/null
+++ b/common/lib/xmodule/xmodule/tests/test_annotator_token.py
@@ -0,0 +1,20 @@
+"""
+This test will run for annotator_token.py
+"""
+import unittest
+
+from xmodule.annotator_token import retrieve_token
+
+
+class TokenRetriever(unittest.TestCase):
+ """
+ Tests to make sure that when passed in a username and secret token, that it will be encoded correctly
+ """
+ def test_token(self):
+ """
+ Test for the token generator. Give an a random username and secret token, it should create the properly encoded string of text.
+ """
+ expected = "eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3N1ZWRBdCI6ICIyMDE0LTAyLTI3VDE3OjAwOjQyLjQwNjQ0MSswOjAwIiwgImNvbnN1bWVyS2V5IjogImZha2Vfc2VjcmV0IiwgInVzZXJJZCI6ICJ1c2VybmFtZSIsICJ0dGwiOiA4NjQwMH0.Dx1PoF-7mqBOOSGDMZ9R_s3oaaLRPnn6CJgGGF2A5CQ"
+ response = retrieve_token("username", "fake_secret")
+ self.assertEqual(expected.split('.')[0], response.split('.')[0])
+ self.assertNotEqual(expected.split('.')[2], response.split('.')[2])
diff --git a/common/lib/xmodule/xmodule/tests/test_textannotation.py b/common/lib/xmodule/xmodule/tests/test_textannotation.py
index 397e3990ef60..25417411ac0c 100644
--- a/common/lib/xmodule/xmodule/tests/test_textannotation.py
+++ b/common/lib/xmodule/xmodule/tests/test_textannotation.py
@@ -12,7 +12,6 @@
from . import get_test_system
-
class TextAnnotationModuleTestCase(unittest.TestCase):
''' text Annotation Module Test Case '''
sample_xml = '''
@@ -26,7 +25,6 @@ class TextAnnotationModuleTestCase(unittest.TestCase):
'''
-
def setUp(self):
"""
Makes sure that the Module is declared and mocked with the sample xml above.
@@ -38,17 +36,6 @@ def setUp(self):
ScopeIds(None, None, None, None)
)
- def test_render_content(self):
- """
- Tests to make sure the sample xml is rendered and that it forms a valid xmltree
- that does not contain a display_name.
- """
- content = self.mod._render_content() # pylint: disable=W0212
- self.assertIsNotNone(content)
- element = etree.fromstring(content)
- self.assertIsNotNone(element)
- self.assertFalse('display_name' in element.attrib, "Display Name should have been deleted from Content")
-
def test_extract_instructions(self):
"""
Tests to make sure that the instructions are correctly pulled from the sample xml above.
@@ -70,5 +57,5 @@ def test_get_html(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()
- for key in ['display_name', 'tag', 'source', 'instructions_html', 'content_html', 'annotation_storage']:
+ for key in ['display_name', 'tag', 'source', 'instructions_html', 'content_html', 'annotation_storage', 'token']:
self.assertIn(key, context)
diff --git a/common/djangoapps/student/tests/test_token_generator.py b/common/lib/xmodule/xmodule/tests/test_token_generator.py
similarity index 92%
rename from common/djangoapps/student/tests/test_token_generator.py
rename to common/lib/xmodule/xmodule/tests/test_token_generator.py
index 1eb09c9173c0..ab2762f0a461 100644
--- a/common/djangoapps/student/tests/test_token_generator.py
+++ b/common/lib/xmodule/xmodule/tests/test_token_generator.py
@@ -1,13 +1,12 @@
"""
This test will run for firebase_token_generator.py.
"""
+import unittest
-from django.test import TestCase
+from xmodule.firebase_token_generator import _encode, _encode_json, _encode_token, create_token
-from student.firebase_token_generator import _encode, _encode_json, _encode_token, create_token
-
-class TokenGenerator(TestCase):
+class TokenGenerator(unittest.TestCase):
"""
Tests for the file firebase_token_generator.py
"""
diff --git a/common/lib/xmodule/xmodule/tests/test_videoannotation.py b/common/lib/xmodule/xmodule/tests/test_videoannotation.py
index cb63d055038d..522cea23eda6 100644
--- a/common/lib/xmodule/xmodule/tests/test_videoannotation.py
+++ b/common/lib/xmodule/xmodule/tests/test_videoannotation.py
@@ -12,7 +12,6 @@
from . import get_test_system
-
class VideoAnnotationModuleTestCase(unittest.TestCase):
''' Video Annotation Module Test Case '''
sample_xml = '''
@@ -34,100 +33,6 @@ def setUp(self):
ScopeIds(None, None, None, None)
)
- def test_annotation_class_attr_default(self):
- """
- Makes sure that it can detect annotation values in text-form if user
- decides to add text to the area below video, video functionality is completely
- found in javascript.
- """
- xml = 'test'
- element = etree.fromstring(xml)
-
- expected_attr = {'class': {'value': 'annotatable-span highlight'}}
- actual_attr = self.mod._get_annotation_class_attr(element) # pylint: disable=W0212
-
- self.assertIsInstance(actual_attr, dict)
- self.assertDictEqual(expected_attr, actual_attr)
-
- def test_annotation_class_attr_with_valid_highlight(self):
- """
- Same as above but more specific to an area that is highlightable in the appropriate
- color designated.
- """
- xml = 'test'
-
- for color in self.mod.highlight_colors:
- element = etree.fromstring(xml.format(highlight=color))
- value = 'annotatable-span highlight highlight-{highlight}'.format(highlight=color)
-
- expected_attr = {'class': {
- 'value': value,
- '_delete': 'highlight'}
- }
- actual_attr = self.mod._get_annotation_class_attr(element) # pylint: disable=W0212
-
- self.assertIsInstance(actual_attr, dict)
- self.assertDictEqual(expected_attr, actual_attr)
-
- def test_annotation_class_attr_with_invalid_highlight(self):
- """
- Same as above, but checked with invalid colors.
- """
- xml = 'test'
-
- for invalid_color in ['rainbow', 'blink', 'invisible', '', None]:
- element = etree.fromstring(xml.format(highlight=invalid_color))
- expected_attr = {'class': {
- 'value': 'annotatable-span highlight',
- '_delete': 'highlight'}
- }
- actual_attr = self.mod._get_annotation_class_attr(element) # pylint: disable=W0212
-
- self.assertIsInstance(actual_attr, dict)
- self.assertDictEqual(expected_attr, actual_attr)
-
- def test_annotation_data_attr(self):
- """
- Test that each highlight contains the data information from the annotation itself.
- """
- element = etree.fromstring('test')
-
- expected_attr = {
- 'data-comment-body': {'value': 'foo', '_delete': 'body'},
- 'data-comment-title': {'value': 'bar', '_delete': 'title'},
- 'data-problem-id': {'value': '0', '_delete': 'problem'}
- }
-
- actual_attr = self.mod._get_annotation_data_attr(element) # pylint: disable=W0212
-
- self.assertIsInstance(actual_attr, dict)
- self.assertDictEqual(expected_attr, actual_attr)
-
- def test_render_annotation(self):
- """
- Tests to make sure that the spans designating annotations acutally visually render as annotations.
- """
- expected_html = 'z'
- expected_el = etree.fromstring(expected_html)
-
- actual_el = etree.fromstring('z')
- self.mod._render_annotation(actual_el) # pylint: disable=W0212
-
- self.assertEqual(expected_el.tag, actual_el.tag)
- self.assertEqual(expected_el.text, actual_el.text)
- self.assertDictEqual(dict(expected_el.attrib), dict(actual_el.attrib))
-
- def test_render_content(self):
- """
- Like above, but using the entire text, it makes sure that display_name is removed and that there is only one
- div encompassing the annotatable area.
- """
- content = self.mod._render_content() # pylint: disable=W0212
- element = etree.fromstring(content)
- self.assertIsNotNone(element)
- self.assertEqual('div', element.tag, 'root tag is a div')
- self.assertFalse('display_name' in element.attrib, "Display Name should have been deleted from Content")
-
def test_extract_instructions(self):
"""
This test ensures that if an instruction exists it is pulled and
@@ -137,6 +42,7 @@ def test_extract_instructions(self):
expected_xml = u"
Video Test Instructions.
"
actual_xml = self.mod._extract_instructions(xmltree) # pylint: disable=W0212
+
self.assertIsNotNone(actual_xml)
self.assertEqual(expected_xml.strip(), actual_xml.strip())
@@ -160,6 +66,6 @@ def test_get_html(self):
"""
Tests to make sure variables passed in truly exist within the html once it is all rendered.
"""
- context = self.mod.get_html()
- for key in ['display_name', 'content_html', 'instructions_html', 'sourceUrl', 'typeSource', 'poster', 'alert', 'annotation_storage']:
+ context = self.mod.get_html() # pylint: disable=W0212
+ for key in ['display_name', 'instructions_html', 'sourceUrl', 'typeSource', 'poster', 'annotation_storage']:
self.assertIn(key, context)
diff --git a/common/lib/xmodule/xmodule/textannotation_module.py b/common/lib/xmodule/xmodule/textannotation_module.py
index 1d732d870925..c6ff6b9b1a0c 100644
--- a/common/lib/xmodule/xmodule/textannotation_module.py
+++ b/common/lib/xmodule/xmodule/textannotation_module.py
@@ -6,6 +6,7 @@
from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor
from xblock.core import Scope, String
+from xmodule.annotator_token import retrieve_token
import textwrap
@@ -30,7 +31,7 @@ class AnnotatableFields(object):
scope=Scope.settings,
default='Text Annotation',
)
- tags = String(
+ 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",
scope=Scope.settings,
@@ -43,6 +44,13 @@ class AnnotatableFields(object):
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='',
+ )
class TextAnnotationModule(AnnotatableFields, XModule):
@@ -59,15 +67,9 @@ def __init__(self, *args, **kwargs):
self.instructions = self._extract_instructions(xmltree)
self.content = etree.tostring(xmltree, encoding='unicode')
- self.highlight_colors = ['yellow', 'orange', 'purple', 'blue', 'green']
-
- def _render_content(self):
- """ Renders annotatable content with annotation spans and returns HTML. """
- xmltree = etree.fromstring(self.content)
- if 'display_name' in xmltree.attrib:
- del xmltree.attrib['display_name']
-
- return etree.tostring(xmltree, encoding='unicode')
+ self.user = ""
+ if self.runtime.get_real_user is not None:
+ self.user = self.runtime.get_real_user(self.runtime.anonymous_student_id).email
def _extract_instructions(self, xmltree):
""" Removes from the xmltree and returns them as a string, otherwise None. """
@@ -82,13 +84,14 @@ def get_html(self):
""" Renders parameters to template. """
context = {
'display_name': self.display_name_with_default,
- 'tag': self.tags,
+ 'tag': self.instructor_tags,
'source': self.source,
'instructions_html': self.instructions,
- 'content_html': self._render_content(),
- 'annotation_storage': self.annotation_storage_url
+ 'content_html': self.content,
+ 'annotation_storage': self.annotation_storage_url,
+ 'token': retrieve_token(self.user, self.annotation_token_secret),
+ 'diacritic_marks': self.diacritics,
}
-
return self.system.render_template('textannotation.html', context)
@@ -101,6 +104,7 @@ class TextAnnotationDescriptor(AnnotatableFields, RawDescriptor):
def non_editable_metadata_fields(self):
non_editable_fields = super(TextAnnotationDescriptor, self).non_editable_metadata_fields
non_editable_fields.extend([
- TextAnnotationDescriptor.annotation_storage_url
+ TextAnnotationDescriptor.annotation_storage_url,
+ TextAnnotationDescriptor.annotation_token_secret
])
return non_editable_fields
diff --git a/common/lib/xmodule/xmodule/videoannotation_module.py b/common/lib/xmodule/xmodule/videoannotation_module.py
index 5f31509d013e..9644c04fa997 100644
--- a/common/lib/xmodule/xmodule/videoannotation_module.py
+++ b/common/lib/xmodule/xmodule/videoannotation_module.py
@@ -7,6 +7,7 @@
from xmodule.x_module import XModule
from xmodule.raw_module import RawDescriptor
from xblock.core import Scope, String
+from xmodule.annotator_token import retrieve_token
import textwrap
@@ -31,7 +32,7 @@ class AnnotatableFields(object):
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'''
@@ -55,73 +56,9 @@ def __init__(self, *args, **kwargs):
self.instructions = self._extract_instructions(xmltree)
self.content = etree.tostring(xmltree, encoding='unicode')
- self.highlight_colors = ['yellow', 'orange', 'purple', 'blue', 'green']
-
- def _get_annotation_class_attr(self, element):
- """ Returns a dict with the CSS class attribute to set on the annotation
- and an XML key to delete from the element.
- """
-
- attr = {}
- cls = ['annotatable-span', 'highlight']
- highlight_key = 'highlight'
- color = element.get(highlight_key)
-
- if color is not None:
- if color in self.highlight_colors:
- cls.append('highlight-' + color)
- attr['_delete'] = highlight_key
- attr['value'] = ' '.join(cls)
-
- return {'class': attr}
-
- def _get_annotation_data_attr(self, element):
- """ Returns a dict in which the keys are the HTML data attributes
- to set on the annotation element. Each data attribute has a
- corresponding 'value' and (optional) '_delete' key to specify
- an XML attribute to delete.
- """
-
- data_attrs = {}
- attrs_map = {
- 'body': 'data-comment-body',
- 'title': 'data-comment-title',
- 'problem': 'data-problem-id'
- }
-
- for xml_key in attrs_map.keys():
- if xml_key in element.attrib:
- value = element.get(xml_key, '')
- html_key = attrs_map[xml_key]
- data_attrs[html_key] = {'value': value, '_delete': xml_key}
-
- return data_attrs
-
- def _render_annotation(self, element):
- """ Renders an annotation element for HTML output. """
- attr = {}
- attr.update(self._get_annotation_class_attr(element))
- attr.update(self._get_annotation_data_attr(element))
-
- element.tag = 'span'
-
- for key in attr.keys():
- element.set(key, attr[key]['value'])
- if '_delete' in attr[key] and attr[key]['_delete'] is not None:
- delete_key = attr[key]['_delete']
- del element.attrib[delete_key]
-
- def _render_content(self):
- """ Renders annotatable content with annotation spans and returns HTML. """
- xmltree = etree.fromstring(self.content)
- xmltree.tag = 'div'
- if 'display_name' in xmltree.attrib:
- del xmltree.attrib['display_name']
-
- for element in xmltree.findall('.//annotation'):
- self._render_annotation(element)
-
- return etree.tostring(xmltree, encoding='unicode')
+ self.user = ""
+ if self.runtime.get_real_user is not None:
+ self.user = self.runtime.get_real_user(self.runtime.anonymous_student_id).email
def _extract_instructions(self, xmltree):
""" Removes from the xmltree and returns them as a string, otherwise None. """
@@ -154,9 +91,8 @@ def get_html(self):
'sourceUrl': self.sourceurl,
'typeSource': extension,
'poster': self.poster_url,
- 'alert': self,
- 'content_html': self._render_content(),
- 'annotation_storage': self.annotation_storage_url
+ 'annotation_storage': self.annotation_storage_url,
+ 'token': retrieve_token(self.user, self.annotation_token_secret),
}
return self.system.render_template('videoannotation.html', context)
@@ -171,6 +107,7 @@ class VideoAnnotationDescriptor(AnnotatableFields, RawDescriptor):
def non_editable_metadata_fields(self):
non_editable_fields = super(VideoAnnotationDescriptor, self).non_editable_metadata_fields
non_editable_fields.extend([
- VideoAnnotationDescriptor.annotation_storage_url
+ VideoAnnotationDescriptor.annotation_storage_url,
+ VideoAnnotationDescriptor.annotation_token_secret
])
return non_editable_fields
diff --git a/common/static/css/vendor/ova/diacritic-annotator.css b/common/static/css/vendor/ova/diacritic-annotator.css
new file mode 100644
index 000000000000..c233ca6cb943
--- /dev/null
+++ b/common/static/css/vendor/ova/diacritic-annotator.css
@@ -0,0 +1,6 @@
+.mark{
+ width:10px;
+ height:10px;
+ position:absolute;
+ background-size:contain;
+}
\ No newline at end of file
diff --git a/common/static/css/vendor/ova/richText-annotator.css b/common/static/css/vendor/ova/richText-annotator.css
index 395cfc5f173c..1e66d4ff061f 100644
--- a/common/static/css/vendor/ova/richText-annotator.css
+++ b/common/static/css/vendor/ova/richText-annotator.css
@@ -20,6 +20,18 @@
z-index:3000000000!important; /*To fix full-screen problems*/
}
+.mce-container-body {
+ min-width:400px;
+}
+
+iframe[id^="annotator-field"] {
+ width: inherit;
+ min-width: 400px;
+}
+
+div.mce-tinymce.mce-container.mce-panel {
+ min-width: 400px;
+}
/* Some change in the design of Annotator */
.annotator-editor .annotator-widget{
diff --git a/common/static/css/vendor/ova/tags-annotator.css b/common/static/css/vendor/ova/tags-annotator.css
index e1e58286286a..054e797e967e 100644
--- a/common/static/css/vendor/ova/tags-annotator.css
+++ b/common/static/css/vendor/ova/tags-annotator.css
@@ -43,7 +43,7 @@ li.token-input-input-token {
div.token-input-dropdown {
position: absolute;
- width: 120px;
+ width: 400px;
background-color: #fff;
overflow: hidden;
border-left: 1px solid #ccc;
diff --git a/common/static/js/vendor/ova/catch/js/catch.js b/common/static/js/vendor/ova/catch/js/catch.js
index 113d7d95c818..83ef8a51ecfc 100644
--- a/common/static/js/vendor/ova/catch/js/catch.js
+++ b/common/static/js/vendor/ova/catch/js/catch.js
@@ -568,7 +568,6 @@ CatchAnnotation.prototype = {
var moreBut = this.element.find('.annotationListButtons .moreButtonCatch');
moreBut.html('More');
- setTimeout();
},
//
@@ -1095,11 +1094,10 @@ CatchAnnotation.prototype = {
annotation = item.data('annotation');
var authorized = permissions.options.userAuthorize('delete', annotation,permissions.user);
if(authorized){
- //annotator.deleteAnnotation(annotation);
if(confirm('Would you like to delete this reply?')){
annotator.plugins['Store']._apiRequest('destroy', annotation, function(){});
item.remove();
- }
+ }
}
}
}
diff --git a/common/static/js/vendor/ova/diacritic-annotator.js b/common/static/js/vendor/ova/diacritic-annotator.js
new file mode 100644
index 000000000000..5f29c2dbd842
--- /dev/null
+++ b/common/static/js/vendor/ova/diacritic-annotator.js
@@ -0,0 +1,203 @@
+/*
+ Diacritic Annotator Plugin v1.0 (https://github.com/lduarte1991/diacritic-annotator)
+ Copyright (C) 2014 Luis F Duarte
+ License: https://github.com/lduarte1991/diacritic-annotator/blob/master/LICENSE.rst
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+var _ref,
+ __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
+ __hasProp = {}.hasOwnProperty,
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
+
+Annotator.Plugin.Diacritics = (function(_super) {
+ __extends(Diacritics, _super);
+
+ //Options will include diacritic name, picture used, baseline
+ Diacritics.prototype.options = null;
+ Diacritics.prototype.diacriticmarks = null;
+
+ //initiate diacritics elements
+ function Diacritics(element,options) {
+ this.pluginSubmit = __bind(this.pluginSubmit, this);
+ this.updateDiacritics = __bind(this.updateDiacritics, this);
+ this.updateViewer = __bind(this.updateViewer, this);
+ this.getDiacritics = __bind(this.getDiacritics, this);
+ this.getPos = __bind(this.getPos, this);
+ this.putMarkatLocation = __bind(this.putMarkatLocation, this);
+ this.updateEditorForDiacritics = __bind(this.updateEditorForDiacritics, this);
+
+ this.options = options;
+ this.diacriticmarks = this.getDiacritics();
+ _ref = Diacritics.__super__.constructor.apply(this, arguments);
+ return _ref;
+ }
+
+ //example variables to be used to receive input in the annotator view
+ Diacritics.prototype.field = null;
+ Diacritics.prototype.input = null;
+
+ //this function will initialize the plug in
+ Diacritics.prototype.pluginInit = function() {
+ console.log("Diacritics-pluginInit");
+
+ //Check that annotator is working
+ if (!Annotator.supported()) {
+ return;
+ }
+ var di = this.diacriticmarks;
+
+ //-- Editor
+ var self = this;
+ if(di != 'undefined'){
+ $.each(di,function(item){
+ self.field = self.annotator.editor.addField({
+ type: 'checkbox', //options (textarea,input,select,checkbox)
+ label: Annotator._t(item),
+ submit: self.pluginSubmit,
+ });
+ });
+
+ //-- Viewer
+ var newview = this.annotator.viewer.addField({
+ load: this.updateViewer,
+ });
+
+ this.annotator.subscribe('annotationsLoaded', this.updateDiacritics);
+ this.annotator.subscribe('annotationUploaded', this.updateDiacritics);
+ this.annotator.subscribe('annotationDeleted', this.updateDiacritics);
+ this.annotator.subscribe('annotationEditorShown', this.updateEditorForDiacritics, this.field);
+
+ var self = this;
+ $(window).resize(function() {
+ self.updateDiacritics();
+ });
+ }
+
+ return this.input = $(this.field).find(':input');
+ };
+
+ //The following function is run when a person hits submit.
+ Diacritics.prototype.pluginSubmit = function(field, annotation) {
+ var checkedItems = $(this.field).find(':input');
+ var self = this;
+ $.each(checkedItems, function(item){
+ if(typeof annotation.tags != 'undefined'){
+ var index = $.inArray(checkedItems[item].placeholder, annotation.tags);
+ if(index != -1){
+ annotation.tags.splice(index, 1);
+ if (typeof $($('.annotator-wrapper')[0]).find('div.'+annotation.id)[0] != 'undefined'){
+ $($('.annotator-wrapper')[0]).find('div.'+annotation.id)[0].remove();
+ } else {
+ $($('.annotator-wrapper')[0]).find('div.undefined')[0].remove();
+ }
+ }
+
+ if(checkedItems[item].checked == true){
+ annotation.tags.unshift(checkedItems[item].placeholder);
+ self.putMarkatLocation(annotation, checkedItems[item].placeholder);
+ }
+ } else {
+ if(checkedItems[item].checked == true){
+ annotation['tags'] = [checkedItems[item].placeholder];
+ self.putMarkatLocation(annotation, checkedItems[item].placeholder);
+ }
+ }
+ });
+
+ }
+
+ Diacritics.prototype.putMarkatLocation = function (annotation, mark){
+ var loc = this.getPos(annotation.highlights[0]);
+ var alignment = this.diacriticmarks[mark][1];
+ var imgurl = this.diacriticmarks[mark][0];
+
+ var newdiv = document.createElement('div');
+ var className = 'mark ' + annotation.id;
+ newdiv.setAttribute('class',className);
+ if(alignment == 'top'){
+ $(newdiv).css('top',""+(loc.y-5)+"px");
+ } else if(alignment == 'bottom'){
+ $(newdiv).css('top',""+(loc.y+loc.height-5)+"px");
+ } else{
+ $(newdiv).css('top',""+loc.y+"px");
+ }
+ $(newdiv).css('left',""+(loc.x+(loc.width/2.0)-10)+"px");
+ $(newdiv).css('background-image', 'url('+imgurl+')');
+ $('.annotator-wrapper')[0].appendChild(newdiv);
+ }
+
+ Diacritics.prototype.getDiacritics = function(){
+ if(typeof this.options.diacritics != 'undefined'){
+ var self = this;
+ var final = new Object(), prelim = this.options.diacritics.split(",");
+ prelim.forEach(function(item){
+ var temp = item.split(";");
+ if (temp.length <3) {return undefined;}
+ final[temp[0]] = [temp[1],temp[2]];
+ });
+ return final;
+ }
+ console.log("Was undefined");
+ return undefined;
+ }
+
+ Diacritics.prototype.getPos = function(el) {
+ var off = $(el).offset();
+ return {x: off.left-$($('.annotator-wrapper')[0]).offset().left, y: off.top-$($('.annotator-wrapper')[0]).offset().top, width:$(el).width(), height:$(el).height()};
+ }
+
+ Diacritics.prototype.updateDiacritics = function(){
+ $('.mark').remove();
+ var annotations = this.annotator.plugins['Store'].annotations;
+ var self = this;
+ annotations.forEach(function(ann){
+ $.each(self.diacriticmarks, function(item){
+ if($.inArray(item, ann.tags) != -1){
+ self.putMarkatLocation(ann, item);
+ }
+ });
+ });
+ }
+
+ Diacritics.prototype.updateViewer = function(field,annotation){
+ $(field).remove();
+ }
+
+ Diacritics.prototype.updateEditorForDiacritics = function(field, annotation){
+ if (typeof annotation.tags == 'undefined'){
+ return;
+ }
+ var self = this;
+
+ var inputItems = $(this.field).find(':input');
+ var dictOfItems = {}
+ $.each(inputItems, function(item){
+ inputItems[item].checked = false;
+ dictOfItems[inputItems[item].placeholder] = inputItems[item];
+ });
+ annotation.tags.forEach(function(tag){
+ if(typeof self.diacriticmarks[tag] != 'undefined'){
+ dictOfItems[tag].checked = true;
+ }
+ });
+ }
+
+
+
+ return Diacritics;
+
+})(Annotator.Plugin);
\ No newline at end of file
diff --git a/common/static/js/vendor/ova/flagging-annotator.js b/common/static/js/vendor/ova/flagging-annotator.js
index ac24ffff240d..8aa792334ffe 100644
--- a/common/static/js/vendor/ova/flagging-annotator.js
+++ b/common/static/js/vendor/ova/flagging-annotator.js
@@ -1,4 +1,3 @@
-// Generated by CoffeeScript 1.6.3
var _ref,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
@@ -101,4 +100,4 @@ Annotator.Plugin.Flagging = (function(_super) {
return Flagging;
-})(Annotator.Plugin);
\ No newline at end of file
+})(Annotator.Plugin);
diff --git a/common/static/js/vendor/ova/ova.js b/common/static/js/vendor/ova/ova.js
index 059af1322f6e..a8bc9c50441a 100644
--- a/common/static/js/vendor/ova/ova.js
+++ b/common/static/js/vendor/ova/ova.js
@@ -1,6 +1,6 @@
/*
Open Video Annotation v1.0 (http://openvideoannotation.org/)
-Copyright (C) 2014 CHS (Harvard University), Daniel Cebri�n Robles and Phil Desenne
+Copyright (C) 2014 CHS (Harvard University), Daniel Cebrian Robles and Phil Desenne
License: https://github.com/CtrHellenicStudies/OpenVideoAnnotation/blob/master/License.rst
This program is free software; you can redistribute it and/or
@@ -2389,6 +2389,8 @@ OpenVideoAnnotation.Annotator = function (element, options) {
if (typeof options.optionsAnnotator.highlightTags!='undefined')
this.annotator.addPlugin("HighlightTags", options.optionsAnnotator.highlightTags);
+ if (typeof options.optionsAnnotator.diacriticMarks != 'undefined')
+ this.annotator.addPlugin("Diacritics", options.optionsAnnotator.diacriticMarks);
/*
this.annotator.addPlugin("Filter", {
diff --git a/common/static/js/vendor/ova/rangeslider.js b/common/static/js/vendor/ova/rangeslider.js
index 51e054fcbad7..e0250b936a80 100644
--- a/common/static/js/vendor/ova/rangeslider.js
+++ b/common/static/js/vendor/ova/rangeslider.js
@@ -1,6 +1,6 @@
/*
RangeSlider v1.0 (https://github.com/danielcebrian/rangeslider-videojs)
-Copyright (C) 2014 Daniel Cebri�n Robles
+Copyright (C) 2014 Daniel Cebrian Robles
License: https://github.com/danielcebrian/rangeslider-videojs/blob/master/License.rst
This program is free software; you can redistribute it and/or
diff --git a/common/static/js/vendor/ova/reply-annotator.js b/common/static/js/vendor/ova/reply-annotator.js
index f88efae2daf0..5fe8a37e30bd 100644
--- a/common/static/js/vendor/ova/reply-annotator.js
+++ b/common/static/js/vendor/ova/reply-annotator.js
@@ -1,6 +1,6 @@
/*
Reply Annotator Plugin v1.0 (https://github.com/danielcebrian/reply-annotator)
- Copyright (C) 2014 Daniel Cebri�n Robles
+ Copyright (C) 2014 Daniel Cebrian Robles
License: https://github.com/danielcebrian/reply-annotator/blob/master/License.rst
This program is free software; you can redistribute it and/or
@@ -17,7 +17,6 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// Generated by CoffeeScript 1.6.3
var _ref,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
diff --git a/common/static/js/vendor/ova/richText-annotator.js b/common/static/js/vendor/ova/richText-annotator.js
index 828d7ef8ef89..426ece43fc7f 100644
--- a/common/static/js/vendor/ova/richText-annotator.js
+++ b/common/static/js/vendor/ova/richText-annotator.js
@@ -1,6 +1,6 @@
/*
Rich Text Annotator Plugin v1.0 (https://github.com/danielcebrian/richText-annotator)
-Copyright (C) 2014 Daniel Cebri�n Robles
+Copyright (C) 2014 Daniel Cebrian Robles
License: https://github.com/danielcebrian/richText-annotator/blob/master/License.rst
This program is free software; you can redistribute it and/or
@@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// Generated by CoffeeScript 1.6.3
var _ref,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
@@ -36,6 +35,7 @@ Annotator.Plugin.RichText = (function(_super) {
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 ",
+ resize: "both",
}
};
@@ -125,7 +125,7 @@ Annotator.Plugin.RichText = (function(_super) {
RichText.prototype.updateEditor = function(field, annotation) {
var text = typeof annotation.text!='undefined'?annotation.text:'';
- tinymce.activeEditor.setContent(text);
+ tinymce.activeEditor.setContent(text);
$(field).remove(); //this is the auto create field by annotator and it is not necessary
}
diff --git a/common/static/js/vendor/ova/share-annotator.js b/common/static/js/vendor/ova/share-annotator.js
index 013933703f36..b6648e32456d 100644
--- a/common/static/js/vendor/ova/share-annotator.js
+++ b/common/static/js/vendor/ova/share-annotator.js
@@ -1,6 +1,6 @@
/*
Share Annotation Plugin v1.0 (https://github.com/danielcebrian/share-annotator)
-Copyright (C) 2014 Daniel Cebri�n Robles
+Copyright (C) 2014 Daniel Cebrian Robles
License: https://github.com/danielcebrian/share-annotator/blob/master/License.rst
This program is free software; you can redistribute it and/or
@@ -17,7 +17,6 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// Generated by CoffeeScript 1.6.3
var _ref,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
diff --git a/common/static/js/vendor/ova/tags-annotator.js b/common/static/js/vendor/ova/tags-annotator.js
index 36a28fa9de82..69cf5e3df7e2 100644
--- a/common/static/js/vendor/ova/tags-annotator.js
+++ b/common/static/js/vendor/ova/tags-annotator.js
@@ -1,3 +1,27 @@
+/*
+HighlightTags Annotator Plugin v1.0 (https://github.com/lduarte1991/tags-annotator)
+ Copyright (C) 2014 Luis F Duarte
+ License: https://github.com/lduarte1991/tags-annotator/blob/master/LICENSE.rst
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+/*===============================================================================
+ ===============================================================================
+ ===============================================================================
+ ===============================================================================
+ ==============================================================================*/
/*
* jQuery Plugin: Tokenizing Autocomplete Text Entry
* Version 1.6.0
@@ -22,7 +46,7 @@ var DEFAULT_SETTINGS = {
// Display settings
hintText: "Type in a search term",
- noResultsText: "No results",
+ noResultsText: "Not Found. Hit ENTER to add a personal tag.",
searchingText: "Searching...",
deleteText: "×",
animateDropdown: true,
@@ -271,6 +295,9 @@ $.TokenList = function (input, url_or_data, settings) {
add_token($(selected_dropdown_item).data("tokeninput"));
hidden_input.change();
return false;
+ } else {
+ add_token({id:$(this).val(), name:$(this).val()});
+ hidden_input.change();
}
break;
@@ -859,7 +886,6 @@ $.TokenList.Cache = function (options) {
};
}(jQuery));
-// Generated by CoffeeScript 1.6.3
var _ref,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
__hasProp = {}.hasOwnProperty,
@@ -886,7 +912,7 @@ Annotator.Plugin.HighlightTags = (function(_super) {
HighlightTags.prototype.field = null;
HighlightTags.prototype.input = null;
HighlightTags.prototype.colors = null;
- HighlightTags.prototype.isFirstTime = true;
+ HighlightTags.prototype.isFirstTime = true;
//this function will initialize the plug in. Create your fields here in the editor and viewer.
HighlightTags.prototype.pluginInit = function() {
diff --git a/common/test/data/tilde/about/index.html~ b/common/test/data/tilde/about/index.html~
deleted file mode 100644
index f24fc29aa68b..000000000000
--- a/common/test/data/tilde/about/index.html~
+++ /dev/null
@@ -1 +0,0 @@
-RED
diff --git a/common/test/data/tilde/static/example.txt~ b/common/test/data/tilde/static/example.txt~
deleted file mode 100644
index f24fc29aa68b..000000000000
--- a/common/test/data/tilde/static/example.txt~
+++ /dev/null
@@ -1 +0,0 @@
-RED
diff --git a/lms/djangoapps/notes/views.py b/lms/djangoapps/notes/views.py
index b6670a7e0923..de459cd3512a 100644
--- a/lms/djangoapps/notes/views.py
+++ b/lms/djangoapps/notes/views.py
@@ -4,7 +4,7 @@
from courseware.courses import get_course_with_access
from notes.models import Note
from notes.utils import notes_enabled_for_course
-
+from xmodule.annotator_token import retrieve_token
@login_required
def notes(request, course_id):
@@ -22,7 +22,7 @@ def notes(request, course_id):
'course': course,
'notes': notes,
'student': student,
- 'storage': storage
+ 'storage': storage,
+ 'token': retrieve_token(student.email, course.annotation_token_secret),
}
-
return render_to_response('notes.html', context)
diff --git a/lms/envs/common.py b/lms/envs/common.py
index ebc339db144c..3c3ec5d0b074 100644
--- a/lms/envs/common.py
+++ b/lms/envs/common.py
@@ -807,6 +807,7 @@
'js/vendor/ova/tags-annotator.js',
'js/vendor/ova/flagging-annotator.js',
'js/vendor/ova/jquery-Watch.js',
+ 'js/vendor/ova/diacritic-annotator.js',
'js/vendor/ova/ova.js',
'js/vendor/ova/catch/js/catch.js',
'js/vendor/ova/catch/js/handlebars-1.1.2.js',
@@ -833,6 +834,7 @@
'css/vendor/ova/share-annotator.css',
'css/vendor/ova/richText-annotator.css',
'css/vendor/ova/tags-annotator.css',
+ 'css/vendor/ova/diacritic-annotator.css',
'css/vendor/ova/flagging-annotator.css',
'css/vendor/ova/ova.css',
'js/vendor/ova/catch/css/main.css'
diff --git a/lms/templates/notes.html b/lms/templates/notes.html
index d8967255814f..e44a78b08ec0 100644
--- a/lms/templates/notes.html
+++ b/lms/templates/notes.html
@@ -68,10 +68,8 @@
${_('My Notes')}
//Grab uri of the course
var parts = window.location.href.split("/"),
- uri = '',
- courseid;
+ uri = '';
for (var index = 0; index <= 6; index += 1) uri += parts[index]+"/"; //Get the unit url
- courseid = parts[4] + "/" + parts[5] + "/" + parts[6];
var pagination = 100,
is_staff = false,
options = {
@@ -130,7 +128,7 @@