Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
5709ff34
Commit
5709ff34
authored
Dec 18, 2012
by
Don Mitchell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactored videos to a much simpler impl b/c about videos don't have
speeds and don't use the video/default.yaml format.
parent
b74c5429
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
69 additions
and
115 deletions
+69
-115
cms/djangoapps/models/settings/course_details.py
+36
-2
cms/static/js/models/settings/course_details.js
+10
-99
cms/static/js/views/settings/main_settings_view.js
+8
-1
cms/templates/settings.html
+1
-1
lms/templates/staff_problem_info.html
+14
-12
No files found.
cms/djangoapps/models/settings/course_details.py
View file @
5709ff34
...
...
@@ -8,6 +8,8 @@ from contentstore.utils import get_modulestore
from
util.converters
import
jsdate_to_time
,
time_to_date
from
cms.djangoapps.models.settings
import
course_grading
from
cms.djangoapps.contentstore.utils
import
update_item
import
re
class
CourseDetails
:
def
__init__
(
self
,
location
):
...
...
@@ -58,7 +60,8 @@ class CourseDetails:
temploc
=
temploc
.
_replace
(
name
=
'video'
)
try
:
course
.
intro_video
=
get_modulestore
(
temploc
)
.
get_item
(
temploc
)
.
definition
[
'data'
]
raw_video
=
get_modulestore
(
temploc
)
.
get_item
(
temploc
)
.
definition
[
'data'
]
course
.
intro_video
=
CourseDetails
.
parse_video_tag
(
raw_video
)
except
ItemNotFoundError
:
pass
...
...
@@ -127,12 +130,43 @@ class CourseDetails:
update_item
(
temploc
,
jsondict
[
'effort'
])
temploc
=
temploc
.
_replace
(
name
=
'video'
)
update_item
(
temploc
,
jsondict
[
'intro_video'
])
recomposed_video_tag
=
CourseDetails
.
recompose_video_tag
(
jsondict
[
'intro_video'
])
update_item
(
temploc
,
recomposed_video_tag
)
# Could just generate and return a course obj w/o doing any db reads, but I put the reads in as a means to confirm
# it persisted correctly
return
CourseDetails
.
fetch
(
course_location
)
@staticmethod
def
parse_video_tag
(
raw_video
):
"""
Because the client really only wants the author to specify the youtube key, that's all we send to and get from the client.
The problem is that the db stores the html markup as well (which, of course, makes any sitewide changes to how we do videos
next to impossible.)
"""
if
not
raw_video
:
return
None
keystring_matcher
=
re
.
search
(
'(?<=embed/)[a-zA-Z0-9_-]+'
,
raw_video
)
if
keystring_matcher
is
None
:
keystring_matcher
=
re
.
search
(
'<?=
\
d+:[a-zA-Z0-9_-]+'
,
raw_video
)
if
keystring_matcher
:
return
keystring_matcher
.
group
(
0
)
else
:
# TODO should this be None or the raw_video? It would be good to at least log this
return
None
@staticmethod
def
recompose_video_tag
(
video_key
):
# TODO should this use a mako template? Of course, my hope is that this is a short-term workaround for the db not storing
# the right thing
result
=
'<iframe width="560" height="315" src="http://www.youtube.com/embed/'
+
\
video_key
+
'?autoplay=1&rel=0" frameborder="0" allowfullscreen=""></iframe>'
return
result
# TODO move to a more general util? Is there a better way to do the isinstance model check?
class
CourseSettingsEncoder
(
json
.
JSONEncoder
):
...
...
cms/static/js/models/settings/course_details.js
View file @
5709ff34
...
...
@@ -50,19 +50,10 @@ CMS.Models.Settings.CourseDetails = Backbone.Model.extend({
errors
.
enrollment_end
=
"The enrollment end date cannot be after the course end date."
;
}
if
(
newattrs
.
intro_video
&&
newattrs
.
intro_video
!=
this
.
get
(
'intro_video'
))
{
var
videos
=
this
.
parse_videosource
(
newattrs
.
intro_video
);
var
vid_errors
=
new
Array
();
var
cachethis
=
this
;
for
(
var
i
=
0
;
i
<
videos
.
length
;
i
++
)
{
// doesn't call parseFloat or Number b/c they stop on first non parsable and return what they have
if
(
!
isFinite
(
videos
[
i
].
speed
))
vid_errors
.
push
(
videos
[
i
].
speed
+
" is not a valid speed."
);
else
if
(
!
videos
[
i
].
key
)
vid_errors
.
push
(
videos
[
i
].
speed
+
" does not have a video id"
);
// can't use get from client to test if video exists b/c of CORS (crossbrowser get not allowed)
// GET "http://gdata.youtube.com/feeds/api/videos/" + videokey
}
if
(
!
_
.
isEmpty
(
vid_errors
))
{
errors
.
intro_video
=
vid_errors
.
join
(
' '
);
if
(
this
.
_videokey_illegal_chars
.
exec
(
newattrs
.
intro_video
))
{
errors
.
intro_video
=
"Key should only contain letters, numbers, _, or -"
;
}
// TODO check if key points to a real video using google's youtube api
}
if
(
!
_
.
isEmpty
(
errors
))
return
errors
;
// NOTE don't return empty errors as that will be interpreted as an error state
...
...
@@ -73,100 +64,20 @@ CMS.Models.Settings.CourseDetails = Backbone.Model.extend({
return
'/'
+
location
.
get
(
'org'
)
+
"/"
+
location
.
get
(
'course'
)
+
'/settings/'
+
location
.
get
(
'name'
)
+
'/section/details'
;
},
_videoprefix
:
/
\s
*<video
\s
*youtube="/g
,
// the below is lax to enable validation
_videospeedparse
:
/
[^
:
]
*/g
,
// /\d+\.?\d*(?=:)/g,
_videokeyparse
:
/
([^
,
\/
>
]
+
)
/g
,
_videonosuffix
:
/
[^
"
\/
>
]
+/g
,
_getNextMatch
:
function
(
regex
,
string
,
cursor
)
{
regex
.
lastIndex
=
cursor
;
var
result
=
regex
.
exec
(
string
);
if
(
_
.
isArray
(
result
))
return
result
[
0
];
else
return
result
;
},
// the whole string for editing (put in edit box)
getVideoSource
:
function
()
{
if
(
this
.
get
(
'intro_video'
))
{
var
cursor
=
0
;
var
videostring
=
this
.
get
(
'intro_video'
);
this
.
_getNextMatch
(
this
.
_videoprefix
,
videostring
,
cursor
);
cursor
=
this
.
_videoprefix
.
lastIndex
;
return
this
.
_getNextMatch
(
this
.
_videonosuffix
,
videostring
,
cursor
);
}
else
return
""
;
},
// the source closest to 1.0 speed
videosourceSample
:
function
()
{
if
(
this
.
get
(
'intro_video'
))
{
var
cursor
=
0
;
var
videostring
=
this
.
get
(
'intro_video'
);
this
.
_getNextMatch
(
this
.
_videoprefix
,
videostring
,
cursor
);
cursor
=
this
.
_videoprefix
.
lastIndex
;
// parse from [speed:id,/s?]* to find 1.0 or take first
var
parsedspeed
=
this
.
_getNextMatch
(
this
.
_videospeedparse
,
videostring
,
cursor
);
var
bestkey
;
if
(
parsedspeed
)
{
cursor
=
this
.
_videospeedparse
.
lastIndex
+
1
;
var
bestspeed
=
Number
(
parsedspeed
);
bestkey
=
this
.
_getNextMatch
(
this
.
_videokeyparse
,
videostring
,
cursor
);
cursor
=
this
.
_videokeyparse
.
lastIndex
+
1
;
while
(
cursor
<
videostring
.
length
&&
bestspeed
!=
1.0
)
{
parsedspeed
=
this
.
_getNextMatch
(
this
.
_videospeedparse
,
videostring
,
cursor
);
if
(
parsedspeed
)
cursor
=
this
.
_videospeedparse
.
lastIndex
+
1
;
else
break
;
if
(
Math
.
abs
(
Number
(
parsedspeed
)
-
1.0
)
<
Math
.
abs
(
bestspeed
-
1.0
))
{
bestspeed
=
Number
(
parsedspeed
);
bestkey
=
this
.
_getNextMatch
(
this
.
_videokeyparse
,
videostring
,
cursor
);
}
else
this
.
_getNextMatch
(
this
.
_videokeyparse
,
videostring
,
cursor
);
if
(
this
.
_videokeyparse
.
lastIndex
>
cursor
)
cursor
=
this
.
_videokeyparse
.
lastIndex
+
1
;
else
cursor
++
;
}
}
else
{
bestkey
=
this
.
_getNextMatch
(
this
.
_videokeyparse
,
videostring
,
cursor
);
}
if
(
bestkey
)
{
// WTF? for some reason bestkey is an array [key, key] (same one repeated)
if
(
_
.
isArray
(
bestkey
))
bestkey
=
bestkey
[
0
];
return
"http://www.youtube.com/embed/"
+
bestkey
;
}
else
return
""
;
}
},
parse_videosource
:
function
(
videostring
)
{
// used to validate before set so cannot get from model attr. Returns [{ speed: fff, key: sss }]
var
cursor
=
0
;
this
.
_getNextMatch
(
this
.
_videoprefix
,
videostring
,
cursor
);
cursor
=
this
.
_videoprefix
.
lastIndex
;
videostring
=
this
.
_getNextMatch
(
this
.
_videonosuffix
,
videostring
,
cursor
);
cursor
=
0
;
// parsed to "fff:kkk,fff:kkk"
var
result
=
new
Array
();
if
(
!
videostring
||
videostring
.
length
==
0
)
return
result
;
while
(
cursor
<
videostring
.
length
)
{
var
speed
=
this
.
_getNextMatch
(
this
.
_videospeedparse
,
videostring
,
cursor
);
if
(
speed
)
cursor
=
this
.
_videospeedparse
.
lastIndex
+
1
;
else
return
result
;
var
key
=
this
.
_getNextMatch
(
this
.
_videokeyparse
,
videostring
,
cursor
);
if
(
key
)
cursor
=
this
.
_videokeyparse
.
lastIndex
+
1
;
// See the WTF above
if
(
_
.
isArray
(
key
))
key
=
key
[
0
];
result
.
push
({
speed
:
speed
,
key
:
key
});
}
return
result
;
},
_videokey_illegal_chars
:
/
[^
a-zA-Z0-9_-
]
/g
,
save_videosource
:
function
(
newsource
)
{
// newsource either is <video youtube="speed:key, *"/> or just the "speed:key, *" string
// returns the videosource for the preview which iss the key whose speed is closest to 1
if
(
newsource
==
null
)
this
.
save
({
'intro_video'
:
null
});
if
(
_
.
isEmpty
(
newsource
)
&&
!
_
.
isEmpty
(
this
.
get
(
'intro_video'
))
)
this
.
save
({
'intro_video'
:
null
});
// TODO remove all whitespace w/in string
else
{
var
newVal
=
((
this
.
_getNextMatch
(
this
.
_videoprefix
,
newsource
,
0
))
?
newsource
:
'<video youtube="'
+
newsource
+
'"/>'
);
if
(
this
.
get
(
'intro_video'
)
!=
newVal
)
this
.
save
(
'intro_video'
,
newVal
);
if
(
this
.
get
(
'intro_video'
)
!=
newsource
)
this
.
save
(
'intro_video'
,
newsource
);
}
return
this
.
videosourceSample
();
},
videosourceSample
:
function
()
{
if
(
this
.
has
(
'intro_video'
))
return
"http://www.youtube.com/embed/"
+
this
.
get
(
'intro_video'
);
else
return
""
;
}
});
cms/static/js/views/settings/main_settings_view.js
View file @
5709ff34
...
...
@@ -193,7 +193,7 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
this
.
$el
.
find
(
'.current-course-introduction-video iframe'
).
attr
(
'src'
,
this
.
model
.
videosourceSample
());
if
(
this
.
model
.
has
(
'intro_video'
))
{
this
.
$el
.
find
(
'.remove-course-introduction-video'
).
show
();
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'intro_video'
]).
val
(
this
.
model
.
get
VideoSource
(
));
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'intro_video'
]).
val
(
this
.
model
.
get
(
'intro_video'
));
}
else
this
.
$el
.
find
(
'.remove-course-introduction-video'
).
hide
();
...
...
@@ -261,6 +261,12 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
this
.
clearValidationErrors
();
var
previewsource
=
this
.
model
.
save_videosource
(
$
(
event
.
currentTarget
).
val
());
this
.
$el
.
find
(
".current-course-introduction-video iframe"
).
attr
(
"src"
,
previewsource
);
if
(
this
.
model
.
has
(
'intro_video'
))
{
this
.
$el
.
find
(
'.remove-course-introduction-video'
).
show
();
}
else
{
this
.
$el
.
find
(
'.remove-course-introduction-video'
).
hide
();
}
break
;
default
:
...
...
@@ -282,6 +288,7 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
this
.
model
.
save_videosource
(
null
);
this
.
$el
.
find
(
".current-course-introduction-video iframe"
).
attr
(
"src"
,
""
);
this
.
$el
.
find
(
'#'
+
this
.
fieldToSelectorMap
[
'intro_video'
]).
val
(
""
);
this
.
$el
.
find
(
'.remove-course-introduction-video'
).
hide
();
}
},
codeMirrors
:
{},
...
...
cms/templates/settings.html
View file @
5709ff34
...
...
@@ -230,7 +230,7 @@ from contentstore import utils
</div>
<div
class=
"input"
>
<input
type=
"text"
class=
"long new-course-introduction-video add-video-data"
id=
"course-introduction-video"
value=
""
placeholder=
"
speed:id,speed:
id"
autocomplete=
"off"
>
<input
type=
"text"
class=
"long new-course-introduction-video add-video-data"
id=
"course-introduction-video"
value=
""
placeholder=
"id"
autocomplete=
"off"
>
<span
class=
"tip tip-stacked"
>
Video restrictions go here
</span>
</div>
</div>
...
...
lms/templates/staff_problem_info.html
View file @
5709ff34
...
...
@@ -61,16 +61,18 @@ category = ${category | h}
<script
type=
"text/javascript"
>
// assumes courseware.html's loaded this method.
setup_debug
(
'${element_id}'
,
%
if
edit_link
:
'${edit_link}'
,
%
else
:
null
,
%
endif
{
'location'
:
'${location}'
,
'xqa_key'
:
'${xqa_key}'
,
'category'
:
'${category}'
,
'user'
:
'${user}'
});
%
if
staff_access
:
setup_debug
(
'${element_id}'
,
%
if
edit_link
:
'${edit_link}'
,
%
else
:
null
,
%
endif
{
'location'
:
'${location}'
,
'xqa_key'
:
'${xqa_key}'
,
'category'
:
'${category}'
,
'user'
:
'${user}'
});
%
endif
</script>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment