Commit fecb2e16 by lduarte1991 Committed by David Baumgold

Annotation Tool: PR #3969

Text Annotation Tool: Added Instructor Filter and Clear Search

Video Annotation Tool: Added Instructor Filter

Image Annotation Tool: Added Instructor Filter

Image Annotation Tool: Annotation Mode for AB Testing

Annotation Tools PR Fixes

- forgot to overwrite the previous line

Annotation Tools: PR Fixes

- Fixed camel case for variable name
- Fixed indentation in imageannotation.html
- Changed all mentions of Instructor Username to Email
- Turned annotation_mode into phrase
- Fixed indentation in imageannotation.html
- Added comments in imageannotation.html

- Changing annotation_mode in OSDA

Annotator Tools: OpaqueKeys update for Notes

Annotation Tools: Added CommonAnnotatorMixin

Annotator Tool: Fixed delete bug and factored out settings

Removed print context

Conflicts:
	common/lib/xmodule/xmodule/imageannotation_module.py
	common/lib/xmodule/xmodule/textannotation_module.py
	common/lib/xmodule/xmodule/videoannotation_module.py
	lms/djangoapps/notes/views.py
parent 643a7bfa
......@@ -6,7 +6,10 @@ from lxml import etree
from urlparse import urlparse
from os.path import splitext, basename
from HTMLParser import HTMLParser
from xblock.core import Scope, String
# Make '_' a no-op so we can scrape strings
_ = lambda text: text
def get_instructions(xmltree):
""" Removes <instructions> from the xmltree and returns them as a string, otherwise None. """
......@@ -53,3 +56,37 @@ def html_to_text(html):
htmlstripper = MLStripper()
htmlstripper.feed(html)
return htmlstripper.get_data()
class CommonAnnotatorMixin(object):
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",
)
......@@ -7,7 +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, html_to_text
from xmodule.annotator_mixin import CommonAnnotatorMixin, get_instructions, html_to_text
from xmodule.annotator_token import retrieve_token
from xblock.fragment import Fragment
......@@ -62,7 +62,7 @@ class AnnotatableFields(object):
)
class ImageAnnotationModule(AnnotatableFields, XModule):
class ImageAnnotationModule(AnnotatableFields, CommonAnnotatorMixin, XModule):
'''Image Annotation Module'''
js = {
'coffee': [
......@@ -97,12 +97,14 @@ class ImageAnnotationModule(AnnotatableFields, XModule):
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,
}
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")
......
......@@ -6,7 +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_mixin import CommonAnnotatorMixin, get_instructions
from xmodule.annotator_token import retrieve_token
from xblock.fragment import Fragment
import textwrap
......@@ -47,11 +47,17 @@ class AnnotatableFields(object):
scope=Scope.settings,
default='None',
)
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"))
class TextAnnotationModule(AnnotatableFields, XModule):
class TextAnnotationModule(AnnotatableFields, CommonAnnotatorMixin, XModule):
''' Text Annotation Module '''
js = {'coffee': [],
'js': []}
......@@ -82,8 +88,12 @@ 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,
}
fragment = Fragment(self.system.render_template('textannotation.html', context))
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js")
......
......@@ -7,7 +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_mixin import CommonAnnotatorMixin, get_instructions, get_extension
from xmodule.annotator_token import retrieve_token
from xblock.fragment import Fragment
......@@ -32,14 +32,24 @@ 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=""
)
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):
class VideoAnnotationModule(AnnotatableFields, CommonAnnotatorMixin, XModule):
'''Video Annotation Module'''
js = {
'coffee': [
......@@ -85,8 +95,11 @@ 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,
}
fragment = Fragment(self.system.render_template('videoannotation.html', context))
fragment.add_javascript_url("/static/js/vendor/tinymce/js/tinymce/tinymce.full.min.js")
......
......@@ -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{
......
......@@ -42,29 +42,29 @@ annotationList:
'</div>'+
'<div class="annotatedBy field">'+
'User'+
gettext('User')+
'</div>'+
'<div class="body field">'+
'Annotation'+
gettext('Annotation')+
'</div>'+
'{{#if videoFormat}}'+
'<div class="start field">'+
'Start'+
gettext('Start')+
'</div>'+
'<div class="end field">'+
'End'+
gettext('End')+
'</div>'+
'{{/if}}'+
'<div class="totalreplies field">'+
'#Replies'+
gettext('#Replies')+
'</div>'+
'<div class="annotatedAt field">'+
'Date posted'+
gettext('Date posted')+
'</div>'+
'</div>'+
'</div>'+
......@@ -73,30 +73,41 @@ annotationList:
'{{/each}}'+
'</div>'+
'<div class="annotationListButtons">'+
'<div class="moreButtonCatch">More</div>'+
'<div class="moreButtonCatch">'+gettext('More')+'</div>'+
'</div>',
//Main->PublicPrivateInstructor
annotationPublicPrivateInstructor:
'<div class="selectors"><div class="PublicPrivate myNotes active">'+gettext('My Notes')+'<span class="action">myNotes</span></div>'+
'<div class="PublicPrivate instructor"> '+gettext('Instructor')+'<span class="action">instructor</span></div>'+
'<div class="PublicPrivate public"> '+gettext('Public')+'<span class="action">public</span></div></div>'+
'<div class="searchbox"><div class="searchinst">'+gettext('Search')+'</div><select class="dropdown-list">'+
'<option>'+gettext('Users')+'</option>'+
'<option>'+gettext('Tags')+'</option>'+
'<option>'+gettext('Annotation Text')+'</option>'+
'</select><input type="text" name="search"/><div class="search-icon" alt="Run search."></div><div class="clear-search-icon" alt="Clear search.">'+gettext('Clear')+'</div></div>',
//Main->PublicPrivate
annotationPublicPrivate:
'<div class="selectors"><div class="PublicPrivate myNotes active">My Notes<span class="action">myNotes</span></div>'+
'<div class="PublicPrivate public"> Public<span class="action">public</span></div></div>'+
'<div class="searchbox"><div class="searchinst">Search</div><select class="dropdown-list">'+
'<option>Users</option>'+
'<option>Tags</option>'+
'<option>Annotation Text</option>'+
'</select><input type="text" name="search"/><div class="search-icon" alt="Run search."></div></div>',
'<div class="selectors"><div class="PublicPrivate myNotes active">'+gettext('My Notes')+'<span class="action">myNotes</span></div>'+
'<div class="PublicPrivate public"> '+gettext('Public')+'<span class="action">public</span></div></div>'+
'<div class="searchbox"><div class="searchinst">'+gettext('Search')+'</div><select class="dropdown-list">'+
'<option>'+gettext('Users')+'</option>'+
'<option>'+gettext('Tags')+'</option>'+
'<option>'+gettext('Annotation Text')+'</option>'+
'</select><input type="text" name="search"/><div class="search-icon" alt="Run search."></div><div class="clear-search-icon" alt="Clear search.">'+gettext('Clear')+'</div></div>',
//Main->MediaSelector
annotationMediaSelector:
'<ul class="ui-tabs-nav">'+
'<li class="ui-state-default" media="text">'+
'Text'+
gettext('Text')+
'</li>'+
'<li class="ui-state-default" media="video">'+
'Video'+
gettext('Video')+
'</li>'+
'li class="ui-state-default" media="image">'+
'Image'+
'<li class="ui-state-default" media="image">'+
gettext('Image')+
'</li>'+
'</ul>',
......@@ -126,7 +137,7 @@ annotationReply:
'<div class="map"></div>'+
'</div>'+
'{{/if}}'+
'<div class="deleteReply">Delete</div>'+
'<div class="deleteReply">'+gettext('Delete')+'</div>'+
'</p>'+
'<p>'+
'{{#if this.text}}'+
......@@ -233,13 +244,13 @@ annotationDetail:
'</div>'+
'<div class="controlReplies">'+
'<div class="newReply" style="text-decoration:underline">Reply</div>&nbsp;'+
'<div class="newReply" style="text-decoration:underline">'+gettext('Reply')+'</div>&nbsp;'+
'<div class="hideReplies" style="text-decoration:underline;display:{{#if hasReplies}}block{{else}}none{{/if}}">Show Replies</div>&nbsp;'+
'{{#if authToEditButton}}'+
'<div class="editAnnotation" style="text-decoration:underline">Edit</div>'+
'<div class="editAnnotation" style="text-decoration:underline">'+gettext('Edit')+'</div>'+
'{{/if}}'+
'{{#if authToDeleteButton}}'+
'<div class="deleteAnnotation" style="text-decoration:underline">Delete</div>'+
'<div class="deleteAnnotation" style="text-decoration:underline">'+gettext('Delete')+'</div>'+
'{{/if}}'+
'</div>'+
......@@ -248,7 +259,7 @@ annotationDetail:
'{{#if tags}}'+
'<div class="tags">'+
'<h3>Tags:</h3>'+
'<h3>'+gettext('Tags:')+'</h3>'+
'{{#each tags}}'+
'<div class="tag">'+
'{{this}}'+
......@@ -290,14 +301,14 @@ CatchAnnotation = function (element, options) {
//Reset element an create a new element div
element.html('<div id="mainCatch" class="annotationListContainer"></div>');
this.current_tab = this.options.default_tab;
//INIT
var self = this;
$( document ).ready(function() {
self.init();
self.refreshCatch(true);
var moreBut = self.element.find('.annotationListButtons .moreButtonCatch');
moreBut.hide();
var moreBut = self.element.find('.annotationListButtons .moreButtonCatch');
moreBut.hide();
});
return this;
......@@ -310,6 +321,7 @@ CatchAnnotation.prototype = {
this.TEMPLATENAMES = [
"annotationList", //Main
"annotationPublicPrivate", //Main->PublicPrivate
"annotationPublicPrivateInstructor", //Main->PublicPrivateInstructor
"annotationMediaSelector", //Main->MediaSelector
"annotationItem", //Main->ContainerRow
"annotationReply",//Main->ContainerRow->Reply
......@@ -317,8 +329,8 @@ CatchAnnotation.prototype = {
"annotationDetail",//Main->ContainerRow->DetailRow
];
//annotator
var wrapper = $('.annotator-wrapper').parent()[0],
annotator = $.data(wrapper, 'annotator');
var wrapper = $('.annotator-wrapper').parent()[0];
var annotator = $.data(wrapper, 'annotator');
this.annotator = annotator;
//Subscribe to annotator
......@@ -336,10 +348,22 @@ CatchAnnotation.prototype = {
this.HTMLTEMPLATES = CatchSources.HTMLTEMPLATES(this.options.imageUrlRoot);
this.TEMPLATES = {};
this._compileTemplates();
// the default annotations are the user's personal ones instead of instructor
// if the default tab is instructor, we must refresh the catch to pull the ones
// under the instructor's email. passing empty strings as arguments will default
// to pulling the annotations for the email within this.options.userId.
if(this.options.default_tab.toLowerCase() == 'instructor'){
this.options.userId = this.options.instructor_email;
this._refresh('','');
}
},
//
// GLOBAL UTILITIES
//
getTemplate: function(templateName){
return this.TEMPLATES[templateName]() || '';
},
refreshCatch: function(newInstance) {
var mediaType = this.options.media || 'text',
annotationItems = [],
......@@ -380,10 +404,16 @@ CatchAnnotation.prototype = {
if (newInstance){
var videoFormat = (mediaType === "video") ? true:false;
var publicPrivateTemplate = '';
if (self.options.showPublicPrivate) {
var templateName = this.options.instructor_email ?
"annotationPublicPrivateInstructor" :
"annotationPublicPrivate";
}
el.html(self.TEMPLATES.annotationList({
annotationItems: annotationItems,
videoFormat: videoFormat,
PublicPrivate: self.options.showPublicPrivate?self.TEMPLATES.annotationPublicPrivate():'',
PublicPrivate: this.getTemplate(templateName),
MediaSelector: self.options.showMediaSelector?self.TEMPLATES.annotationMediaSelector():'',
}));
}else{
......@@ -402,10 +432,21 @@ CatchAnnotation.prototype = {
//Set PublicPrivate
var PublicPrivateButtons = el.find('.annotationListButtons .PublicPrivate').removeClass('active'); //reset
for (var index=0;index<PublicPrivateButtons.length;index++) {
var span = $(PublicPrivateButtons[index]).find('span'),
isUser = (typeof self.options.userId!='undefined' && self.options.userId!='' && self.options.userId!=null);
if (isUser && span.html()=="myNotes") $(PublicPrivateButtons[index]).addClass('active');
else if (!isUser && span.html()=="public") $(PublicPrivateButtons[index]).addClass('active');
var span = $(PublicPrivateButtons[index]).find('span');
if (span.html().toLowerCase()==self.current_tab.toLowerCase()) {
switch (self.current_tab.toLowerCase()){
case 'public':
self.options.userId = '';
break;
case 'instructor':
self.options.userId = this.options.instructor_email;
break;
default:
self.options.userId = this.annotator.plugins.Permissions.user.id;
break;
}
$(PublicPrivateButtons[index]).addClass('active');
}
}
//reset all old events
......@@ -423,6 +464,7 @@ CatchAnnotation.prototype = {
onControlRepliesClick = this.__bind(this._onControlRepliesClick, this),
onMoreButtonClick = this.__bind(this._onMoreButtonClick, this),
onSearchButtonClick = this.__bind(this._onSearchButtonClick, this),
onClearSearchButtonClick = this.__bind(this._onClearSearchButtonClick, this),
onDeleteReplyButtonClick = this.__bind(this._onDeleteReplyButtonClick, this),
onZoomToImageBoundsButtonClick = this.__bind(this._onZoomToImageBoundsButtonClick, this);
......@@ -463,6 +505,9 @@ CatchAnnotation.prototype = {
//Search Button
el.on("click",".searchbox .search-icon", onSearchButtonClick);
//Clear Search Button
el.on("click",".searchbox .clear-search-icon", onClearSearchButtonClick);
//Delete Reply Button
el.on("click", ".replies .replyItem .deleteReply", onDeleteReplyButtonClick);
......@@ -471,16 +516,16 @@ CatchAnnotation.prototype = {
changeMedia: function(media) {
var media = media || 'text';
this.options.media = media;
this._refresh();
this._refresh();
this.refreshCatch(true);
this.checkTotAnnotations();
this.checkTotAnnotations();
},
changeUserId: function(userId) {
var userId = userId || '';
this.options.userId = userId;
this._refresh();
this.refreshCatch(true);
this.checkTotAnnotations();
this.checkTotAnnotations();
},
loadAnnotations: function() {
var annotator = this.annotator,
......@@ -568,21 +613,26 @@ CatchAnnotation.prototype = {
var annotations = annotator.plugins['Store'].annotations,
tot = typeof annotations !='undefined'?annotations.length:0,
attempts = 0; // max 100
if(annotation.media == "image"){
self.refreshCatch(true);
self.checkTotAnnotations();
} else {
//This is to watch the annotations object, to see when is deleted the annotation
var ischanged = function(){
var new_tot = annotator.plugins['Store'].annotations.length;
if (attempts<100)
setTimeout(function(){
if (new_tot != tot){
self.refreshCatch(true);
self.checkTotAnnotations();
}else{
attempts++;
ischanged();
}
},100); //wait for the change in the annotations
};
ischanged();
var ischanged = function(){
var new_tot = annotator.plugins['Store'].annotations.length;
if (attempts<100)
setTimeout(function(){
if (new_tot != tot){
self.refreshCatch(true);
self.checkTotAnnotations();
}else{
attempts++;
ischanged();
}
},100); //wait for the change in the annotations
};
ischanged();
}
});
annotator.subscribe("annotationCreated", function (annotation){
var attempts = 0; // max 100
......@@ -770,6 +820,10 @@ CatchAnnotation.prototype = {
var allannotations = this.annotator.plugins['Store'].annotations,
osda = this.annotator.osda;
if(this.options.externalLink){
uri += (uri.indexOf('?') >= 0)?'&osdaId='+osdaId:'?osdaId='+osdaId;
location.href = uri;
}
for(var item in allannotations){
var an = allannotations[item];
if (typeof an.id!='undefined' && an.id == osdaId){//this is the annotation
......@@ -1011,8 +1065,18 @@ CatchAnnotation.prototype = {
userId = '';
//Get userI
userId = (action.html()=="myNotes")? this.annotator.plugins.Permissions.user.id : '';
switch (action.html()){
case 'public':
userId = '';
break;
case 'instructor':
userId = this.options.instructor_email;
break;
default:
userId = this.annotator.plugins.Permissions.user.id;
break;
}
this.current_tab = action.html();
//Change userid and refresh
this.changeUserId(userId);
},
......@@ -1069,6 +1133,9 @@ CatchAnnotation.prototype = {
this._refresh(searchtype,searchInput);
},
_onClearSearchButtonClick: function(evt){
this._refresh('','');
},
_clearAnnotator: function(){
var annotator = this.annotator,
store = annotator.plugins.Store,
......
......@@ -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)
......@@ -9,70 +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)}
</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:{
......@@ -103,7 +102,7 @@
if (annotation.permissions) {
tokens = annotation.permissions[action] || [];
if (is_staff){
return true;
return true;
}
if (tokens.length === 0) {
return true;
......@@ -153,7 +152,7 @@
offset:0,
uri:uri,
media:'image',
userid:'${user.email}',
userid:'${user.email}',
}
},
highlightTags:{
......@@ -172,6 +171,8 @@
},
optionsOpenSeadragon:{
id: "imageHolder",
annotation_mode: "${annotation_mode}",
flags: is_staff,
prefixUrl: "${settings.STATIC_URL}" + "js/vendor/ova/images/",
${openseadragonjson}
},
......@@ -179,29 +180,43 @@
};
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>
......
......@@ -174,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>
......@@ -175,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