Commit 2387c08b by kurtb

Merge pull request #1 from kurtb/master

Basic Office Mix XBlock
parents 64e2c21e bb2b7ef2
*.pyc
*.swp
*.egg-info
from .officemix import OfficeMixXBlock
\ No newline at end of file
"""
XBlock for Office Mix
Allows for the easy selection and embedding of an Office Mix within an XBlock.
Makes use of the Office Mix oEmbed entry points to look up the embed code and then
inserts it within the XBlock.
"""
import cgi
import decimal
import pkg_resources
import requests
import string
from xblock.core import XBlock
from xblock.fields import Scope, Integer, String
from xblock.fragment import Fragment
class OfficeMixXBlock(XBlock):
"""
An XBlock providing Office Mix embedding capabilities
"""
# Stored values for the XBlock
href = String(
display_name="Office Mix Embed URL",
help="URL of the Office Mix you want to embed",
scope=Scope.content,
default='https://mix.office.com/watch/10g8h9tvipyg8')
display_name = String(
display_name="Display Name",
help="This name appears in the horizontal navigation at the top of the page.",
scope=Scope.settings,
default="Office Mix")
def resource_string(self, path):
"""Handy helper for getting resources from our kit."""
data = pkg_resources.resource_string(__name__, path)
return data.decode("utf8")
def studio_view(self, context):
"""
Studio view part
"""
href = self.href or ''
display_name = self.display_name or ''
html_str = self.resource_string("static/html/officemix_edit.html")
frag = Fragment(html_str.format(href=cgi.escape(href), display_name=cgi.escape(display_name)))
js_str = self.resource_string("/static/js/officemix_edit.js")
frag.add_javascript(js_str)
css_str = self.resource_string("/static/css/officemix_edit.css")
frag.add_css(css_str)
frag.initialize_js('OfficeMixEditBlock')
return frag
def student_view(self, context=None):
"""
Create a fragment used to display the XBlock to a student
`context` is a dictionary used to configure the display (unused).
Returns a `Fragment` object specifying the HTML, CSS and JavaScript to display
"""
href = self.href or ''
display_name = self.display_name or ''
# Make the oEmbed call to get the embed code
try:
embed_code, width, height = self.get_embed_code(href)
html_str = self.resource_string("static/html/officemix.html")
except Exception as ex:
html_str = self.resource_string("static/html/embed_error.html")
frag = Fragment(html_str.format(self=self, exception=cgi.escape(str(ex))))
return frag
# Grab and round the aspect ratio
ratio = decimal.Decimal(float(height) / width * 100.0)
# Construct the HTML
frag = Fragment(html_str.format(
self=self,
embed_code=embed_code,
display_name=cgi.escape(display_name)))
# And construct the CSS
css_str = self.resource_string("static/css/officemix.css")
css_str = string.replace(unicode(css_str), "{aspect_ratio}", cgi.escape(unicode(round(ratio, 2))))
frag.add_css(css_str)
return frag
@XBlock.json_handler
def studio_submit(self, data, suffic=''):
self.href = data.get('href')
self.display_name = data.get('display_name')
return {'result': 'success'}
def get_embed_code(self, url):
"""
Makes an oEmbed call out to Office Mix to retrieve the embed code and width and height of the mix
for the given url.
"""
parameters = { 'url': url }
oEmbedRequest = requests.get("https://mix.office.com/oembed/", params = parameters)
oEmbedRequest.raise_for_status()
responseJson = oEmbedRequest.json()
return responseJson['html'], responseJson['width'], responseJson['height']
@staticmethod
def workbench_scenarios():
""" Returns a single element which is the Office Mix xblock """
return [
("Office Mix",
"""
<officemix href="https://mix.office.com/watch/1otxpj7hz6kbx" />
"""),
]
/* The below will maximize the height of the iframe based on the aspect
* ratio of the mix. */
.max-aspect-ratio-frame {
width: 100%;
position: relative;
height: 0;
padding-bottom: {aspect_ratio}%;
}
.max-aspect-ratio-frame iframe {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
}
/* Match EdX's border around a video */
.mix-border {
background: #f3f3f3;
display: block;
margin: 0 -12px;
padding: 12px;
border-radius: 5px;
outline: none;
}
.wrapper-comp-settings .list-input.settings-list .setting-help {
margin-left: 25%;
width: 45%;
padding: 0 13px;
}
<p>There was an error retrieving the embed code for the given Office Mix.</p>
<p>{exception}</p>
<h2>{display_name}</h2>
<div class="mix-border">
<div class="max-aspect-ratio-frame">
{embed_code}
</div>
</div>
<div class="wrapper-comp-settings is-active editor-with-buttons" id="settings-tab">
<ul class="list-input settings-list">
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="display-name">Component Display Name</label>
<input class="input setting-input" name="display-name" id="display-name" value="{display_name}" type="text" />
</div>
<span class="tip setting-help">The name students see. This name appears in the course ribbon and as a header for the video.</span>
</li>
<li class="field comp-setting-entry is-set">
<div class="wrapper-comp-setting">
<label class="label setting-label" for="href">Office Mix URL</label>
<input class="input setting-input" name="href" id="href" value="{href}" type="text" />
</div>
<span class="tip setting-help">The URL for your Office Mix.</span>
</li>
</ul>
<div class="xblock-actions">
<ul>
<li class="action-item">
<a href="#" class="button action-primary save-button">Save</a>
</li>
<li class="action-item">
<a href="#" class="button cancel-button">Cancel</a>
</li>
</ul>
</div>
</div>
function OfficeMixEditBlock(runtime, element) {
$(element).find('.save-button').bind('click', function() {
var handlerUrl = runtime.handlerUrl(element, 'studio_submit');
var data = {
href: $(element).find('input[name=href]').val(),
display_name: $(element).find('input[name=display-name]').val(),
};
$.post(handlerUrl, JSON.stringify(data)).done(function(response) {
window.location.reload(false);
});
});
$(element).find('.cancel-button').bind('click', function() {
runtime.notify('cancel', {});
});
}
"""Setup for Office Mix XBlock."""
import os
from setuptools import setup
def package_data(pkg, roots):
"""Generic function to find package_data.
All of the files under each of the `roots` will be declared as package
data for package `pkg`.
"""
data = []
for root in roots:
for dirname, _, files in os.walk(os.path.join(pkg, root)):
for fname in files:
data.append(os.path.relpath(os.path.join(dirname, fname), pkg))
return {pkg: data}
setup(
name='xblock-officemix',
version='0.1',
description='Office Mix XBlock',
packages=[
'officemix',
],
install_requires=[
'XBlock',
],
entry_points={
'xblock.v1': [
'officemix = officemix:OfficeMixXBlock',
]
},
package_data=package_data("officemix", ["static", "public"]),
)
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