diff --git a/common/lib/xmodule/xmodule/annotator_mixin.py b/common/lib/xmodule/xmodule/annotator_mixin.py new file mode 100644 index 0000000000000000000000000000000000000000..8b12263580add857538431b3da1ec974f2f33fad --- /dev/null +++ b/common/lib/xmodule/xmodule/annotator_mixin.py @@ -0,0 +1,45 @@ +""" +Annotations Tool Mixin +This file contains global variables and functions used in the various Annotation Tools. +""" +from pkg_resources import resource_string +from lxml import etree +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') + if instructions is not None: + instructions.tag = 'div' + xmltree.remove(instructions) + return etree.tostring(instructions, encoding='unicode') + return None + +def get_extension(srcurl): + ''' get the extension of a given url ''' + if 'youtu' in srcurl: + return 'video/youtube' + else: + disassembled = urlparse(srcurl) + file_ext = splitext(basename(disassembled.path))[1] + return 'video/' + file_ext.replace('.', '') + +class MLStripper(HTMLParser): + "helper function for html_to_text below" + def __init__(self): + self.reset() + self.fed = [] + def handle_data(self, d): + self.fed.append(d) + def handle_entityref(self, name): + self.fed.append('&%s;' % name) + def get_data(self): + return ''.join(self.fed) + +def html_to_text(html): + "strips the html tags off of the text to return plaintext" + s = MLStripper() + s.feed(html) + return s.get_data() \ No newline at end of file diff --git a/common/lib/xmodule/xmodule/annotator_token.py b/common/lib/xmodule/xmodule/annotator_token.py index 6fa569597851e4d1d35d3db00539a050c1266b79..129315739c2660563e4231c208d10f98d6034e96 100644 --- a/common/lib/xmodule/xmodule/annotator_token.py +++ b/common/lib/xmodule/xmodule/annotator_token.py @@ -23,8 +23,8 @@ def retrieve_token(userid, secret): 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) + newhour, newmin = divmod((delta.days * 24 * 60 * 60 + delta.seconds + 30) // 60, 60) # pylint: disable=E1103 + newtime = "%s%+02d:%02d" % (dtnow.isoformat(), newhour, newmin) # pylint: disable=E1103 # uses the issued time (UTC plus timezone), the consumer key and the user's email to maintain a # federated system in the annotation backend server custom_data = {"issuedAt": newtime, "consumerKey": secret, "userId": userid, "ttl": 86400} diff --git a/common/lib/xmodule/xmodule/tests/test_annotator_mixin.py b/common/lib/xmodule/xmodule/tests/test_annotator_mixin.py new file mode 100644 index 0000000000000000000000000000000000000000..09d216b709ad0578559245b0a52533be870f8e48 --- /dev/null +++ b/common/lib/xmodule/xmodule/tests/test_annotator_mixin.py @@ -0,0 +1,52 @@ +""" +This test will run for annotator_mixin.py +""" + +import unittest +from lxml import etree + +from xmodule.annotator_mixin import get_instructions, get_extension, html_to_text + +class HelperFunctionTest(unittest.TestCase): + """ + Tests to ensure that the following helper functions work for the annotation tool + """ + sample_xml = ''' + <annotatable> + <instructions><p>Helper Test Instructions.</p></instructions> + </annotatable> + ''' + sample_sourceurl = "http://video-js.zencoder.com/oceans-clip.mp4" + sample_youtubeurl = "http://www.youtube.com/watch?v=yxLIu-scR9Y" + sample_html = '<p><b>Testing here</b> and not bolded here</p>' + + def test_get_instructions(self): + """ + Function takes in an input of a specific xml string with surrounding instructions tags and returns a valid html string. + """ + xmltree = etree.fromstring(self.sample_xml) + + expected_xml = u"<div><p>Helper Test Instructions.</p></div>" + actual_xml = get_instructions(xmltree) # pylint: disable=W0212 + self.assertIsNotNone(actual_xml) + self.assertEqual(expected_xml.strip(), actual_xml.strip()) + + xmltree = etree.fromstring('<annotatable>foo</annotatable>') + actual = get_instructions(xmltree) # pylint: disable=W0212 + self.assertIsNone(actual) + + def test_get_extension(self): + """ + Tests whether given a url if the video will return a youtube source or extension + """ + expectedyoutube = 'video/youtube' + expectednotyoutube = 'video/mp4' + result1 = get_extension(self.sample_sourceurl) # pylint: disable=W0212 + result2 = get_extension(self.sample_youtubeurl) # pylint: disable=W0212 + self.assertEqual(expectedyoutube, result2) + self.assertEqual(expectednotyoutube, result1) + + def test_html_to_text(self): + expectedText = "Testing here and not bolded here" + result = html_to_text(self.sample_html) + self.assertEqual(expectedText, result) \ No newline at end of file diff --git a/common/lib/xmodule/xmodule/tests/test_annotator_token.py b/common/lib/xmodule/xmodule/tests/test_annotator_token.py index ae06808bba597f5f3a0baab38153bea8e4eae08a..49f376436be6287f91b65ba069c4695dc16d1302 100644 --- a/common/lib/xmodule/xmodule/tests/test_annotator_token.py +++ b/common/lib/xmodule/xmodule/tests/test_annotator_token.py @@ -12,9 +12,9 @@ class TokenRetriever(unittest.TestCase): """ 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. + 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]) \ No newline at end of file + self.assertNotEqual(expected.split('.')[2], response.split('.')[2]) diff --git a/common/lib/xmodule/xmodule/tests/test_videoannotation.py b/common/lib/xmodule/xmodule/tests/test_videoannotation.py index 4a081803aa861b545439945dca5e592a2c96225e..533ea80203d45d16aaf961870ee0080a2ae90a9f 100644 --- a/common/lib/xmodule/xmodule/tests/test_videoannotation.py +++ b/common/lib/xmodule/xmodule/tests/test_videoannotation.py @@ -66,6 +66,6 @@ class VideoAnnotationModuleTestCase(unittest.TestCase): """ Tests to make sure variables passed in truly exist within the html once it is all rendered. """ - context = self.mod.get_html() # pylint: disable=W0212 + 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 2f5f3250a91436c15a426ff928c0a465a6af7181..258f2707874632212432946834ca852ad5708475 100644 --- a/common/lib/xmodule/xmodule/textannotation_module.py +++ b/common/lib/xmodule/xmodule/textannotation_module.py @@ -6,6 +6,7 @@ from pkg_resources import resource_string from xmodule.x_module import XModule 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 import textwrap @@ -70,12 +71,7 @@ class TextAnnotationModule(AnnotatableFields, XModule): def _extract_instructions(self, xmltree): """ Removes <instructions> from the xmltree and returns them as a string, otherwise None. """ - instructions = xmltree.find('instructions') - if instructions is not None: - instructions.tag = 'div' - xmltree.remove(instructions) - return etree.tostring(instructions, encoding='unicode') - return None + return get_instructions(xmltree) def get_html(self): """ Renders parameters to template. """ diff --git a/common/lib/xmodule/xmodule/videoannotation_module.py b/common/lib/xmodule/xmodule/videoannotation_module.py index df6236d006c6f0be1a6d90c6e8f260c9cf970dfd..1431b03b25abd451b808ecdee07a5ec8a3a92c92 100644 --- a/common/lib/xmodule/xmodule/videoannotation_module.py +++ b/common/lib/xmodule/xmodule/videoannotation_module.py @@ -7,6 +7,7 @@ from pkg_resources import resource_string from xmodule.x_module import XModule 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 import textwrap @@ -65,24 +66,11 @@ class VideoAnnotationModule(AnnotatableFields, XModule): def _extract_instructions(self, xmltree): """ Removes <instructions> from the xmltree and returns them as a string, otherwise None. """ - instructions = xmltree.find('instructions') - if instructions is not None: - instructions.tag = 'div' - xmltree.remove(instructions) - return etree.tostring(instructions, encoding='unicode') - return None + return get_instructions(xmltree) def _get_extension(self, srcurl): ''' get the extension of a given url ''' - if 'youtu' in srcurl: - return 'video/youtube' - else: - spliturl = srcurl.split(".") - extensionplus1 = spliturl[len(spliturl) - 1] - spliturl = extensionplus1.split("?") - extensionplus2 = spliturl[0] - spliturl = extensionplus2.split("#") - return 'video/' + spliturl[0] + return get_extension(srcurl) def get_html(self): """ Renders parameters to template. """ diff --git a/common/static/css/vendor/ova/richText-annotator.css b/common/static/css/vendor/ova/richText-annotator.css index 395cfc5f173ceff5a78db001d09de73ae15cab82..4cadb8e0550de8bc3a9b5b78117e1825f5ba2194 100644 --- a/common/static/css/vendor/ova/richText-annotator.css +++ b/common/static/css/vendor/ova/richText-annotator.css @@ -17,9 +17,21 @@ } .annotator-wrapper .mce-container { - z-index:3000000000!important; /*To fix full-screen problems*/ + 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{ @@ -30,4 +42,4 @@ .mce-ico.mce-i-rubric{ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAYAAAA71pVKAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QkBBB07nraNoQAAAhZJREFUKM+NkstrE1EUxr+5c08ykztpJtVoazHBF8FgQQzonyBKEZS6FrQKLl0EXBRT0ZULJSs3oii4TyHgu90IlTaL6qouWlv7Ck1N0BSnmZk714WbPHz07M4534+Pw3eAHdTY8A9+Nd9/bshU1DpnO4HXjh2ZY2J9/OSTxHTrnP8PvJYf+BDQ6qEDaQBB43jrTusUFy4oPjsYWYzF+VS91nxLYfdhKgONaQT3W/KMxr1XY5e+qj86f8zsKYYsZ6AvjWFzA8ORHkAnwN8So7evzL/8pzMAXL/Hq8mMv1up371T7Z+/c3n9cKeuDS6Xy6dN07zLuZ56Onk2Ed2/ANJsnE/PQMpgyffle+kYzwazB1+3waVS6X48Hr9BRPB9H57nYXplFKeSt8D1Hriug9XKF0x+Lmw+ys8m2m42DOOn4zhQSsGyLOi6jqONm9isbmFVFlDbaGKx8QaB1rvdlbNhGLAsC0IIGIYBIQSy2ROQ0oOp7wOPraHXEugRvDtnzjmi0SiICEIIEBGklAB9B6cmbG0AUnrY5m73h+m6DsYYTNMEYwxEBMY0hGNVhHkcZigBO9qHlDHS7cwYg23bAIBQKAQigud7IH0XwtxDoHwEIQ9SLKx0wa7rPiaivYyxESklXNeFBg0mjyNQTQSuATMSm6ipuYt//eVcLhdeXl5+UKlUlur1upqamVAv3j3/VCyOD3VqfwF6uLp3q+vMcgAAAABJRU5ErkJggg=='); background-repeat: no-repeat; -} +} \ 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 ac24ffff240d738bb1d55d3b22432dba55924a76..16b658d6de96e93a9e2efd73025bf79557997464 100644 --- a/common/static/js/vendor/ova/flagging-annotator.js +++ b/common/static/js/vendor/ova/flagging-annotator.js @@ -101,4 +101,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/reply-annotator.js b/common/static/js/vendor/ova/reply-annotator.js index f88efae2daf03cff9a94897f6126bf98e9aa7960..6ced09eb150abc893f6442c46b059868fa94e2e0 100644 --- a/common/static/js/vendor/ova/reply-annotator.js +++ b/common/static/js/vendor/ova/reply-annotator.js @@ -82,6 +82,7 @@ Annotator.Plugin.Reply = (function(_super) { var string; return self; }); + field.remove(); this.annotation = annotation; //Create the actions for the buttons return ret; diff --git a/common/static/js/vendor/ova/tags-annotator.js b/common/static/js/vendor/ova/tags-annotator.js index 36a28fa9de828fff70ccf9565226610260d463f8..dde5529e6fafb62c4f794dab63ee6259fe880b80 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, @@ -39,7 +63,7 @@ var DEFAULT_SETTINGS = { prePopulate: null, processPrePopulate: false, - // Manipulation settings + // Manipulation settings idPrefix: "token-input-", // Formatters @@ -271,7 +295,10 @@ $.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; case KEY.ESCAPE: @@ -886,7 +913,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() {